raycv/data/util/dlib_landmark.py
2020-08-30 14:44:40 +08:00

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