- 【Dlib】人脸检测、特征点检测、人脸对齐、人脸识别
- 深度学习与人脸识别之-脸部分割与校正
- github项目FaceSegmentation
- http://dlib.net/face_alignment.py.html
- 知乎问答
首先获取模型,下载地址在这,我使用的是获取脸部68个关键点的模型shape_predictor_68_face_landmarks.dat
68关键点位置示意图如下:
首先贴出python代码
"""
代码功能:
1. 用dlib人脸检测器检测出人脸,返回的人脸矩形框
2. 对检测出的人脸进行关键点检测并用圈进行标记
3. 将检测出的人脸关键点信息写到txt文本中
"""
import cv2
import dlib
import numpy as np
predictor_model = 'shape_predictor_68_face_landmarks.dat'
detector = dlib.get_frontal_face_detector()# dlib人脸检测器
predictor = dlib.shape_predictor(predictor_model)
# cv2读取图像
test_img_path = "input/Messi.jpg"
output_pos_info = "output_pos_info/Messi.txt"
img = cv2.imread(test_img_path)
file_handle = open(output_pos_info, 'a')
# 取灰度
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# 人脸数rects(rectangles)
rects = detector(img_gray, 0)
for i in range(len(rects)):
landmarks = np.matrix([[p.x, p.y] for p in predictor(img,rects[i]).parts()])
for idx, point in enumerate(landmarks):
# 68点的坐标
pos = (point[0, 0], point[0, 1])
print(idx+1, pos)
pos_info = str(point[0, 0]) + ' ' + str(point[0, 1]) + '\n'
file_handle.write(pos_info)
# 利用cv2.circle给每个特征点画一个圈,共68个
cv2.circle(img, pos, 3, color=(0, 255, 0))
# 利用cv2.putText输出1-68
#font = cv2.FONT_HERSHEY_SIMPLEX
#cv2.putText(img, str(idx+1), pos, font, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
file_handle.close()
cv2.imwrite("output/Messi_keypoints.png", img)
大致过程如下:先用人脸检测器获取到人脸矩形框rectangles,再用68点shape模型获取full_object_detection
对象。最后将关键点标记出来,并写入文本中。
rects = detector(img_gray, 0)
返回的是人脸的bounding box
,多张人脸可能对应有多个。
predictor = dlib.shape_predictor(predictor_model)
predictor(img,rects[i]).parts()
predictor(img, rects[i]).part(1)
predictor返回的是一个full_object_detection
对象,通过parts()
可以获得所有关键点的位置,通过part(idx)
idx从0开始,可以获取某个关键点的信息。
测试图片的原图与标注关键点后图片如下图所示。
可以采用dlib.get_face_chip()
来分割人脸
"""
代码功能:
1. 用dlib人脸检测器检测出人脸,返回的人脸矩形框
2. 对检测出的人脸进行关键点检测并切割出人脸
"""
import cv2
import dlib
import numpy as np
predictor_model = 'shape_predictor_68_face_landmarks.dat'
detector = dlib.get_frontal_face_detector()# dlib人脸检测器
predictor = dlib.shape_predictor(predictor_model)
# cv2读取图像
test_img_path = "input/Messi.jpg"
img = cv2.imread(test_img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 人脸数rects
rects = detector(img, 0)
# faces存储full_object_detection对象
faces = dlib.full_object_detections()
for i in range(len(rects)):
faces.append(predictor(img,rects[i]))
face_images = dlib.get_face_chips(img, faces, size=320)
for image in face_images:
cv_bgr_img = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
cv2.imwrite('output/Messi_clip.png', cv_bgr_img)
Dlib检测出的脸部区域对于下巴和额头区域会做过多的裁剪,并且分割使用的是矩形框。
分割结果如图
先用dlib等打点工具把人脸最外层的landmark点打出来,然后利用opencv的convexhull得到凸包然后就可以抠出人脸区域了.
python代码(获取人脸的掩模)
def get_image_hull_mask(image_shape, image_landmarks, ie_polys=None):
# get the mask of the image
if image_landmarks.shape[0] != 68:
raise Exception(
'get_image_hull_mask works only with 68 landmarks')
int_lmrks = np.array(image_landmarks, dtype=np.int)
#hull_mask = np.zeros(image_shape[0:2]+(1,), dtype=np.float32)
hull_mask = np.full(image_shape[0:2] + (1,), 0, dtype=np.float32)
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[0:9],
int_lmrks[17:18]))), (1,))
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[8:17],
int_lmrks[26:27]))), (1,))
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[17:20],
int_lmrks[8:9]))), (1,))
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[24:27],
int_lmrks[8:9]))), (1,))
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[19:25],
int_lmrks[8:9],
))), (1,))
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[17:22],
int_lmrks[27:28],
int_lmrks[31:36],
int_lmrks[8:9]
))), (1,))
cv2.fillConvexPoly(hull_mask, cv2.convexHull(
np.concatenate((int_lmrks[22:27],
int_lmrks[27:28],
int_lmrks[31:36],
int_lmrks[8:9]
))), (1,))
# nose
cv2.fillConvexPoly(
hull_mask, cv2.convexHull(int_lmrks[27:36]), (1,))
if ie_polys is not None:
ie_polys.overlay_mask(hull_mask)
print()
return hull_mask
方法利用的就是opencv的convexhull得到凸包然后就可以抠出人脸区域。
得到掩模,这里使用两种方式来得到人脸区域
- 将mask作为$\alpha$通道,来控制图片区域的透明度,最后得到图片是4通道的
def merge_add_alpha(img_1, mask):
# merge rgb and mask into a rgba image
r_channel, g_channel, b_channel = cv2.split(img_1)
if mask is not None:
alpha_channel = np.ones(mask.shape, dtype=img_1.dtype)
alpha_channel *= mask*255
else:
alpha_channel = np.zeros(img_1.shape[:2], dtype=img_1.dtype)
img_BGRA = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))
return img_BGRA
分割结果为
- 掩模与原始图像进行与运算,返回图像是三通道。
def merge_add_mask(img_1, mask):
if mask is not None:
height = mask.shape[0]
width = mask.shape[1]
channel_num = mask.shape[2]
for row in range(height):
for col in range(width):
for c in range(channel_num):
if mask[row, col, c] == 0:
mask[row, col, c] = 0
else:
mask[row, col, c] = 255
r_channel, g_channel, b_channel = cv2.split(img_1)
r_channel = cv2.bitwise_and(r_channel, mask)
g_channel = cv2.bitwise_and(g_channel, mask)
b_channel = cv2.bitwise_and(b_channel, mask)
res_img = cv2.merge((b_channel, g_channel, r_channel))
else:
res_img = img_1
return res_img
分割结果为
思路比较简单,计算两眼连线与水平线的夹角,然后通过角度得到对应的旋转矩阵。对图片进行相应的变换。
def single_face_alignment(face, landmarks):
eye_center = ((landmarks[36, 0] + landmarks[45, 0]) * 1. / 2, # 计算两眼的中心坐标
(landmarks[36, 1] + landmarks[45, 1]) * 1. / 2)
dx = (landmarks[45, 0] - landmarks[36, 0]) # note: right - right
dy = (landmarks[45, 1] - landmarks[36, 1])
angle = math.atan2(dy, dx) * 180. / math.pi # 计算角度
RotateMatrix = cv2.getRotationMatrix2D(eye_center, angle, scale=1) # 计算仿射矩阵
align_face = cv2.warpAffine(face, RotateMatrix, (face.shape[0], face.shape[1])) # 进行放射变换,即旋转
return align_face
人脸对齐后如图
这样就完成人脸检测-->人脸关键点检测-->人脸分割-->人脸对齐。
可以用以上方法对fer2013数据集进行预处理,如图所示。