如何使用凸包多边形裁剪面部区域
How to crop face regions using convexhull polygons
我正在使用 dlib 库中的地标点到 select 我脸上的前额、鼻子和眼睛区域,基于这个问题:。它就像一个魅力,我有我想要的点,我想做的是裁剪使用凸包多边形设置地标的图像。
我想做的是从这里开始:
对此:
然后保存
有什么办法吗?即使它看起来不漂亮。这是我当前的面部跟踪代码:
import cv2
import dlib
from imutils import face_utils
import imutils
import numpy as np
cap = cv2.VideoCapture(0)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_81_face_landmarks.dat")
def face_remap(shape):
remapped_image = cv2.convexHull(shape)
return remapped_image
while True:
_, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
out_face = np.zeros_like(frame)
faces = detector(gray, 1)
for face in faces:
x1 = face.left()
y1 = face.top()
x2 = face.right()
y2 = face.bottom()
aam = [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 77, 75, 75, 55, 69, 70, 71, 80, 72, 73, 79, 74, 78]
landmarks = predictor(gray, face)
shape = face_utils.shape_to_np(landmarks)
remapped_shape = np.zeros_like(shape)
feature_mask = np.zeros((frame.shape[0], frame.shape[1]))
x_pts = []
y_pts = []
for n in (aam):
x = landmarks.part(n).x
y = landmarks.part(n).y
x_pts.append(x)
y_pts.append(y)
cv2.circle(frame, (x, y), 1, (255, 0, 0), -1)
x1 = min(x_pts)
x2 = max(x_pts)
y1 = min(y_pts)
y2 = max(y_pts)
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 3)
cv2.imshow("out", frame)
k = cv2.waitKey(1)
if k == 27:
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] = frame[feature_mask]
cv2.imwrite("out_face.png", out_face)
break
cap.release()
cv2.destroyAllWindows()
此代码将从背景中裁剪面部图像并设置地标,如示例所示。但是我想在地标周围裁剪
您只选择了 dlib 在面部识别出的 81 个地标中的一个子集,并丢弃了与嘴巴、下巴和面部外轮廓相关的地标。
您应该做一个额外的选择,只留下您感兴趣的区域边界上的点。
此外,您应该 排序 选定的点,这样 以正确的顺序 连接它们将形成一个多边形,准确标记您要裁剪的区域.
获得多边形后,您可以使用 this method 对其进行裁剪:
- 使用
cv2.fillPoly()
从多边形绘制蒙版。
- 使用
cv2.bitwsie_and()
从图像中仅获取蒙版的裁剪多边形部分。
- 使用
cv2.boundingRect()
获取裁剪区域的边界矩形。
正如@Shai 在评论中提到的,你的形状根本不是凸起的。
要从 convexhull 多边形中裁剪图像,您可以先从 convexhull 中获取蒙版,然后使用蒙版对原始图像应用按位和运算。
也许这样的功能会有所帮助。
def mask_from_contours(ref_img, contours):
mask = numpy.zeros(ref_img.shape, numpy.uint8)
mask = cv2.drawContours(mask, contours, -1, (255,255,255), -1)
return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
如果您只是想从多边形中裁剪图像,您可以通过编程方式连接地标,通过地标索引进行硬编码,并从多边形中获取遮罩,然后应用按位与运算。
这是填充多边形的代码示例。
import numpy as np
import cv2
import matplotlib.pyplot as plt
a3 = np.array( [[[10,10],[100,10],[100,100],[10,100]]], dtype=np.int32 )
im = np.zeros([240,320],dtype=np.uint8)
cv2.fillPoly( im, a3, 255 )
我正在使用 dlib 库中的地标点到 select 我脸上的前额、鼻子和眼睛区域,基于这个问题:
我想做的是从这里开始:
对此:
然后保存
有什么办法吗?即使它看起来不漂亮。这是我当前的面部跟踪代码:
import cv2
import dlib
from imutils import face_utils
import imutils
import numpy as np
cap = cv2.VideoCapture(0)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_81_face_landmarks.dat")
def face_remap(shape):
remapped_image = cv2.convexHull(shape)
return remapped_image
while True:
_, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
out_face = np.zeros_like(frame)
faces = detector(gray, 1)
for face in faces:
x1 = face.left()
y1 = face.top()
x2 = face.right()
y2 = face.bottom()
aam = [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 77, 75, 75, 55, 69, 70, 71, 80, 72, 73, 79, 74, 78]
landmarks = predictor(gray, face)
shape = face_utils.shape_to_np(landmarks)
remapped_shape = np.zeros_like(shape)
feature_mask = np.zeros((frame.shape[0], frame.shape[1]))
x_pts = []
y_pts = []
for n in (aam):
x = landmarks.part(n).x
y = landmarks.part(n).y
x_pts.append(x)
y_pts.append(y)
cv2.circle(frame, (x, y), 1, (255, 0, 0), -1)
x1 = min(x_pts)
x2 = max(x_pts)
y1 = min(y_pts)
y2 = max(y_pts)
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 3)
cv2.imshow("out", frame)
k = cv2.waitKey(1)
if k == 27:
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] = frame[feature_mask]
cv2.imwrite("out_face.png", out_face)
break
cap.release()
cv2.destroyAllWindows()
此代码将从背景中裁剪面部图像并设置地标,如示例所示。但是我想在地标周围裁剪
您只选择了 dlib 在面部识别出的 81 个地标中的一个子集,并丢弃了与嘴巴、下巴和面部外轮廓相关的地标。
您应该做一个额外的选择,只留下您感兴趣的区域边界上的点。 此外,您应该 排序 选定的点,这样 以正确的顺序 连接它们将形成一个多边形,准确标记您要裁剪的区域.
获得多边形后,您可以使用 this method 对其进行裁剪:
- 使用
cv2.fillPoly()
从多边形绘制蒙版。 - 使用
cv2.bitwsie_and()
从图像中仅获取蒙版的裁剪多边形部分。 - 使用
cv2.boundingRect()
获取裁剪区域的边界矩形。
正如@Shai 在评论中提到的,你的形状根本不是凸起的。
要从 convexhull 多边形中裁剪图像,您可以先从 convexhull 中获取蒙版,然后使用蒙版对原始图像应用按位和运算。
也许这样的功能会有所帮助。
def mask_from_contours(ref_img, contours):
mask = numpy.zeros(ref_img.shape, numpy.uint8)
mask = cv2.drawContours(mask, contours, -1, (255,255,255), -1)
return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
如果您只是想从多边形中裁剪图像,您可以通过编程方式连接地标,通过地标索引进行硬编码,并从多边形中获取遮罩,然后应用按位与运算。
这是填充多边形的代码示例。
import numpy as np
import cv2
import matplotlib.pyplot as plt
a3 = np.array( [[[10,10],[100,10],[100,100],[10,100]]], dtype=np.int32 )
im = np.zeros([240,320],dtype=np.uint8)
cv2.fillPoly( im, a3, 255 )