4、目标检测基础(上):滑动窗口与图像金字塔、Haar特征与Adaboost分类器(人脸检测)、HOG特征与SVM(行人检测)
各位同学,今天我们来聊聊目标检测的“老底子”。
说实话,现在大家一提到目标检测,脑子里蹦出来的都是YOLO、Faster R-CNN这些深度学习模型。但我想说,如果你没搞懂滑动窗口、图像金字塔、Haar特征和HOG特征这些经典方法,你很难真正理解现代检测器为什么那么设计。
我当年刚入行时,第一个项目就是做人脸检测。那时候哪有什么GPU,全靠CPU硬扛。嗯,今天我就把当年踩过的坑和积累的经验,一次性讲清楚。
核心观点:传统目标检测 = 特征工程 + 分类器。特征决定了你能检测什么,分类器决定了你检测得准不准。
4.1 滑动窗口与图像金字塔:暴力但有效的搜索策略
先问大家一个问题:一张图片里,目标可能出现在任何位置,也可能有大有小。你怎么找到它?
最简单的思路就是——穷举。这就是滑动窗口的核心思想。
4.1.1 滑动窗口是什么?
说白了,就是一个固定大小的矩形框,在图像上从左到右、从上到下地滑动。每滑动一步,就把框内的图像区域提取出来,交给分类器判断:这是目标还是背景?
- 窗口大小:通常固定,比如64×128(行人检测常用)或24×24(人脸检测常用)
- 步长:每次移动的像素数。步长越小,检测越精细,但计算量越大
- 缩放比例:为了检测不同大小的目标,需要缩放图像本身——这就是图像金字塔
我的经验:步长一般设为窗口宽高的10%~20%。比如64宽的窗口,步长8~12像素比较合理。步长太小,你会等到天荒地老;步长太大,目标容易漏检。
4.1.2 图像金字塔:解决尺度问题
目标有大有小,怎么办?
图像金字塔就是答案。你把原图不断缩小(通常每次缩小1.1~1.5倍),形成一系列不同尺度的图像。然后在每一层上跑滑动窗口。
举个例子:
- 原图1000×800,窗口64×128
- 缩小到800×640,窗口还是64×128——相当于检测了更大的目标
- 再缩小到640×512——检测的目标更大
- 以此类推,直到图像比窗口还小
注意:金字塔层数越多,检测越全面,但计算量呈指数增长。我建议一般建3~5层就够了。曾经有个项目,我建了10层金字塔,结果一帧图像处理了3秒……直接被客户骂惨了。
4.1.3 滑动窗口的致命缺陷
你想想看,一张1000×800的图像,步长8像素,金字塔5层,总共要检测多少个窗口?
答案是:几十万甚至上百万个。每个窗口都要提取特征、跑分类器——这速度,你能忍?
所以,滑动窗口虽然思路简单,但实际应用中必须配合高效的特征和分类器。这就是接下来要讲的Haar和HOG的用武之地。
4.2 Haar特征与Adaboost分类器:人脸检测的经典组合
2001年,Viola和Jones提出了一个里程碑式的工作——实时人脸检测。核心就是Haar特征 + Adaboost + 级联结构。
我至今记得第一次在笔记本上跑通这个算法时,那种“哇,真的能实时检测人脸”的兴奋感。虽然现在看它已经过时了,但它的设计思想至今影响着很多现代算法。
4.2.1 Haar特征:像积木一样的矩形特征
Haar特征其实就是一些黑白相间的矩形模板。计算时,用白色区域像素和减去黑色区域像素和,得到一个数值。
常见的Haar特征有:
- 边缘特征:2个矩形,检测明暗变化(比如眼睛比脸颊暗)
- 线特征:3个矩形,检测线条(比如鼻梁)
- 中心特征:4个矩形,检测斑点(比如眼睛区域)
关键点:Haar特征为什么快?因为它用了积分图(Integral Image)。积分图可以在常数时间内计算出任意矩形区域的像素和。没有积分图,Haar特征根本跑不起来。
4.2.2 Adaboost:从弱分类器到强分类器
单个Haar特征分类能力很弱,可能只有51%的准确率——比瞎猜好一点点。
Adaboost的思路是:把很多个这样的弱分类器组合起来,形成一个强分类器。怎么组合?
- 先训练一个弱分类器,找出它分错的样本
- 加大这些错分样本的权重
- 再训练下一个弱分类器,让它更关注之前分错的样本
- 重复,直到达到目标精度
最终,每个弱分类器有一个权重,投票决定结果。
我的经验:Adaboost训练时,弱分类器的数量不是越多越好。我曾经试过用2000个弱分类器,结果过拟合严重,检测率反而下降了。一般100~200个就够用了。
4.2.3 级联结构:让检测飞起来
Viola-Jones最巧妙的设计是级联(Cascade)。
你想想看,一张图像中,99%的区域都是背景。如果每个窗口都跑200个弱分类器,那太浪费了。
级联的思路是:
- 前几层用少量、简单的Haar特征,快速排除明显不是人脸的区域
- 只有通过前几层的候选区域,才进入更复杂的后续层
- 最终只有极少数区域能通过所有层,被判定为人脸
这样,大部分窗口在前几层就被淘汰了,计算量大幅下降。
注意:级联的每一层都要设置一个“通过率”。如果通过率太高,很多非人脸会进入下一层,增加计算量;如果通过率太低,真正的人脸可能被误杀。我一般设置每层通过率99.5%以上,保证不漏检。
4.3 HOG特征与SVM:行人检测的黄金搭档
2005年,Dalal和Triggs提出了HOG(方向梯度直方图)特征,专门用于行人检测。这个算法在深度学习兴起之前,一直是行人检测的标杆。
我记得2012年做安防项目时,用的就是HOG+SVM。虽然现在有更好的方法,但HOG的思想——用梯度方向描述局部形状——至今仍是很多特征提取方法的基础。
4.3.1 HOG特征:用梯度说话
HOG的核心思想是:物体的局部外观和形状,可以通过局部梯度方向的分布来描述。
具体步骤:
- 计算梯度:对每个像素,计算水平方向和垂直方向的梯度
- 划分细胞单元:将图像分成8×8的小块(cell)
- 统计梯度方向直方图:每个cell内,统计9个方向的梯度幅值
- 块归一化:将2×2个cell组成一个block,进行对比度归一化
- 串联特征:所有block的特征串联起来,形成最终的HOG特征向量
举个例子,一个64×128的行人图像:
- cell大小:8×8,所以有8×16=128个cell
- block大小:2×2 cell,步长8像素,所以有7×15=105个block
- 每个block有4个cell × 9个方向 = 36维特征
- 最终特征维度:105 × 36 = 3780维
为什么HOG适合行人?行人的轮廓(头、肩膀、腿)有明显的边缘和梯度变化。HOG正好能捕捉这些信息。相比之下,Haar特征更适合人脸这种有固定纹理模式的物体。
4.3.2 SVM:找到最佳分界线
特征提取完了,接下来就是分类。HOG的搭档是SVM(支持向量机)。
SVM的目标是找到一个超平面,把正样本(行人)和负样本(背景)分开,并且让两类样本到超平面的距离(间隔)最大化。
为什么用SVM?
- HOG特征维度高(3780维),SVM在高维空间表现好
- SVM的泛化能力强,不容易过拟合
- SVM有成熟的实现(比如OpenCV的cv2.HOGDescriptor)
我的经验:训练SVM时,负样本的选择非常关键。我曾经只用了一些简单的背景图片做负样本,结果模型把树影、路灯都检测成了行人。后来我用了“难例挖掘”——把误检的样本加入训练集重新训练,效果好了很多。
4.3.3 HOG vs Haar:怎么选?
| 对比项 | Haar特征 | HOG特征 |
|---|---|---|
| 适用场景 | 人脸检测(纹理固定) | 行人检测(轮廓明显) |
| 特征维度 | 低(几百维) | 高(几千维) |
| 计算速度 | 快(积分图加速) | 较慢(梯度计算耗时) |
| 分类器 | Adaboost + 级联 | SVM |
| 光照鲁棒性 | 一般 | 较好(归一化后) |
4.4 实战经验总结
说了这么多,最后给大家几个实用建议:
- 滑动窗口的步长和金字塔层数要权衡。我一般先设大步长(比如16像素)快速测试,再根据结果调小步长
- Haar特征适合人脸,HOG适合行人。不要混用。我曾经试过用HOG检测人脸,效果很差——人脸太光滑,梯度信息不够丰富
- 训练数据要多样化。正样本要包含不同角度、光照、遮挡的情况。负样本要覆盖各种背景
- 难例挖掘是提升精度的关键。先训练一个初版模型,在验证集上跑,把误检的样本加入训练集,再训练一次
避坑指南:我曾经在一个项目中,直接用OpenCV自带的HOG行人检测器,结果在雨天场景下漏检率高达40%。后来发现,默认的检测器是在晴天数据集上训练的。解决办法是:收集雨天数据,重新训练SVM。所以,别迷信预训练模型,数据才是王道。
好了,今天的内容就到这里。滑动窗口、图像金字塔、Haar特征、HOG特征——这些传统方法虽然老了,但它们是理解现代目标检测的基石。下一节我们会讲更深入的内容,到时候你会发现,很多现代方法都是在这些老方法的基础上改进的。
可可精品资料库