计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术

人脸识别的前提肯定是先得找到图像中的人脸,再经过其它方法进行人脸的识别,这篇就来总结下如何进行人脸的检测。

本章代码以图片为例,如果想要实现视频人脸实时检测追踪,只需加入读入视频,然后检测帧图片即可。

本章代码有参考和引用,但是不记得原博了,原谅啦~~

(文中有帅哥!!!哈哈哈哈  我是个小花痴,鉴定完毕! )

1. dlib

如果你了解过一点点人脸检测,那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()

效果图如下:

计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术_第1张图片

这时候一定要用另外一种方法做比较了。

2.CascadeClassifier

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() 

效果图:

计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术_第2张图片

对比效果自己看吧。

3. dlib68点

不过对于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)

效果图:

计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术_第3张图片

4. CascadeClassifier定义单个五官

相比于dlib的68个点定位单个五官,其实CascadeClassifier也有对应的数据,只要你想要,没有找不到。

举个例子吧,如果想找到眼睛:

eye_cascade = cv2.CascadeClassifier('./data/cascade_files/haarcascade_eye.xml')

5. 有趣的来了,换脸小魔术

点看下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吧,赖煜哲和马天宇,哈哈哈,两大男神!

计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术_第4张图片计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术_第5张图片

噔噔蹬蹬~~

结果:计算机视觉——OpenCV对象检测(人脸检测dlib,CascadeClassifier等)以及换脸小魔术_第6张图片是不是毫无PS感觉呢~~

附上定义的函数,参考来的:

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))

 

你可能感兴趣的:(计算机视觉,opencv,dlib,换脸,人脸检测)