OpenCV学习日记(4)

绪言

本篇主要介绍OpenCV中的人脸识别,初步探究人脸识别中的技术原理。

人脸识别

人脸识别是目前计算机视觉中最重要的部分之一。人脸识别又分两种:静态识别和动态识别,即图片的人脸识别和视频动态的人脸识别,也就是离散和连续的区别。本篇介绍的是人脸识别技术中的Haar级联分类器技术。

Haar级联

Haar特征的概念

我们识别一个图像,必定是抽取它的特征来进行识别,从而排除图像中的干扰,提取相关信息的概率更加高。虽然任意像素都可能影响多个特征,但特征应该比像素数少得多。两个图像的相似程度可以通过它们对应特征的欧氏距离度量。
那么,什么是Haar特征呢?
Haar特征分为四类:边缘特征、线性特征、中心特征和对角线特征,将这些特征组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和‘减’去黑色矩形像素和。
人脸识别——Haar特征、积分图及Haar级联
在人脸识别中,采用的是类Haar特征,该概念最早出现在文献《Robust Real-Time Face Detection, Paul Viola and Michael Jones, Kluwer Academic Publishers, 2001》中,每个类Haar特征都描述了相邻图像区域的对比模式,利用边、顶点和细线生成不同的特征。

Haar级联的概念

Haar级联基于大名鼎鼎的Adaboost算法,Adaboost算法是1995年提出来的一种机器学习算法,其核心原理是:利用训练的弱分类器,通过权重线性叠加,得到一个强分类器。也就是我们常说的“三个臭皮匠顶个诸葛亮”。而Adaboost背后的原理,由于学术水平有限,还没办法完全读懂,读者可以参考以下两篇文章进行理解:
AdaBoost原理详解
基于Haar特征的Adaboost级联人脸检测分类器
也就是说,Haar级联的原理就是通过在分类器训练的过程中,添加特征训练条件,并根据特征在人脸识别中的重要性给予相应的权重,使强分类器可以更好地检测到人脸面部特征。

detectMultiScale()

detectMultiScale是CascadeClassfier类中的一个方法,作用是通过当前的Haar级联,侦测到符合当前Haar级联的像素点。
detectMultiScale()函数的定义
参数:
image:灰度处理过的八位图像(CV_8U)
scaleFactor:在前后两次相继的扫描中,搜索窗口的比例系数。在C++的OpenCV库中被定义为1.1,也就是下一次的扩大比例为此次的110%。
minNeighbors:匹配成功的最小矩形框的数目,最小值为3。
flags:检测时候的状态位取值(默认为0):

参数名称 作用
CASCADE_DO_CANNY_PRUNING=1 利用canny边缘检测来排除一些边缘很少或者很多的图像区域
CASCADE_SCALE_IMAGE=2 正常比例检测
CASCADE_FIND_BIGGEST_OBJECT=4 只检测最大的物体
CASCADE_DO_ROUGH_SEARCH=8 粗略的检测

minSize、maxSize:搜索的最小/最大矩形框尺寸

静态Haar级联的实现

OpenCV3中提供了检测多种人脸特征的Haar级联数据,存放在data/haarcascades中,以xml的形式存在。该xml描述了特征的信息。当然也可以自己创建级联并进行训练,生成属于自己的Haar级联。
检测静态图像的人脸检测代码如下所示:

# 静态照片的人脸检测
import cv2
filename='2100x1400.png'
face_cascade=cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
face_cascade.load('D:/Program Files/OpenCV3/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml') # 加载Haar级联
img=cv2.imread(filename)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
faces=face_cascade.detectMultiScale(gray,1.3,5) # 这里的参数使用我这里推荐的,效果比较好
count1=0
for (x,y,w,h) in faces:
    img=cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    count1+=1
print('Find %d faces in total' % count1)
cv2.namedWindow('Face detected')
cv2.imshow('Face detected',img)
cv2.imwrite('./classmates.jpg',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

import cv2
filename='1200x800.png'
face_cascade=cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
face_cascade.load('D:/Program Files/OpenCV3/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml')
img=cv2.imread(filename)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
faces=face_cascade.detectMultiScale(gray,1.3,5)
count2=0
for (x,y,w,h) in faces:
    img=cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    count2+=1
print('Find %d faces in total' % count2)
cv2.namedWindow('Face detected')
cv2.imshow('Face detected',img)
cv2.imwrite('./classmates.jpg',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

使用Haar级联的步骤为:
1.创建一个CascadeClassifier类对象,并使用load方法载入相应的Haar级联,我这里使用的是frontalface(正脸) Haar级联。
2.载入图像,并通过cvtColor转换出来一张灰度图。
3.通过detectMultiScale找出人脸信息,并存放在一个np.array里面。
4.通过for循环读出脸的四个顶点的坐标,在彩色图上画出一个正方形。
5.显示彩色图。
找到的人脸数量如下图所示:
找到的人脸数量
同时,从这个例子可以看出,分辨率越低,找人脸的效果就越差。分辨率低的那一张有一个人脸框把人家女孩子的胸框进去了(手动捂脸)。

动态Haar级联的实现

我们很多时候,要进行实时处理分析图像。其实也很简单,就是将连续的视频流割为一帧帧,通过对每一帧的分析来得出当前帧的人脸框在哪。
我们先贴出代码,然后再对代码进行分析。

# 视频人脸检测
import cv2
def detect():
    face_cascade=cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
    face_cascade.load('D:/Program Files/OpenCV3/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml')
    eye_cascade=cv2.CascadeClassifier('haarcascade_eye.xml')
    eye_cascade.load('D:/Program Files/OpenCV3/opencv-master/data/haarcascades/haarcascade_eye.xml')
    camera=cv2.VideoCapture(0)
    print('press q to exit.')
    while(camera.isOpened()):
        ret,frame=camera.read()
        gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        faces=face_cascade.detectMultiScale(gray,1.3,5)
        for (x,y,w,h) in faces:
            img=cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
            roi_gray=gray[y:y+h,x:x+w]
            roi_color=img[y:y+h,x:x+w]
            eyes=eye_cascade.detectMultiScale(roi_gray,1.03,5,0,(40,40))
            for (ex,ey,ew,eh) in eyes:
                cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
        cv2.imshow("camera",frame)
        if cv2.waitKey(1) & 0xff == ord("q"):
            break
    camera.release()
    cv2.destroyAllWindows()

if __name__=="__main__":
    detect()

下面来分析代码:

roi_gray=gray[y:y+h,x:x+w]
roi_color=img[y:y+h,x:x+w]

这两句,将侦测出来的人脸矩形框框了起来,存在了roi_gray(灰度图)和roi_color(BGR三色图)里。

eyes=eye_cascade.detectMultiScale(roi_gray,1.03,5,0,(40,40))

然后,在人脸中使用探测眼睛的Haar级联,并且为了防止假阳性,将最小的搜索尺寸控制在了40x40像素。(在detectMultiScale中应调整参数使准确率更高)。
跑个题,我觉得这句代码其实相当巧妙:

if cv2.waitKey(1) & 0xff == ord("q"):
    break

byte为什么要与上0xff?
我们来分析一下,0xff转成二进制是11111111,任何正数和它与都应该是其本身。看上去好像毫无意义。但是0xff本质上是一个int类型的数,当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。不会使得数据改变,从而在判断是否相等的时候不会误判。
我的CPU是64位的,也不知道在32位上面会不会出现cv2.waitKey(1)的结果与ord(“q”)不一致的情况。
测试出来,去掉0xff也是没有问题的。
(本篇未完成)

猜你喜欢

转载自blog.csdn.net/weixin_40427089/article/details/81607258