OpenCV Python 级联分类器
【目标】
- Haar 级联目标检测器工作方式;
- Haar 级联分类器检测人脸和人眼
【理论】
基于Haar特征的级联分类器的目标检测是Paul Viola和Michael Jones在2001年的论文中提出的一种有效的目标检测方法。这是一种基于机器学习的方法,从大量的正面和负面图像中训练级联函数。然后用它来检测其他图像中的物体。
这里我们将使用人脸检测。最初,该算法需要大量的正面图像(人脸图像)和负面图像(没有人脸的图像)来训练分类器。然后我们需要从中提取特征。为此,使用下图所示的Haar特征。它们就像卷积核一样。每个特征都是一个单独的值,由黑色矩形下的像素和减去白色矩形下的像素和得到。
现在,每个内核的所有可能的大小和位置都被用来计算大量的特征。(想象一下它需要多少计算量?即使是一个24x24的窗口也会产生超过160000个特征)。对于每个特征计算,我们需要找到白色和黑色矩形下的像素之和。为了解决这个问题,他们引入了积分图。无论您的图像有多大,它都将给定像素的计算减少到只涉及四个像素的操作。不错,不是吗?它让事情变得超快。
但在我们计算的所有这些特征中,大多数都是不相关的。例如,考虑下面的图像。上面一行显示了两个很好的特征。选择的第一个特征似乎集中在眼睛区域通常比鼻子和脸颊区域更黑的属性上。选择的第二个特征依赖于眼睛比鼻梁暗的属性。但同样的窗口应用于脸颊或任何其他地方是无关紧要的。那么,我们如何从160000多个功能中选择出最好的功能呢?它由Adaboost实现。
为此,我们将每个特征应用到所有的训练图像上。对于每个特征,它会找到最佳阈值,将人脸分为正面和负面。显然,会有错误或错误分类。我们选择错误率最小的特征,这意味着它们是最准确地分类人脸和非人脸图像的特征。(这个过程没有这么简单。每张图片在开始时都被赋予了相同的权重。每次分类后,错误分类的图像权重都会增加。然后进行相同的过程。计算新的错误率。还有新的砝码。该过程将继续进行,直到达到所需的精度或错误率或找到所需的特征数量为止)。
最后的分类器是这些弱分类器的加权和。它被称为弱分类器,因为它单独不能对图像进行分类,但与其他分类器一起形成强分类器。论文称,即使200个特征也能提供95%的检测准确率。他们的最终设置有大约6000个功能。(想象一下从160000多个功能减少到6000个功能。这是一个很大的收获)。
现在你得到一个图像。以每个24x24的窗口为例。应用6000个特性。检查一下是不是脸。哇. .这不是有点低效和耗时吗?是的,它是。作者对此有一个很好的解决方案。
在图像中,大部分图像是非人脸区域。因此,最好有一个简单的方法来检查一个窗口是否不是一个人脸。如果不是,一次性丢弃它,不要再处理它。相反,把注意力集中在可以露出脸的部位。这样,我们花更多的时间检查可能的面部区域。
为此,他们引入了分级器级联的概念。不是将所有6000个特征应用到一个窗口上,而是将这些特征分组到分类器的不同阶段并逐个应用。(通常前几个阶段包含的功能会非常少)。如果一个窗口在第一阶段失败,则丢弃它。我们不考虑剩余的功能。如果通过了,应用第二阶段的特性并继续这个过程。通过所有阶段的窗口是一个面区域。那个计划怎么样!
作者的探测器有6000多个特征,共38个阶段,前5个阶段分别为1、10、25、25和50个特征。(上图中的两个特征实际上是从Adaboost中获得的最佳两个特征)。根据作者的说法,每个子窗口平均评估6000多个特征中的10个。
这是对Viola-Jones人脸检测工作原理的一个简单直观的解释。阅读论文以获得更多细节,或查看附加资源部分的参考资料。
【代码】
import cv2
def detectAndDisplay(frame):
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
frame_gray = cv2.equalizeHist(frame_gray)
# -- Detect faces
faces = face_cascade.detectMultiScale(frame_gray)
for (x, y, w, h) in faces:
center = (x + w//2, y + h//2)
frame = cv2.ellipse(frame, center, (w//2, h//2),
0, 0, 360, (255, 0, 255), 4)
faceROI = frame_gray[y:y+h, x:x+w]
# -- In each face, detect eyes
eyes = eyes_cascade.detectMultiScale(faceROI)
for (x2, y2, w2, h2) in eyes:
eye_center = (x + x2 + w2//2, y + y2 + h2//2)
radius = int(round((w2 + h2)*0.25))
frame = cv2.circle(frame, eye_center, radius, (255, 0, 0), 4)
cv2.imshow('Capture - Face detection', frame)
return frame
face_cascade_name = "assets/haarcascade_frontalface_alt.xml"
eyes_cascade_name = "assets/haarcascade_eye_tree_eyeglasses.xml"
face_cascade = cv2.CascadeClassifier()
eyes_cascade = cv2.CascadeClassifier()
# -- 1. Load the cascades
if not face_cascade.load(face_cascade_name):
print('--(!)Error loading face cascade')
exit(0)
if not eyes_cascade.load(eyes_cascade_name):
print('--(!)Error loading eyes cascade')
exit(0)
# camera_device = 0
camera_device = "assets/1.mp4"
# -- 2. Read the video stream
cap = cv2.VideoCapture(camera_device)
ret, frame = cap.read()
h, w, _ = frame.shape
# 定义保存视频的编码格式、分辨率和帧率等信息
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.mp4', fourcc, 20.0, (w//2, h//2))
if not cap.isOpened:
print('--(!)Error opening video capture')
exit(0)
while True:
ret, frame = cap.read()
if frame is None:
print('--(!) No captured frame -- Break!')
break
framersz = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
frmaeres = detectAndDisplay(framersz)
# 保存视频
out.write(frmaeres)
if cv2.waitKey(10) == 27:
break
cap.release()
out.release()
cv2.destroyAllWindows()
【接口】
- CascadeClassifier
cv2.CascadeClassifier( ) -> <CascadeClassifier object>
cv2.CascadeClassifier( filename ) -> <CascadeClassifier object>
从文件加载一个级联分类器
- detectMultiScale
cv2.CascadeClassifier.detectMultiScale( image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]] ) -> objects
cv2.CascadeClassifier.detectMultiScale2( image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]] ) -> objects, numDetections
cv2.CascadeClassifier.detectMultiScale3( image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]] ) -> objects, rejectLevels, levelWeights
检测输入图像中不同大小的对象。检测到的对象作为矩形列表返回。
- image: 待检测目标的图像
- objects: 检测到的目标,vector或者list
- numDetections: 对应对象的检测数向量。一个物体的探测次数是相邻的正分类矩形的数量,这些矩形连接在一起形成了该物体。
- scaleFactor: 指定在每个图像尺度上图像大小减小多少。
- minNeighbors: 指定每个候选矩形应该有多少个相邻矩形框来保留它。
- flags: 该参数与cvHaarDetectObjects函数中对于旧级联的含义相同。但它不用于新的级联。
- minSize: 最小目标大小
- maxSize: 最大目标大小
【参考】
- OpenCV: Cascade Classifier
- Paul Viola and Michael J. Jones. Robust real-time face detection. International Journal of Computer Vision, 57(2):137–154, 2004. [263]
- Rainer Lienhart and Jochen Maydt. An extended set of haar-like features for rapid object detection. In Image Processing. 2002. Proceedings. 2002 International Conference on, volume 1, pages I–900. IEEE, 2002. [149]
- Video Lecture on Face Detection and Tracking
- An interesting interview regarding Face Detection by Adam Harvey
- OpenCV Face Detection: Visualized on Vimeo by Adam Harvey
- OpenCV: cv::CascadeClassifier Class Reference