本章将学习以下内容:
- 掌握Haar特征级联分类器的基本原理
- 将该方法扩展应用于眼睛检测等任务
技术原理
基于Haar特征的级联分类器目标检测方法, 由Paul Viola和Michael Jones在其2001年发表的论文《使用增强型简单特征级联的快速目标检测》中提出,是一种高效的机器学习检测技术。 该方法通过以下流程实现:首先利用大量正样本(包含目标图像)和负样本(不包含目标图像)训练级联分类器, 将训练好的分类器模型应用于新图像中的目标检测。
这里将以人脸检测为例展开说明。 该算法首先需要大量正样本(含人脸的图像)和负样本(无人脸的图像)来训练分类器,随后从中提取特征。 如图所示,这里采用Haar特征进行计算,这些特征类似于卷积核, 每个特征值都是通过计算黑色矩形区域像素总和减去白色矩形区域像素总和得到的单个数值。
算法会遍历每个特征核所有可能的尺寸和位置来计算大量特征(想象一下计算量有多大?仅24x24像素的窗口就能产生超过16万个特征)。 传统方法需要重复计算矩形区域内像素总和,而积分图的引入完美解决了这个问题,无论区域多大, 只需通过四个像素点的运算就能快速求出任意矩形区域的像素和,这使计算效率得到质的飞跃。
然而,在计算的海量特征中,绝大多数其实并无实际意义。 以下图为例:首行展示的两个有效特征中,第一个特征利用了眼睛区域通常比鼻梁和脸颊更暗的特性,第二个特征则捕捉了眼睛比鼻梁部位更暗的规律。 但若将这些检测窗口应用于脸颊等其他区域就完全失效。 那么如何从超过16万个特征中筛选出最优特征? 这正是通过Adaboost算法实现的,它能够智能地选择最具判别力的特征,并赋予不同权重,从而构建出高效的分类器。
具体实现时,算法会将所有特征逐一应用于训练图像集。 针对每个特征:首先自动确定最佳分类阈值来区分人脸/非人脸,但不可避免地会出现误判。 我们筛选错误率最低的特征,即最能准确区分人脸与非人脸的优质特征(实际过程更为复杂:初始时所有样本权重相同,每轮分类后增加误判样本权重,迭代执行特征选择与权重调整,直至达到预设的准确率、错误率或特征数量要求)。
最终的分类器是这些弱分类器的加权组合,之所以称为"弱",是因为单个分类器无法独立完成判断,但组合起来就形成了强分类器。 据论文所述,仅需200个特征即可达到95%的检测准确率,而最终系统使用了约6000个特征(从超过16万个特征中筛选出6000个,这效率提升非常显著)。
实际检测时,对图像的每个24x24窗口都需计算全部6000个特征来判断是否为人脸。 这听起来效率很低? 确实如此,但原作者提出了巧妙的级联结构解决方案,通过分阶段快速排除非人脸区域,大幅降低计算量。
由于图像中大部分区域都属于非人脸区域,更高效的策略是: 先通过简单判断快速排除明显不包含人脸的窗口,无需二次处理,从而集中资源分析可能的人脸区域。
为此,研究者提出了级联分类器(Cascade of Classifiers)的创新结构, 将6000个特征分组到多个检测阶段(通常前几个阶段仅含少量特征)。 检测时逐级筛选:窗口若未通过当前阶段则立即丢弃,通过所有阶段的窗口才被判定为人脸。 这种机制大幅提升了检测效率!
该论文提出的检测器包含6000多个特征,分为38个检测阶段, 前五个阶段分别设置1、10、25、25和50个特征(图示的两个特征正是通过Adaboost算法筛选出的最优特征)。 研究表明,每个子窗口平均只需计算6000多个特征中的10个即可完成判断。
以上便是 Viola-Jones 人脸检测算法的工作原理简介, 如需深入了解请参阅原始论文或扩展阅读中的参考文献。
OpenCV 中的 Haar 级联检测
OpenCV 同时提供了级联分类器的训练和检测工具。 如需针对汽车、飞机等特定目标训练自定义分类器, 可参考官方文档《级联分类器训练指南》进行操作。
本文主要介绍检测功能。OpenCV已内置多种预训练分类器(人脸、眼睛、微笑等),
对应的XML文件存储在opencv/data/haarcascades/
目录下。
下面演示如何使用 OpenCV 实现人脸和眼睛检测:
首先加载所需的XML分类器文件,将输入图像(或视频)转换为灰度格式进行处理。
import numpy as np
import cv2
face_cascade = cv2.CascadeClassifier('/data/cvdata/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('/data/cvdata/haarcascade_eye.xml')
img = cv2.imread('/data/cvdata/sachin.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
接下来在图像中进行人脸检测,检测成功则返回人脸区域的矩形坐标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)
%matplotlib inline
import matplotlib.pyplot as plt
plt.imshow(img)
<matplotlib.image.AxesImage at 0x7f12a4c84410>
扩展阅读
- 视频讲座:《人脸检测与追踪技术》 在线观看
- 专题访谈:Adam Harvey 解读 Viola-Jones 人脸检测算法