68 lines
2.7 KiB
Python
68 lines
2.7 KiB
Python
import numpy as np
|
|
import cv2
|
|
from skimage import feature
|
|
|
|
# https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/
|
|
DLIB_LANDMARKS_PART_LIST = [
|
|
[list(range(0, 17)) + list(range(68, 83)) + [0]], # face
|
|
[range(17, 22)], # right eyebrow
|
|
[range(22, 27)], # left eyebrow
|
|
[[28, 31], range(31, 36), [35, 28]], # nose
|
|
[[36, 37, 38, 39], [39, 40, 41, 36]], # right eye
|
|
[[42, 43, 44, 45], [45, 46, 47, 42]], # left eye
|
|
[range(48, 55), [54, 55, 56, 57, 58, 59, 48]], # mouth
|
|
[range(60, 65), [64, 65, 66, 67, 60]] # tongue
|
|
]
|
|
|
|
|
|
def dist_tensor(key_points, size=(256, 256)):
|
|
dist_list = []
|
|
for edge_list in DLIB_LANDMARKS_PART_LIST:
|
|
for edge in edge_list:
|
|
pts = key_points[edge, :]
|
|
im_edge = np.zeros(size, np.uint8)
|
|
cv2.polylines(im_edge, [pts], isClosed=False, color=255, thickness=1)
|
|
im_dist = cv2.distanceTransform(255 - im_edge, cv2.DIST_L1, 3)
|
|
im_dist = np.clip((im_dist / 3), 0, 255).astype(np.uint8)
|
|
dist_list.append(im_dist)
|
|
return np.stack(dist_list)
|
|
|
|
|
|
def read_keypoints(kp_path, origin_size=(256, 256), size=(256, 256), thickness=1):
|
|
key_points = np.loadtxt(kp_path, delimiter=",").astype(np.int32)
|
|
if origin_size != size:
|
|
# resize key_points using simplest way...
|
|
key_points = (key_points * (np.array(size) / np.array(origin_size))).astype(np.int32)
|
|
|
|
# add upper half face by symmetry
|
|
face_pts = key_points[:17, :]
|
|
face_baseline_y = (face_pts[0, 1] + face_pts[-1, 1]) // 2
|
|
upper_symmetry_face_pts = face_pts[1:-1, :].copy()
|
|
# keep x untouched
|
|
upper_symmetry_face_pts[:, 1] = face_baseline_y + (face_baseline_y - upper_symmetry_face_pts[:, 1]) * 2 // 3
|
|
key_points = np.vstack((key_points, upper_symmetry_face_pts[::-1, :]))
|
|
|
|
assert key_points.shape == (83, 2)
|
|
|
|
part_labels = np.zeros((len(DLIB_LANDMARKS_PART_LIST), *size), np.uint8)
|
|
part_edge = np.zeros(size, np.uint8)
|
|
for i, edge_list in enumerate(DLIB_LANDMARKS_PART_LIST):
|
|
indices = [item for sublist in edge_list for item in sublist]
|
|
pts = key_points[indices, :]
|
|
cv2.fillPoly(part_labels[i], pts=[pts], color=1)
|
|
if i in [1, 2]:
|
|
# some part of landmarks is a line
|
|
cv2.polylines(part_edge, [pts], isClosed=False, color=1, thickness=thickness)
|
|
else:
|
|
cv2.drawContours(part_edge, [pts], 0, color=1, thickness=thickness)
|
|
|
|
return key_points, part_labels, part_edge
|
|
|
|
|
|
def edge_map(img, part_labels, part_edge, remove_edge_within_face=True):
|
|
edges = feature.canny(np.array(img.convert("L")))
|
|
if remove_edge_within_face:
|
|
edges = edges * (part_labels.sum(0) == 0) # remove edges within face
|
|
edges = part_edge + edges
|
|
return edges
|