使用 dlib 面部地标裁剪面部
Cropping face using dlib facial landmarks
我正在尝试使用 dlib 识别的面部标志来裁剪面部。右边的眉毛引起了问题 - 作物平整而不是沿着眉毛弧线。
我做错了什么?
from imutils import face_utils
import imutils
import numpy as np
import collections
import dlib
import cv2
def face_remap(shape):
remapped_image = shape.copy()
# left eye brow
remapped_image[17] = shape[26]
remapped_image[18] = shape[25]
remapped_image[19] = shape[24]
remapped_image[20] = shape[23]
remapped_image[21] = shape[22]
# right eye brow
remapped_image[22] = shape[21]
remapped_image[23] = shape[20]
remapped_image[24] = shape[19]
remapped_image[25] = shape[18]
remapped_image[26] = shape[17]
# neatening
remapped_image[27] = shape[0]
return remapped_image
"""
MAIN CODE STARTS HERE
"""
# load the input image, resize it, and convert it to grayscale
image = cv2.imread("images/faceCM1.jpg")
image = imutils.resize(image, width=500)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
out_face = np.zeros_like(image)
# initialize dlib's face detector (HOG-based) and then create the facial landmark predictor
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(SHAPE_PREDICTOR)
# detect faces in the grayscale image
rects = detector(gray, 1)
# loop over the face detections
for (i, rect) in enumerate(rects):
"""
Determine the facial landmarks for the face region, then convert the facial landmark (x, y)-coordinates to a NumPy array
"""
shape = predictor(gray, rect)
shape = face_utils.shape_to_np(shape)
#initialize mask array
remapped_shape = np.zeros_like(shape)
feature_mask = np.zeros((image.shape[0], image.shape[1]))
# we extract the face
remapped_shape = face_remap(shape)
cv2.fillConvexPoly(feature_mask, remapped_shape[0:27], 1)
feature_mask = feature_mask.astype(np.bool)
out_face[feature_mask] = image[feature_mask]
cv2.imshow("mask_inv", out_face)
cv2.imwrite("out_face.png", out_face)
sample image of cropped face showing the issue
因为你提供的脸型不是凸脸。
fillConvexPoly 仅适用于凸形,在这种情况下,有一个凹角(在#27 点),因此结果一团糟。
要解决此问题,请将函数修改为
def face_remap(shape):
remapped_image = cv2.convexHull(shape)
return remapped_image
这会给你一个看起来像这样的结果。
现在你可以写更多的代码来删除额头上的三角形部分(如果你想要那样的话)
使用由 68 个地标形成的凸包并不能完全达到预期的输出,所以我使用 scikit-image
而不是 OpenCV
来解决这个问题
1.加载图像并预测 68 个地标
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
img = dlib.load_rgb_image('mean.jpg')
rect = detector(img)[0]
sp = predictor(img, rect)
landmarks = np.array([[p.x, p.y] for p in sp.parts()])
2。 Select代表脸型的界标
(我不得不颠倒眉毛标志的顺序,因为 68 landmarks 没有按顺序来描述面部轮廓)
outline = landmarks[[*range(17), *range(26,16,-1)]]
3。使用 scikit-image
使用这些地标绘制多边形
Y, X = skimage.draw.polygon(outline[:,1], outline[:,0])
4。用零创建一个 canvas 并使用多边形作为原始图像的掩码
cropped_img = np.zeros(img.shape, dtype=np.uint8)
cropped_img[Y, X] = img[Y, X]
为了完整起见,我在下面提供了一个使用 scipy.spatial.ConvexHull 的解决方案,如果此选项仍然是首选
vertices = ConvexHull(landmarks).vertices
Y, X = skimage.draw.polygon(landmarks[vertices, 1], landmarks[vertices, 0])
cropped_img = np.zeros(img.shape, dtype=np.uint8)
cropped_img[Y, X] = img[Y, X]
我正在尝试使用 dlib 识别的面部标志来裁剪面部。右边的眉毛引起了问题 - 作物平整而不是沿着眉毛弧线。
我做错了什么?
from imutils import face_utils
import imutils
import numpy as np
import collections
import dlib
import cv2
def face_remap(shape):
remapped_image = shape.copy()
# left eye brow
remapped_image[17] = shape[26]
remapped_image[18] = shape[25]
remapped_image[19] = shape[24]
remapped_image[20] = shape[23]
remapped_image[21] = shape[22]
# right eye brow
remapped_image[22] = shape[21]
remapped_image[23] = shape[20]
remapped_image[24] = shape[19]
remapped_image[25] = shape[18]
remapped_image[26] = shape[17]
# neatening
remapped_image[27] = shape[0]
return remapped_image
"""
MAIN CODE STARTS HERE
"""
# load the input image, resize it, and convert it to grayscale
image = cv2.imread("images/faceCM1.jpg")
image = imutils.resize(image, width=500)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
out_face = np.zeros_like(image)
# initialize dlib's face detector (HOG-based) and then create the facial landmark predictor
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(SHAPE_PREDICTOR)
# detect faces in the grayscale image
rects = detector(gray, 1)
# loop over the face detections
for (i, rect) in enumerate(rects):
"""
Determine the facial landmarks for the face region, then convert the facial landmark (x, y)-coordinates to a NumPy array
"""
shape = predictor(gray, rect)
shape = face_utils.shape_to_np(shape)
#initialize mask array
remapped_shape = np.zeros_like(shape)
feature_mask = np.zeros((image.shape[0], image.shape[1]))
# we extract the face
remapped_shape = face_remap(shape)
cv2.fillConvexPoly(feature_mask, remapped_shape[0:27], 1)
feature_mask = feature_mask.astype(np.bool)
out_face[feature_mask] = image[feature_mask]
cv2.imshow("mask_inv", out_face)
cv2.imwrite("out_face.png", out_face)
sample image of cropped face showing the issue
因为你提供的脸型不是凸脸。 fillConvexPoly 仅适用于凸形,在这种情况下,有一个凹角(在#27 点),因此结果一团糟。
要解决此问题,请将函数修改为
def face_remap(shape):
remapped_image = cv2.convexHull(shape)
return remapped_image
这会给你一个看起来像这样的结果。
现在你可以写更多的代码来删除额头上的三角形部分(如果你想要那样的话)
使用由 68 个地标形成的凸包并不能完全达到预期的输出,所以我使用 scikit-image
而不是 OpenCV
1.加载图像并预测 68 个地标
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
img = dlib.load_rgb_image('mean.jpg')
rect = detector(img)[0]
sp = predictor(img, rect)
landmarks = np.array([[p.x, p.y] for p in sp.parts()])
2。 Select代表脸型的界标
(我不得不颠倒眉毛标志的顺序,因为 68 landmarks 没有按顺序来描述面部轮廓)
outline = landmarks[[*range(17), *range(26,16,-1)]]
3。使用 scikit-image
Y, X = skimage.draw.polygon(outline[:,1], outline[:,0])
4。用零创建一个 canvas 并使用多边形作为原始图像的掩码
cropped_img = np.zeros(img.shape, dtype=np.uint8)
cropped_img[Y, X] = img[Y, X]
为了完整起见,我在下面提供了一个使用 scipy.spatial.ConvexHull 的解决方案,如果此选项仍然是首选
vertices = ConvexHull(landmarks).vertices
Y, X = skimage.draw.polygon(landmarks[vertices, 1], landmarks[vertices, 0])
cropped_img = np.zeros(img.shape, dtype=np.uint8)
cropped_img[Y, X] = img[Y, X]