人脸识别的前提肯定是先得找到图像中的人脸,再经过其它方法进行人脸的识别,这篇就来总结下如何进行人脸的检测。
本章代码以图片为例,如果想要实现视频人脸实时检测追踪,只需加入读入视频,然后检测帧图片即可。
本章代码有参考和引用,但是不记得原博了,原谅啦~~
(文中有帅哥!!!哈哈哈哈 我是个小花痴,鉴定完毕! )
如果你了解过一点点人脸检测,那dlib肯定就不陌生了。具体不介绍,只需要在环境中安装dlib(pip install dlib),这种安装一定不陌生啦~~
直接上运用代码:
# -*- coding: utf-8 -*-
import sys
import dlib
import cv2
faces_path = "./data/ma2.jpg"
detector = dlib.get_frontal_face_detector() #人脸分类器
predictor_path = "./data/shape_predictor_68_face_landmarks.dat" #别忘记下载这里导入的文件
predictor = dlib.shape_predictor(predictor_path) #特征提取器
# opencv 读取图片,并显示
img = cv2.imread(faces_path, cv2.IMREAD_COLOR)
b, g, r = cv2.split(img) # 分离三个颜色通道
img2 = cv2.merge([r, g, b]) # 融合三个颜色通道生成新图片
dets = detector(img, 1) #使用detector进行人脸检测 dets为返回的结果
print("Number of faces detected: {}".format(len(dets)))
sp = img2.shape
height=sp[0]
width=sp[1]
for index, face in enumerate(dets):
shape = predictor(img, face) #使用predictor进行人脸关键点识别
miny=height
maxy=0
middleX=shape.part(30).x #鼻子中心点
for i in range(0,68):
print("第%d个点: x=%d y=%d\r\n"%(i,shape.part(i).x,shape.part(i).y))
if shape.part(i).ymaxy:
maxy=shape.part(i).y
#计算人脸外框
deltaY=maxy-miny;
deltaY1=0.9*deltaY
deltaY2=0.5*0.9*deltaY
deltaX=(deltaY+deltaY1+deltaY2)*3/8
left = int(middleX-deltaX)
right=int(middleX+deltaX)
top=int(miny-deltaY1)+10
bottom=int(maxy+deltaY2)+10
# 在图片中标注人脸显示
cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)
cv2.namedWindow(faces_path, cv2.WINDOW_AUTOSIZE)
cv2.imshow(faces_path, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果图如下:
这时候一定要用另外一种方法做比较了。
import cv2
import numpy as np
face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_alt.xml')
scaling_factor = 0.5
image=cv2.imread('./data/ma2.jpg')
frame = cv2.resize(image, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA)
face_rects = face_cascade.detectMultiScale(frame, scaleFactor=1.3, minNeighbors=3)
for (x,y,w,h) in face_rects:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3)
cv2.imshow('Face Detector', frame)
c = cv2.waitKey(0)
cv2.destroyAllWindows()
效果图:
对比效果自己看吧。
不过对于dlib,优势在于可以准确地捕捉到人脸68个点,然后可以随心所欲的调用,可以裁剪出嘴巴、眼睛、鼻子等等,接下去要做什么还不是选择一下点位置的问题~~
识别显示68个点的代码如下:
# _*_ coding:utf-8 _*_
import numpy as np
import cv2
import dlib
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./data/shape_predictor_68_face_landmarks.dat')
# cv2读取图像
img = cv2.imread("./data/ma.jpg")
# 取灰度
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# 人脸数rects
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,pos)
# 利用cv2.circle给每个特征点画一个圈,共68个
cv2.circle(img, pos, 5, color=(0, 255, 0))
# 利用cv2.putText输出1-68
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, str(idx+1), pos, font, 0.8, (0, 0, 255), 1,cv2.LINE_AA)
cv2.namedWindow("img", 2)
cv2.imshow("img", img)
cv2.waitKey(0)
效果图:
相比于dlib的68个点定位单个五官,其实CascadeClassifier也有对应的数据,只要你想要,没有找不到。
举个例子吧,如果想找到眼睛:
eye_cascade = cv2.CascadeClassifier('./data/cascade_files/haarcascade_eye.xml')
点看下68个点的定位
# 68个点
FACE_POINTS = list(range(17, 68)) # 脸
MOUTH_POINTS = list(range(48, 61)) # 嘴巴
RIGHT_BROW_POINTS = list(range(17, 22)) # 右眉毛
LEFT_BROW_POINTS = list(range(22, 27)) # 左眉毛
RIGHT_EYE_POINTS = list(range(36, 42)) # 右眼睛
LEFT_EYE_POINTS = list(range(42, 48)) # 左眼睛
NOSE_POINTS = list(range(27, 35)) # 鼻子
JAW_POINTS = list(range(0, 17)) # 下巴
然后定义换脸函数:
# 换脸函数
def Switch_face(Base_path,cover_path):
im1, landmarks1 = read_im_and_landmarks(Base_path) # 底图
im2, landmarks2 = read_im_and_landmarks(cover_path) # 贴上来的图
if len(landmarks1) == 0 & len(landmarks2) == 0 :
raise ImproperNumber("Faces detected is no face!")
if len(landmarks1) > 1 & len(landmarks2) > 1 :
raise ImproperNumber("Faces detected is more than 1!")
M = transformation_from_points(landmarks1[ALIGN_POINTS],
landmarks2[ALIGN_POINTS])
mask = get_face_mask(im2, landmarks2)
warped_mask = warp_im(mask, M, im1.shape)
combined_mask = numpy.max([get_face_mask(im1, landmarks1), warped_mask],
axis=0)
warped_im2 = warp_im(im2, M, im1.shape)
warped_corrected_im2 = correct_colours(im1, warped_im2, landmarks1)
output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask
return output_im
当然,除了换脸函数,还有很多其他的函数需要定义,比如:获取关键点、获取图像掩码、调整大小、修复肤色等。
先看下两大帅哥的PK吧,赖煜哲和马天宇,哈哈哈,两大男神!
噔噔蹬蹬~~
附上定义的函数,参考来的:
def get_landmarks(im):
rects = detector(im, 1)
if len(rects) > 1:
raise TooManyFaces
if len(rects) == 0:
raise NoFaces
return numpy.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])
def annotate_landmarks(im, landmarks):
'''
人脸关键点,画图函数
'''
im = im.copy()
for idx, point in enumerate(landmarks):
pos = (point[0, 0], point[0, 1])
cv2.putText(im, str(idx), pos,
fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
fontScale=0.4,
color=(0, 0, 255))
cv2.circle(im, pos, 3, color=(0, 255, 255))
return im
def draw_convex_hull(im, points, color):
'''
# 绘制凸多边形 计算凸包
'''
points = cv2.convexHull(points)
cv2.fillConvexPoly(im, points, color=color)
def get_face_mask(im, landmarks):
im = numpy.zeros(im.shape[:2], dtype=numpy.float64)
for group in OVERLAY_POINTS:
draw_convex_hull(im,
landmarks[group],
color=1)
im = numpy.array([im, im, im]).transpose((1, 2, 0))
im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0
im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)
return im
def transformation_from_points(points1, points2):
points1 = points1.astype(numpy.float64)
points2 = points2.astype(numpy.float64)
c1 = numpy.mean(points1, axis=0)
c2 = numpy.mean(points2, axis=0)
points1 -= c1
points2 -= c2
s1 = numpy.std(points1)
s2 = numpy.std(points2)
points1 /= s1
points2 /= s2
U, S, Vt = numpy.linalg.svd(points1.T * points2)
R = (U * Vt).T
return numpy.vstack([numpy.hstack(((s2 / s1) * R,
c2.T - (s2 / s1) * R * c1.T)),
numpy.matrix([0., 0., 1.])])
def read_im_and_landmarks(fname):
im = cv2.imread(fname, cv2.IMREAD_COLOR)
im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR,
im.shape[0] * SCALE_FACTOR))
s = get_landmarks(im)
return im, s
def warp_im(im, M, dshape):
output_im = numpy.zeros(dshape, dtype=im.dtype)
cv2.warpAffine(im,
M[:2],
(dshape[1], dshape[0]),
dst=output_im,
borderMode=cv2.BORDER_TRANSPARENT,
flags=cv2.WARP_INVERSE_MAP)
return output_im
def correct_colours(im1, im2, landmarks1):
blur_amount = COLOUR_CORRECT_BLUR_FRAC * numpy.linalg.norm(
numpy.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -
numpy.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))
blur_amount = int(blur_amount)
if blur_amount % 2 == 0:
blur_amount += 1
im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)
im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)
# Avoid divide-by-zero errors.
im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)
return (im2.astype(numpy.float64) * im1_blur.astype(numpy.float64) /
im2_blur.astype(numpy.float64))