基础
使用Haar基于特征的级联分类器记性物体检测是一个很有效的物体检测方法。它是一个基于机器学习的方法,从大量积极和消极的图像里训练一个级联函数。然后用来在其他图像里检测物体。
这里我们来做面部识别,初始状态下,算法需要大量积极图像(面部的图像)和消极图像(没有面部的图像)来训练分类器。然后我们需要从里面提取特征。这用到了下面的图像展示的haar特征。他们就像我们的卷积核一样,每个特征是从黑色矩形的像素和里减去白色矩形的像素和得到的一个值。
现在每个核的所有可能的大小和位置都用来计算很多的特征(可以想象一下需要多啊的计算量,即使是一个24x24的窗口的结果也是超过160000个特征)。对于每个特征计算,我们需要找到黑色和白色方块下的像素和。要解决这个,他们提出了全景图像。它只简单的计算像素和,像素的数量可能是多大,需要运算的只是4个像素。这会让事情变得很快。
但是所有这些要计算的特征里,大部分是无关的,比如下面这张图像,顶上的行显示出两个号的特征,第一个选择的特征看起来集中在眼睛的区域要比鼻子和脸颊的区域要暗这个属性上。第二个选择的特征依赖眼睛比鼻梁要暗这个属性。但是应用在脸颊或者其他地方的同样的窗口是无关的,所以我们如何从160000+个特整理找到最好的特征呢?可以用Adaboost。
我们在所有训练图像上应用每个特征,对每个特征,它找到最佳的阈值来把脸分类成积极的和消极的。但是显然,有误差和分类错误。我们选择错误率最小的特征,这表示这些特征能够最好的分类有脸的图和没脸的图。(这个过程没那么简单,每个图像在开始的权重都一样,每次分类后,错误分类的图像的权重都增加,然后再次执行同样的过程,计算新的错误率,新的权重。持续这个过程直到需要的准确度或者错误率达到标准或者直到了需要的数量的特征)
最后的分类器是这些弱分类器的加权和。他被叫做弱分类器是因为它不能分类图像,只能和其他在一起组成强分类器。论文说即使200个特征也能提供95%的准确率。他们的最终的设置有6000个特征(想象一下从160000+个特征到6000个特征,这是个巨大的节省)
所以现在你取一个图像,取出每个24x24的窗口,应用6000个特征。检查是否它是脸或者不是。这是不是很美效率?是的,作者有个好办法解决。
在图像里,大部分图像区域是非脸的区域,所以最好的办法是有一个简单办法来检查一个区域不是脸。如果不是脸,就把它扔掉。再也不处理。这样我们可以有更多时间来处理有脸的区域。
它们提出了一个概念级联分类器。不用把所有6000个特征应用到一个窗口上,而是把特征分组成不同的阶段,一个一个应用。(一般最开始的几个阶段会包含非常少的特征)。如果一个窗口在最开始阶段失败了,就丢弃,我们不再上面继续尝试剩下的特征,如果通过,应用第二个阶段的特征并持续这个过程。通过了所有阶段的窗口就是脸部区域。
作者的检测器有6000+个特征,38个阶段,最开始的5个阶段有1,10,25,25和50个特征(上面图像的两个特征实际上是从Adaboost得到的最好的两个特征)。根据作者说的,平均来说,对于每个子窗口6000+个特征中改的10个会被评估。
这就是简单直观的介绍Viola-Jones面部识别是如何工作的。
OpenCV里的Haar-cascade检测
OpenCV提供了检测器和训练器。如果你想训练你自己的分类器来识别诸如汽车啊,飞机什么的,你可以使用OpenCV来创建一个。详细内容见:Cascade Classifier Training
这里我们只看检测器,OpenCV已经包含了很多训练过的分类器,面部的,眼睛的,笑容的等。那些XML文件存在opencv/data/haarcascades/目录。让我们创建一张脸和眼的检测器吧。
首先我们需要加载必须的XML分类器,然后用灰度模式加载我们的输入图像(或者视频)。
import numpy as np
import cv2face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')img = cv2.imread('sachin.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
现在我们在图像里找到脸。如果找到脸,它会返回检测到的脸的位置(x, y, w,h)。当我们得到这些位置,我们可以为脸创建一个ROI然后在这个ROI上应用眼睛检测(因为眼睛总是在脸上的!)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
img = cv2.rectangle(img,(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)
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
END