级联分类器原理介绍:
使用基于Haar特征的级联分类器进行对象检测是Paul Viola和Michael Jones在其论文“使用简单特征的增强级联进行快速对象检测”中于2001年提出的一种有效的对象检测方法。这是一种基于机器学习的方法,其中从许多正负图像中训练级联函数。然后用于检测其他图像中的对象。
在这里,我们将进行人脸检测。最初,该算法需要大量正图像(面部图像)和负图像(无面部图像)来训练分类器。然后,我们需要从中提取特征。为此,使用下图所示的haar功能。它们就像我们的卷积核。每个特征都是通过从黑色矩形下的像素总和中减去白色矩形下的像素总和而获得的单个值。
现在,每个内核的所有可能大小和位置都用于计算大量功能。(试想一下,它需要多少计算?即使是24x24的窗口也会产生超过160000个特征)。对于每个特征计算,我们需要找到白色和黑色矩形下的像素总和。为了解决这个问题,他们引入了积分图像。它简化了仅需要四个像素的操作的像素总和的计算(像素数量可以是多少)。很好,不是吗?它使事情变得超快。
但是在我们计算出的所有这些功能中,大多数都不相关。例如,考虑下图。第一行显示了两个好的功能。选择的第一个特征似乎着眼于眼睛区域通常比鼻子和脸颊区域更暗的性质。选择的第二个功能取决于眼睛比鼻子的鼻梁更黑的属性。但是在脸颊或其他任何地方使用相同的窗户是无关紧要的。那么,我们如何从160000多个功能中选择最佳功能?它是由Adaboost实现的。
为此,我们将所有功能应用于所有训练图像。对于每个特征,它会找到最佳的阈值,该阈值会将面部分为正面和负面。但是很显然,会有错误或分类错误。我们选择错误率最低的特征,这意味着它们是对面部和非面部图像进行最佳分类的功能。(此过程并非如此简单。开始时,每个图像的权重均相等。每次分类后,错误分类的图像的权重都会增加。然后再次执行相同的过程。将计算新的错误率。还要计算新的权重。继续执行该过程,直到达到所需的准确性或错误率或找到所需的功能数量为止。
最终分类器是这些弱分类器的加权和。之所以称其为弱,是因为它本身无法对图像进行分类,而是与其他图像一起构成一个强分类器。该论文说,甚至200个功能都可以提供95%的准确度检测。他们的最终设置具有大约6000个功能。(想象一下,从160000多个功能减少到6000个功能。这是很大的收获)。
因此,现在您拍摄一张照片。取每个24x24窗口。向其应用6000个功能。检查是否有脸。哇..哇..这不是效率低下又费时吗?是的。作者对此有一个很好的解决方案。
在图像中,大多数图像区域是非面部区域。因此,最好有一种简单的方法来检查窗口是否不是面部区域。如果不是,请将其一次性丢弃。不要再处理它。而是将注意力集中在可以有脸的区域。这样,我们可以找到更多时间检查可能的面部区域。
为此,他们引入了级联分类器的概念。不要将所有6000个功能部件应用到一个窗口上,而是将这些功能部件分组到不同的分类器阶段,然后逐一应用。(通常前几个阶段将包含很少的功能)。如果窗口在第一阶段失败,则将其丢弃。我们不考虑其剩余功能。如果通过,则应用功能的第二阶段并继续该过程。经过所有阶段的窗口是一个面部区域。计划如何!
作者的探测器具有6000多个特征,具有38个阶段,在前五个阶段具有1、10、25、25和50个特征。(上图中的两个功能实际上是从Adaboost获得的最佳两个功能)。根据作者的说法,每个子窗口平均评估6000多个功能中的10个。
因此,这是Viola-Jones人脸检测工作原理的简单直观说明。
在OpenCV中通过Haar级联检测完成人脸识别:
OpenCV配备了各种检测器。如果您想为任何物体(例如汽车,飞机等)训练自己的分类器,则可以使用OpenCV创建一个。
OpenCV已经包含许多针对面部,眼睛,微笑等进行过预训练的分类器。这些XML文件存储在opencv/data/haarcascades/
文件夹中。让我们用OpenCV创建面部和眼睛检测器。
首先,我们需要加载所需的XML分类器。然后以灰度模式加载我们的输入图像(或视频)。
现在,我们在图像中找到了面孔。如果找到人脸,则将检测到的人脸的位置返回为Rect(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()
结果如下: