OpenCV实现人脸检测

5,633 阅读6分钟

前言

  人脸检测与识别是深受关注的技术。目前,人脸检测与识别有很多的算法实现,常用的人脸检测的算法有Dilb,OpenCV,OpenFace,MTCNN等。常用人脸识别的算法包括FaceNet,InsightFace模型等。

  此文是当初做人脸识别项目时根据他人博客做的小程序,全文比较简单,因此在我介绍人脸识别项目前先介绍一下简单的OpenCV人脸检测

一、OpenCV人脸检测原理

  OpenCV采用的是基于Haar的cascade分类器。

  基于Haar特征的cascade分类器是Paul Viola和 Michael Jone在2001年发表的论文”Rapid Object Detection using a Boosted Cascade of Simple Features”中提出的一种有效的物品检测方法。它是一种机器学习方法,通过许多正负样例中训练得到cascade方程,然后将其应用于其他图片。

  和许多分类器的训练一样,需要根据大量正样例(包含人脸的图片)和负样例(不包含人脸的图片)来进行训练,由此在这些图片中获取特征。

  Haar特征包含三种:边缘特征、线性特征、中心特征和对角线特征。每种分类器都从图片中提取出对应的特征。

  对于下图来说,第一个特征根据眼睛所在位置通常比脸颊和鼻子更黑来选择,即横的黑道将人脸中较暗的双眼提取了出来;而第二个特征选择的依据则是眼睛比鼻梁要黑,即竖的白道将人脸中较亮的鼻梁提取了出来。

  就如何在大量特征中选择最好的特征这个问题来说,采用了Adaboost的方法,首先通过机器学习找出人脸分类效果最好、错误率最小的特征。训练开始时,所有训练集中的图片具有相同的权重,对于被分类错误的图片,提升权重,重新计算出新的错误率和新的权重。直到错误率或迭代次数达到要求。

  最终的分类器是这些弱分类器的加权和。之所以称之为弱分类器是因为每个分类器不能单独分类图片,但是将他们聚集起来就形成了强分类器。论文表明,只需要200个特征的分类器在检测中的精确度达到了95%。最终的分类器大约有6000个特征。(将超过160000个特征减小到6000个,这是非常大的进步了)

  事实上,一张图片绝大部分的区域都不是人脸。如果对一张图片的每个角落都提取6000个特征,将会浪费巨量的计算资源。如果能找到一个简单的方法能够检测某个窗口是不是人脸区域,如果该窗口不是人脸区域,那么就只看一眼便直接跳过,也就不用进行后续处理了,这样就能集中精力判别那些可能是人脸的区域。

  为此,有人引入了Cascade 分类器。它不是将6000个特征都用在一个窗口,而是将特征分为不同的阶段,然后一个阶段一个阶段的应用这些特征(通常情况下,前几个阶段只有很少量的特征)。如果窗口在第一个阶段就检测失败了,那么就直接舍弃它,无需考虑剩下的特征。如果检测通过,则考虑第二阶段的特征并继续处理。如果所有阶段的都通过了,那么这个窗口就是人脸区域。

  作者的检测器将6000+的特征分为了38个阶段,前五个阶段分别有1,10,25,25,50个特征(前文图中提到的识别眼睛和鼻梁的两个特征实际上是Adaboost中得到的最好的两个特征)。根据作者所述,平均每个子窗口只需要使用6000+个特征中的10个左右。

二、OpenCV实现人脸检测

  实际上OpenCV自带的Harr级联分类器是成熟的分类器,并不需要再次进行训练,所需代码量非常少。

  OpenCV包含许多训练好的分类器,比如脸、眼、微笑等。这些XML文件存储在opencv/data/haarcascades/文件夹中,使用时可以直接调取。

import cv2

path = '【图片】' #图片地址
img = cv2.imread(path) #读取图片

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+'haarcascade_frontalface_default.xml') #获取人脸级联分类器,'.xml'文件里包含训练出来的人脸特征

faces = face_cascade.detectMultiScale(img,scaleFactor=【放大倍数】,minNeighbors=【重复识别次数】) #用分类器进行人脸识别,返回人脸坐标列表

for (x,y,w,h) in faces: #对每一张人脸进行操作
    img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) #画出人脸框,倒数两个参数分别为颜色和线条宽度,其中颜色采用BGR格式

cv2.imshow('img',img) #展示人脸框

  怎么样,是不是很简单~

  以《庆余年》海报为例,人脸区域使用蓝色方框圈出。

  除了人脸分类器,还有人眼分类器

eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+'haarcascade_eye.xml')

  还有微笑分类器

smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+'haarcascade_smile.xml')

  代码与示例代码相似,加载相应模型,只不过为了节省计算资源,需要在识别出的人脸框内再进行检测。

  示例代码:

for (x,y,w,h) in faces:
    img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    # 框选出人脸区域,在人脸区域而不是全图中进行人眼检测,节省计算资源
    face_area = img[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(face_area)
    # 用人眼级联分类器引擎在人脸区域进行人眼识别,返回的eyes为眼睛坐标列表
    for (ex,ey,ew,eh) in eyes:
        #画出人眼框,绿色,画笔宽度为1
        cv2.rectangle(face_area,(ex,ey),(ex+ew,ey+eh),(0,255,0),1)

三、调用摄像头实时人脸检测

  原理也比较简单,OpenCV有调用电脑摄像头的函数,然后用循环实时把人脸框显示出来。

import cv2

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+'haarcascade_frontalface_default.xml')

# 调用摄像头摄像头
cap = cv2.VideoCapture(0) #opencv的函数,参数0代表调用电脑自带摄像,若改为1则调用外设摄像

while(True):
    # 获取摄像头拍摄到的画面
    ret, frame = cap.read()
    faces = face_cascade.detectMultiScale(frame, 1.3, 5)
    img = frame
    for (x,y,w,h) in faces:
        # 画出人脸框,蓝色,画笔宽度微
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
        
    # 实时展示效果画面
    cv2.imshow('frame2',img)
    # 每5毫秒监听一次键盘动作
    if cv2.waitKey(5) & 0xFF == ord('q'):
        break

# 最后,关闭所有窗口
cap.release()
cv2.destroyAllWindows()

  动态监测如下:

(本来我想用我女朋友的照片,毕竟我手机里有很多,奈何她不同意,只好选择另一张照片,正好也测试一下非现实人脸是否可以识别)

  下期采用深度学习实现人脸识别(在检测的基础上识别出人物对应信息)

  后文MTCNN+FaceNet构建人脸识别详解

缺陷

  1. 脸部倾斜时识别不出人脸区域
  2. 2D识别,图片即可识别,并不是根据人脸的三维建模

参考资料

1.使用Haar Cascade 进行人脸识别

2.十行python代码实现人脸识别