Path: blob/master/FaceMaskOverlay/lib/utils/transforms.py
3443 views
# ------------------------------------------------------------------------------1# Copyright (c) Microsoft2# Licensed under the MIT License.3# Created by Tianheng Cheng([email protected]), Yang Zhao4# ------------------------------------------------------------------------------56import cv27import torch8import scipy9import scipy.misc10import numpy as np111213MATCHED_PARTS = {14"300W": ([1, 17], [2, 16], [3, 15], [4, 14], [5, 13], [6, 12], [7, 11], [8, 10],15[18, 27], [19, 26], [20, 25], [21, 24], [22, 23],16[32, 36], [33, 35],17[37, 46], [38, 45], [39, 44], [40, 43], [41, 48], [42, 47],18[49, 55], [50, 54], [51, 53], [62, 64], [61, 65], [68, 66], [59, 57], [60, 56]),19"AFLW": ([1, 6], [2, 5], [3, 4],20[7, 12], [8, 11], [9, 10],21[13, 15],22[16, 18]),23"COFW": ([1, 2], [5, 7], [3, 4], [6, 8], [9, 10], [11, 12], [13, 15], [17, 18], [14, 16], [19, 20], [23, 24]),24"WFLW": ([0, 32], [1, 31], [2, 30], [3, 29], [4, 28], [5, 27], [6, 26], [7, 25], [8, 24], [9, 23], [10, 22],25[11, 21], [12, 20], [13, 19], [14, 18], [15, 17], # check26[33, 46], [34, 45], [35, 44], [36, 43], [37, 42], [38, 50], [39, 49], [40, 48], [41, 47], # elbrow27[60, 72], [61, 71], [62, 70], [63, 69], [64, 68], [65, 75], [66, 74], [67, 73],28[55, 59], [56, 58],29[76, 82], [77, 81], [78, 80], [87, 83], [86, 84],30[88, 92], [89, 91], [95, 93], [96, 97])}313233def fliplr_joints(x, width, dataset='aflw'):34"""35flip coords36"""37matched_parts = MATCHED_PARTS[dataset]38# Flip horizontal39x[:, 0] = width - x[:, 0]4041if dataset == 'WFLW':42for pair in matched_parts:43tmp = x[pair[0], :].copy()44x[pair[0], :] = x[pair[1], :]45x[pair[1], :] = tmp46else:47for pair in matched_parts:48tmp = x[pair[0] - 1, :].copy()49x[pair[0] - 1, :] = x[pair[1] - 1, :]50x[pair[1] - 1, :] = tmp51return x525354def get_3rd_point(a, b):55direct = a - b56return b + np.array([-direct[1], direct[0]], dtype=np.float32)575859def get_dir(src_point, rot_rad):60sn, cs = np.sin(rot_rad), np.cos(rot_rad)6162src_result = [0, 0]63src_result[0] = src_point[0] * cs - src_point[1] * sn64src_result[1] = src_point[0] * sn + src_point[1] * cs6566return src_result676869def get_affine_transform(70center, scale, rot, output_size,71shift=np.array([0, 0], dtype=np.float32), inv=0):72if not isinstance(scale, np.ndarray) and not isinstance(scale, list):73print(scale)74scale = np.array([scale, scale])7576scale_tmp = scale * 200.077src_w = scale_tmp[0]78dst_w = output_size[0]79dst_h = output_size[1]8081rot_rad = np.pi * rot / 18082src_dir = get_dir([0, src_w * -0.5], rot_rad)83dst_dir = np.array([0, dst_w * -0.5], np.float32)8485src = np.zeros((3, 2), dtype=np.float32)86dst = np.zeros((3, 2), dtype=np.float32)87src[0, :] = center + scale_tmp * shift88src[1, :] = center + src_dir + scale_tmp * shift89dst[0, :] = [dst_w * 0.5, dst_h * 0.5]90dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir9192src[2:, :] = get_3rd_point(src[0, :], src[1, :])93dst[2:, :] = get_3rd_point(dst[0, :], dst[1, :])9495if inv:96trans = cv2.getAffineTransform(np.float32(dst), np.float32(src))97else:98trans = cv2.getAffineTransform(np.float32(src), np.float32(dst))99100return trans101102103def crop_v2(img, center, scale, output_size, rot=0):104trans = get_affine_transform(center, scale, rot, output_size)105106dst_img = cv2.warpAffine(107img, trans, (int(output_size[0]), int(output_size[1])),108flags=cv2.INTER_LINEAR109)110111return dst_img112113114def get_transform(center, scale, output_size, rot=0):115"""116General image processing functions117"""118# Generate transformation matrix119h = 200 * scale120t = np.zeros((3, 3))121t[0, 0] = float(output_size[1]) / h122t[1, 1] = float(output_size[0]) / h123t[0, 2] = output_size[1] * (-float(center[0]) / h + .5)124t[1, 2] = output_size[0] * (-float(center[1]) / h + .5)125t[2, 2] = 1126if not rot == 0:127rot = -rot # To match direction of rotation from cropping128rot_mat = np.zeros((3, 3))129rot_rad = rot * np.pi / 180130sn, cs = np.sin(rot_rad), np.cos(rot_rad)131rot_mat[0, :2] = [cs, -sn]132rot_mat[1, :2] = [sn, cs]133rot_mat[2, 2] = 1134# Need to rotate around center135t_mat = np.eye(3)136t_mat[0, 2] = -output_size[1]/2137t_mat[1, 2] = -output_size[0]/2138t_inv = t_mat.copy()139t_inv[:2, 2] *= -1140t = np.dot(t_inv, np.dot(rot_mat, np.dot(t_mat, t)))141return t142143144def transform_pixel(pt, center, scale, output_size, invert=0, rot=0):145# Transform pixel location to different reference146t = get_transform(center, scale, output_size, rot=rot)147if invert:148t = np.linalg.inv(t)149new_pt = np.array([pt[0] - 1, pt[1] - 1, 1.]).T150new_pt = np.dot(t, new_pt)151return new_pt[:2].astype(int) + 1152153154def transform_preds(coords, center, scale, output_size):155156for p in range(coords.size(0)):157coords[p, 0:2] = torch.tensor(transform_pixel(coords[p, 0:2], center, scale, output_size, 1, 0))158return coords159160161def crop(img, center, scale, output_size, rot=0):162center_new = center.clone()163164# Preprocessing for efficient cropping165ht, wd = img.shape[0], img.shape[1]166sf = scale * 200.0 / output_size[0]167if sf < 2:168sf = 1169else:170new_size = int(np.math.floor(max(ht, wd) / sf))171new_ht = int(np.math.floor(ht / sf))172new_wd = int(np.math.floor(wd / sf))173if new_size < 2:174return torch.zeros(output_size[0], output_size[1], img.shape[2]) \175if len(img.shape) > 2 else torch.zeros(output_size[0], output_size[1])176else:177img = cv2.resize(img, (new_wd, new_ht), interpolation=cv2.INTER_LINEAR)178center_new[0] = center_new[0] * 1.0 / sf179center_new[1] = center_new[1] * 1.0 / sf180scale = scale / sf181182# Upper left point183ul = np.array(transform_pixel([0, 0], center_new, scale, output_size, invert=1))184# Bottom right point185br = np.array(transform_pixel(output_size, center_new, scale, output_size, invert=1))186187# Padding so that when rotated proper amount of context is included188pad = int(np.linalg.norm(br - ul) / 2 - float(br[1] - ul[1]) / 2)189if not rot == 0:190ul -= pad191br += pad192193new_shape = [br[1] - ul[1], br[0] - ul[0]]194if len(img.shape) > 2:195new_shape += [img.shape[2]]196197new_img = np.zeros(new_shape, dtype=np.float32)198199# Range to fill new array200new_x = max(0, -ul[0]), min(br[0], len(img[0])) - ul[0]201new_y = max(0, -ul[1]), min(br[1], len(img)) - ul[1]202# Range to sample from original image203old_x = max(0, ul[0]), min(len(img[0]), br[0])204old_y = max(0, ul[1]), min(len(img), br[1])205new_img[new_y[0]:new_y[1], new_x[0]:new_x[1]] = img[old_y[0]:old_y[1], old_x[0]:old_x[1]]206207if not rot == 0:208# Remove padding209new_img = scipy.misc.imrotate(new_img, rot)210new_img = new_img[pad:-pad, pad:-pad]211new_img = cv2.resize(212new_img, (output_size[0], output_size[1]), interpolation=cv2.INTER_LINEAR213)214return new_img215216217def generate_target(img, pt, sigma, label_type='Gaussian'):218# Check that any part of the gaussian is in-bounds219tmp_size = sigma * 3220ul = [int(pt[0] - tmp_size), int(pt[1] - tmp_size)]221br = [int(pt[0] + tmp_size + 1), int(pt[1] + tmp_size + 1)]222if (ul[0] >= img.shape[1] or ul[1] >= img.shape[0] or223br[0] < 0 or br[1] < 0):224# If not, just return the image as is225return img226227# Generate gaussian228size = 2 * tmp_size + 1229x = np.arange(0, size, 1, np.float32)230y = x[:, np.newaxis]231x0 = y0 = size // 2232# The gaussian is not normalized, we want the center value to equal 1233if label_type == 'Gaussian':234g = np.exp(- ((x - x0) ** 2 + (y - y0) ** 2) / (2 * sigma ** 2))235else:236g = sigma / (((x - x0) ** 2 + (y - y0) ** 2 + sigma ** 2) ** 1.5)237238# Usable gaussian range239g_x = max(0, -ul[0]), min(br[0], img.shape[1]) - ul[0]240g_y = max(0, -ul[1]), min(br[1], img.shape[0]) - ul[1]241# Image range242img_x = max(0, ul[0]), min(br[0], img.shape[1])243img_y = max(0, ul[1]), min(br[1], img.shape[0])244245img[img_y[0]:img_y[1], img_x[0]:img_x[1]] = g[g_y[0]:g_y[1], g_x[0]:g_x[1]]246return img247248249