282 lines
13 KiB
Python
282 lines
13 KiB
Python
import numpy as np
|
||
import random
|
||
import cv2
|
||
|
||
INDEX_LEFT_EYEBROW = [276, 283, 282, 295, 285, 336, 296, 334, 293, 300]
|
||
INDEX_RIGHT_EYEBROW = [46, 53, 52, 65, 55, 107, 66, 105, 63, 70]
|
||
INDEX_EYEBROW = INDEX_LEFT_EYEBROW + INDEX_RIGHT_EYEBROW
|
||
|
||
INDEX_NOSE_EDGE = [343, 355, 358, 327, 326, 2, 97, 98, 129, 126, 114]
|
||
INDEX_NOSE_MID = [6, 197,195,5,4]
|
||
INDEX_NOSE = INDEX_NOSE_EDGE + INDEX_NOSE_MID
|
||
|
||
INDEX_LIPS_INNER = [78, 95, 88, 178, 87, 14, 317, 402, 318, 324,308,415,310,311,312,13,82,81,80,191]
|
||
INDEX_LIPS_OUTER = [61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291,409,270,269,267,0,37,39,40,185,]
|
||
INDEX_LIPS = INDEX_LIPS_INNER + INDEX_LIPS_OUTER
|
||
|
||
INDEX_LEFT_EYE = [263, 249, 390, 373, 374, 380, 381, 382, 362, 398, 384, 385, 386, 387, 388, 466]
|
||
INDEX_RIGHT_EYE = [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246]
|
||
INDEX_EYE = INDEX_LEFT_EYE + INDEX_RIGHT_EYE
|
||
# 下半边脸的轮廓
|
||
INDEX_FACE_OVAL = [
|
||
454, 323, 361, 288, 397, 365,
|
||
379, 378, 400, 377, 152, 148, 176, 149, 150,
|
||
136, 172, 58, 132, 93, 234,
|
||
# 206, 426
|
||
]
|
||
|
||
INDEX_MUSCLE = [
|
||
371,266,425,427,434,394,
|
||
169,214,207,205,36,142
|
||
]
|
||
|
||
main_keypoints_index = INDEX_EYEBROW + INDEX_NOSE + INDEX_LIPS + INDEX_EYE + INDEX_FACE_OVAL + INDEX_MUSCLE
|
||
|
||
rotate_ref_index = INDEX_EYEBROW + INDEX_NOSE + INDEX_EYE + INDEX_FACE_OVAL + INDEX_MUSCLE
|
||
# print(len(main_keypoints_index))
|
||
Normalized = True
|
||
if Normalized:
|
||
tmp = 0
|
||
list_ = []
|
||
for i in [INDEX_LEFT_EYEBROW, INDEX_RIGHT_EYEBROW, INDEX_NOSE_EDGE, INDEX_NOSE_MID, INDEX_LIPS_INNER, INDEX_LIPS_OUTER, INDEX_LEFT_EYE, INDEX_RIGHT_EYE, INDEX_FACE_OVAL, INDEX_MUSCLE]:
|
||
i = (tmp + np.arange(len(i))).tolist()
|
||
list_.append(i)
|
||
tmp += len(i)
|
||
[INDEX_LEFT_EYEBROW, INDEX_RIGHT_EYEBROW, INDEX_NOSE_EDGE, INDEX_NOSE_MID, INDEX_LIPS_INNER, INDEX_LIPS_OUTER, INDEX_LEFT_EYE, INDEX_RIGHT_EYE, INDEX_FACE_OVAL, INDEX_MUSCLE] = list_
|
||
INDEX_EYEBROW = INDEX_LEFT_EYEBROW + INDEX_RIGHT_EYEBROW
|
||
INDEX_NOSE = INDEX_NOSE_EDGE + INDEX_NOSE_MID
|
||
INDEX_LIPS = INDEX_LIPS_INNER + INDEX_LIPS_OUTER
|
||
INDEX_EYE = INDEX_LEFT_EYE + INDEX_RIGHT_EYE
|
||
# print(INDEX_FACE_OVAL_STABLE)
|
||
# print(len(main_keypoints_index))
|
||
# print(len(set(main_keypoints_index)))
|
||
FACE_MASK_INDEX = INDEX_FACE_OVAL[2:-2]
|
||
def crop_face(keypoints, is_train = False, size = [512, 512]):
|
||
"""
|
||
x_ratio: 裁剪出一个正方形,边长根据keypoints的宽度 * x_ratio决定
|
||
"""
|
||
x_min, y_min, x_max, y_max = np.min(keypoints[FACE_MASK_INDEX, 0]), np.min(keypoints[FACE_MASK_INDEX, 1]), np.max(keypoints[FACE_MASK_INDEX, 0]), np.max(
|
||
keypoints[FACE_MASK_INDEX, 1])
|
||
y_min = keypoints[33, 1] # 两眼间的点开始y轴裁剪
|
||
border_width_half = max(x_max - x_min, y_max - y_min) * 0.6
|
||
center_x = int((x_min + x_max) / 2.0)
|
||
center_y = int((y_min + y_max) / 2.0)
|
||
if is_train:
|
||
w_offset = random.randint(-2, 2)
|
||
h_offset = random.randint(-2, 2)
|
||
center_x = center_x + w_offset
|
||
center_y = center_y + h_offset
|
||
x_min, y_min, x_max, y_max = int(center_x - border_width_half), int(center_y - border_width_half), int(
|
||
center_x + border_width_half), int(center_y + border_width_half)
|
||
x_min = max(0, x_min)
|
||
y_min = max(0, y_min)
|
||
x_max = min(size[1], x_max)
|
||
y_max = min(size[0], y_max)
|
||
return [x_min, y_min, x_max, y_max]
|
||
|
||
def draw_face_feature_maps(keypoints, mode = ["mouth", "nose", "eye", "oval"], size=(256, 256), im_edges = None, mouth_width = None, mouth_height = None):
|
||
w, h = size
|
||
# edge map for face region from keypoints
|
||
if im_edges is None:
|
||
im_edges = np.zeros((h, w, 3), np.uint8) # edge map for all edges
|
||
|
||
if "mouth_bias" in mode:
|
||
|
||
w0, w1, h0, h1 = (int(keypoints[INDEX_NOSE_EDGE[5], 0] - mouth_width / 2),
|
||
int(keypoints[INDEX_NOSE_EDGE[5], 0] + mouth_width / 2),
|
||
int(keypoints[INDEX_NOSE_EDGE[5], 1] + mouth_height / 4),
|
||
int(keypoints[INDEX_NOSE_EDGE[5], 1] + mouth_height / 4 + mouth_height))
|
||
w0, h0 = max(0, w0), max(h0, 0)
|
||
# print(w0, w1, h0, h1)
|
||
mouth_mask = np.zeros((h, w, 3), np.uint8) # edge map for all edges
|
||
mouth_mask[h0:h1, w0:w1] = 255
|
||
mouth_index = np.where(mouth_mask == 255)
|
||
blur = (10, 10)
|
||
img_mouth = cv2.cvtColor(im_edges, cv2.COLOR_BGR2GRAY)
|
||
img_mouth = cv2.blur(img_mouth, blur)
|
||
# print(mouth_index)
|
||
mean_ = int(np.mean(img_mouth[(mouth_index[0], mouth_index[1])]))
|
||
max_, min_ = random.randint(mean_ + 40, mean_ + 70), random.randint(mean_ - 70, mean_ - 40)
|
||
img_mouth = (img_mouth.astype(np.float32) - min_) / (max_ - min_) * 255.
|
||
img_mouth = img_mouth.clip(0, 255).astype(np.uint8)
|
||
# print(img_mouth.shape[0], img_mouth.shape[1])
|
||
img_mouth = cv2.resize(img_mouth, (100, 50))
|
||
|
||
# 定义噪声的标准差
|
||
sigma = 8 # 你可以根据需要调整这个值
|
||
# 生成与图片相同大小和类型的噪声
|
||
noise = sigma * np.random.randn(img_mouth.shape[0], img_mouth.shape[1])
|
||
# 将噪声添加到图片上
|
||
img_mouth = img_mouth + noise
|
||
img_mouth = cv2.resize(img_mouth.clip(0, 255).astype(np.uint8), (im_edges.shape[0], im_edges.shape[1]))
|
||
|
||
img_mouth = np.concatenate(
|
||
[img_mouth[:, :, np.newaxis], img_mouth[:, :, np.newaxis], img_mouth[:, :, np.newaxis]], axis=2)
|
||
|
||
# bias_x = int(min(size[0] - max(mouth_index[0]), min(mouth_index[0])))
|
||
# bias_y = int(min(size[0] - max(mouth_index[1]), min(mouth_index[1])))
|
||
# print(bias_x, bias_y)
|
||
# bias_x = random.randint(-bias_x // 10, bias_x // 10)
|
||
# bias_y = random.randint(-bias_y // 10, bias_y // 10)
|
||
#
|
||
# mouth_index2 = np.array(mouth_index)
|
||
# # print(mouth_index)
|
||
# mouth_index2[0] = mouth_index[0] + bias_x
|
||
# mouth_index2[1] = mouth_index[1] + bias_y
|
||
# mouth_index2 = (mouth_index2[0], mouth_index2[1], mouth_index2[2])
|
||
# # print(mouth_index2.T, mouth_index2.shape)
|
||
#
|
||
output = np.zeros((h, w, 3), np.uint8)
|
||
# output[mouth_index2] = img_mouth[mouth_index]
|
||
# return im_edges
|
||
output[mouth_index] = img_mouth[mouth_index]
|
||
im_edges = output
|
||
if "nose" in mode:
|
||
for ii in range(len(INDEX_NOSE_EDGE) - 1):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_NOSE_EDGE[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_NOSE_EDGE[ii+1]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 255, 0), 2)
|
||
for ii in range(len(INDEX_NOSE_MID) -1):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_NOSE_MID[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_NOSE_MID[ii + 1]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 255, 0), 2)
|
||
if "eye" in mode:
|
||
for ii in range(len(INDEX_LEFT_EYE)):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_LEFT_EYE[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_LEFT_EYE[(ii + 1)%len(INDEX_LEFT_EYE)]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 255, 0), 2)
|
||
for ii in range(len(INDEX_RIGHT_EYE)):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_RIGHT_EYE[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_RIGHT_EYE[(ii + 1) % len(INDEX_RIGHT_EYE)]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 255, 0), 2)
|
||
if "oval" in mode:
|
||
tmp = INDEX_FACE_OVAL[:6]
|
||
for ii in range(len(tmp) -1):
|
||
pt1 = [int(flt) for flt in keypoints[tmp[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[tmp[ii + 1]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 0, 255), 2)
|
||
tmp = INDEX_FACE_OVAL[-6:]
|
||
for ii in range(len(tmp) - 1):
|
||
pt1 = [int(flt) for flt in keypoints[tmp[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[tmp[ii + 1]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 0, 255), 2)
|
||
|
||
# if "mouth_outer" in mode:
|
||
# pts = keypoints[INDEX_LIPS_OUTER]
|
||
# pts = pts.reshape((-1, 1, 2)).astype(np.int32)
|
||
# cv2.fillPoly(im_edges, [pts], color=(255, 0, 0))
|
||
# if "mouth" in mode:
|
||
# pts = keypoints[INDEX_LIPS_OUTER][:,2]
|
||
# pts = pts.reshape((-1, 1, 2)).astype(np.int32)
|
||
# cv2.fillPoly(im_edges, [pts], color=(255, 0, 0))
|
||
# pts = keypoints[INDEX_LIPS_INNER][:,2]
|
||
# pts = pts.reshape((-1, 1, 2)).astype(np.int32)
|
||
# cv2.fillPoly(im_edges, [pts], color=(0, 0, 0))
|
||
# if "mouth_outer" in mode:
|
||
# for ii in range(len(INDEX_LIPS_OUTER)):
|
||
# pt1 = [int(flt) for flt in keypoints[INDEX_LIPS_OUTER[ii]]][:2]
|
||
# pt2 = [int(flt) for flt in keypoints[INDEX_LIPS_OUTER[(ii + 1)%len(INDEX_LIPS_OUTER)]]][:2]
|
||
# cv2.line(im_edges, tuple(pt1), tuple(pt2), (255, 0, 0), 2)
|
||
|
||
|
||
|
||
if "mouth" in mode:
|
||
for ii in range(len(INDEX_LIPS_OUTER)):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_LIPS_OUTER[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_LIPS_OUTER[(ii + 1)%len(INDEX_LIPS_OUTER)]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (255, 0, 0), 2)
|
||
for ii in range(len(INDEX_LIPS_INNER)):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_LIPS_INNER[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_LIPS_INNER[(ii + 1)%len(INDEX_LIPS_INNER)]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (255, 0, 0), 2)
|
||
if "muscle" in mode:
|
||
for ii in range(len(INDEX_MUSCLE) - 1):
|
||
pt1 = [int(flt) for flt in keypoints[INDEX_MUSCLE[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[INDEX_MUSCLE[(ii + 1) % len(INDEX_MUSCLE)]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (255, 255, 255), 2)
|
||
|
||
if "oval_all" in mode:
|
||
tmp = INDEX_FACE_OVAL
|
||
for ii in range(len(tmp) -1):
|
||
pt1 = [int(flt) for flt in keypoints[tmp[ii]]][:2]
|
||
pt2 = [int(flt) for flt in keypoints[tmp[ii + 1]]][:2]
|
||
cv2.line(im_edges, tuple(pt1), tuple(pt2), (0, 0, 255), 2)
|
||
|
||
return im_edges
|
||
|
||
def smooth_array(array, weight = [0.1,0.8,0.1], mode = "numpy"):
|
||
'''
|
||
|
||
Args:
|
||
array: [n_frames, n_values], 需要转换为[n_values, 1, n_frames]
|
||
weight: Conv1d.weight, 一维卷积核权重
|
||
Returns:
|
||
array: [n_frames, n_values], 光滑后的array
|
||
'''
|
||
if mode == "torch":
|
||
import torch
|
||
import torch.nn as nn
|
||
import torch.nn.functional as F
|
||
input = torch.Tensor(np.transpose(array[:, np.newaxis, :], (2, 1, 0)))
|
||
smooth_length = len(weight)
|
||
assert smooth_length % 2 == 1, "卷积核权重个数必须使用奇数"
|
||
pad = (smooth_length // 2, smooth_length // 2) # 当pad只有两个参数时,仅改变最后一个维度, 左边扩充1列,右边扩充1列
|
||
input = F.pad(input, pad, "replicate")
|
||
|
||
with torch.no_grad():
|
||
conv1 = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=smooth_length)
|
||
# 卷积核的元素值初始化
|
||
weight = torch.tensor(weight).view(1, 1, -1)
|
||
conv1.weight = torch.nn.Parameter(weight)
|
||
nn.init.constant_(conv1.bias, 0) # 偏置值为0
|
||
# print(conv1.weight)
|
||
out = conv1(input)
|
||
return out.permute(2, 1, 0).squeeze().numpy()
|
||
else:
|
||
# out = np.zeros([array.shape[0] + 2, array.shape[1]])
|
||
# input = np.zeros([array.shape[0] + 2, array.shape[1]])
|
||
# input[0] = array[0]
|
||
# input[-1] = array[-1]
|
||
# input[1:-1] = array
|
||
# for i in range(out.shape[1]):
|
||
# out[:, i] = np.convolve(input[:, i], weight, mode="same")
|
||
# out0 = out[1:-1]
|
||
fliter = np.array([weight]).T
|
||
x0 = array
|
||
fliter = np.repeat(fliter, x0.shape[1], axis=1)
|
||
out0 = np.zeros_like(x0)
|
||
for i in range(len(x0)):
|
||
if i == 0 or i == len(x0) - 1:
|
||
out0[i] = x0[i]
|
||
else:
|
||
tmp = x0[i - 1:i + 2] * fliter
|
||
out0[i] = np.sum(tmp, axis=0)
|
||
return out0
|
||
|
||
def generate_face_mask():
|
||
face_mask = np.zeros([256, 256], dtype=np.uint8)
|
||
for i in range(20):
|
||
ii = 19 - i
|
||
face_mask[ii, :] = 13 * i
|
||
face_mask[255 - ii, :] = 13 * i
|
||
face_mask[:, ii] = 13 * i
|
||
face_mask[:, 255 - ii] = 13 * i
|
||
face_mask = np.array([face_mask, face_mask, face_mask]).transpose(1, 2, 0).astype(float) / 255.
|
||
print(face_mask.shape)
|
||
return face_mask
|
||
|
||
from math import cos,sin,radians
|
||
def RotateAngle2Matrix(tmp): #tmp为xyz的旋转角,角度值
|
||
tmp = [radians(i) for i in tmp]
|
||
matX = np.array([[1.0, 0, 0],
|
||
[0.0, cos(tmp[0]), -sin(tmp[0])],
|
||
[0.0, sin(tmp[0]), cos(tmp[0])]])
|
||
matY = np.array([[cos(tmp[1]), 0, sin(tmp[1])],
|
||
[0.0, 1, 0],
|
||
[-sin(tmp[1]), 0, cos(tmp[1])]])
|
||
matZ = np.array([[cos(tmp[2]), -sin(tmp[2]), 0],
|
||
[sin(tmp[2]), cos(tmp[2]), 0],
|
||
[0, 0, 1]])
|
||
matRotate = np.matmul(matZ, matY)
|
||
matRotate = np.matmul(matRotate, matX)
|
||
return matRotate |