☰
HOG特征全解析:从方向梯度直方图到行人检测实战
2026/10/14 4:30:39 网站建设 项目流程

最近整理手头的视觉特征工程笔记,翻到了那篇做行人检测绕不过去的经典论文,标题就是 Histograms of Oriented Gradients for Human Detection。HOG特征在国内更常叫“方向梯度直方图”,也有叫“梯度方向直方图”的,简单理解就是把图像局部区域的梯度方向统计成一个多维特征向量。这篇论文笔记,我按自己的理解把HOG特征从原理、计算、训练到调参的完整链路拆一遍,重点回答一个问题:为什么靠统计梯度方向,就能把行人检测做得足够稳。内容适合刚接触计算机视觉、还没系统读过原始论文的读者,也适合做过检测但没深究特征细节的工程同学。读完这套思路,你基本可以把传统检测管线自己复现一遍,也能更清楚后来深度学习里的特征层到底在学什么。

1. HOG特征到底在解决什么问题

1.1 行人检测难在哪里

行人检测是目标检测里最典型的任务之一,难就难在目标的“模式”不是固定的。行人可以直立行走,也可以侧身弯腰;衣服颜色千变万化;背景可能是街道、树木、栏杆,也可能是一堆轮廓跟人很像的杂物。光照一变,同一段路的同一个行人,在图像里可能变成完全不同的像素值。

如果直接用原始像素做模板匹配,你会发现这个模板几乎没有泛化能力。稍微换一个背景,像素值整体就会偏移;稍微转动一点角度,模板相关性就大幅下降。那我们能不能用“边缘”来做人呢?单纯提取边缘也不够:边缘图对噪声敏感,而且同一目标的边缘密度会随光照、纹理变化很大。更根本的问题是,边缘检测只告诉我们哪些位置有变化,却没告诉我们变化的“性质”。同样是一条竖直的边,可能是人的侧面轮廓,也可能是一根电线杆,单靠边缘位置很难区分。

所以HOG特征要解决的,是“如何在允许外观细节变化的前提下,把局部形状信息表达得更稳定”。它的本质是在一个很小的图像区域里,统计梯度方向的分布。这个分布既包含边缘朝向信息,又对单个像素的显微变化不敏感,恰好适合描述行人这种“轮廓主导、纹理次要”的目标。

1.2 核心思路:把梯度方向变成统计量

用一个生活里的类比来解释:如果你隔着很远的距离看一个人,细节可能看不清,但你仍能通过他整体的轮廓和姿态认出那是人。HOG做的事情,就是先把这种轮廓切成很多小块,再把每个小块里所有边缘的方向和强度记录成一种频率分布,相当于给每个小块画了一张“方向统计图”。

具体来说,对图像每个像素,我们先计算它在水平方向和垂直方向的梯度分量,然后求出梯度的方向和幅值。方向告诉你这个边缘是水平的、竖直的还是斜着的,幅值告诉你这段边缘有多“强烈”。然后我们把一小块区域,比如 8×8 像素,按照方向角度划分成 9 个区间,每个像素把自己幅值大小“投”到对应方向的区间里。这样,每个小块就得到一个 9 维的分布统计,也就是方向直方图。

为什么这种统计会有效?因为行人的四肢、头部、躯干在局部都有相对稳定的边缘方向结构。虽然衣服颜色、背景纹理经常变化,但局部轮廓的方向分布通常不会彻底改变。比如人的腿不管裤子的纹理是什么,边缘方向大致还是竖直的;手臂横着伸出去的边缘方向大致是水平的。HOG正是用局部方向统计来捕获这种“低层形状共性”,后续再做归一化来抵抗光照变化。

这背后其实还有一层设计逻辑:HOG不刻意寻找关键点,而是把整张图划分成密集的网格,每个网格都要提取特征。相比只对少数兴趣点做描述,密集采样能保证不遗漏行人身上任何一段边缘,尤其是中低分辨率的行人检测中非常重要。

2. 特征计算全流程拆解

2.1 预处理与梯度计算

原论文的流程并不复杂。首先是预处理:一般先把彩色图像转成灰度图,再做一次Gamma校正,常见的做法是取像素值的平方根,目的是压缩光照和对比度的动态范围,让后续特征更稳定。

真正的核心从梯度开始。计算水平梯度时,用模板[-1, 0, 1]对图像做卷积;垂直梯度用模板[-1, 0, 1]的转置。选择这套模板的原因很直接:它是最简单的中心差分,对像素噪声有一些平滑作用,又不至于把局部边缘拉得太宽。用更大的滤波器,比如带平滑权重的算子,虽然能滤掉更多噪声,但也会把细小结构抹平,实验下来检测精度并不划算。

有了水平梯度 dx 和垂直梯度 dy,我们可以算出每个像素的梯度幅值sqrt(dx^2 + dy^2)和方向角度atan2(dy, dx)。这里有一个关键细节:论文使用的角度范围是 0 到 180 度,也就是“无符号方向”。这意味着把某个边缘旋转 180 度之后,它会被当成同一个方向。

为什么这么做?因为行人检测中一个很常见的问题是正反对比:同一段轮廓,背景从浅色变成深色时,梯度方向会翻转。我们不希望同一个轮廓仅仅因为前景背景颜色对调,就被计算成两种完全不同的特征。如果只关心边缘“本身的朝向”,而不是从亮到暗的方向,检测器会稳定很多。在训练和预测阶段,这个角度范围必须保持一致,否则特征分布会错乱。

2.2 Cell与Block:局部统计的尺度选择

梯度图算完之后,HOG会把它划分成固定大小的单元格,也就是Cell。经典参数是 8×8 像素为一个Cell。每个Cell内部有 64 个像素,每个像素根据自己梯度方向投到 9 个方向区间中,每个区间对应 20 度范围。幅值用于投票,所以边缘越强的像素对直方图贡献越大。

投票的时候不直接只投到最近的方向区间,而是采用双线性插值。举个例子:某个像素的方向对应的角度是 25 度,它落在 20 度和 40 度两个区间的中间位置,那么它的幅值会按距离比例分给这两个区间,离哪个近,哪个分到的就多一些。这样做可以避免量化误差带来的特征突变。如果没有这步插值,同一个边缘稍微旋转一两度,就可能在直方图里从第2个bin跳到第3个bin,造成明显的噪声。

Block则是把若干个Cell组合成一个更大的统计单元,通常取 2×2 个Cell,也就是 16×16 像素。最关键的差异在于Block之间是重叠的:在一个 64×128 的检测窗口里,Block以8像素为步长滑动,所以同一个Cell会被多个Block覆盖。这种重叠设计增加了特征的冗余度,但换来了更强的鲁棒性——局部边缘即使出现轻微位移,多个Block也能有相近的描述表现。

要知道,如果不做Block级归一化,只单独统计每个Cell,那么这个Cell的直方图值就很容易受局部对比度影响。高对比度区域会让整个Cell里的幅值整体飙升,低对比度区域则整体缩水;这会直接破坏分类器对相似形状的判断。把多个Cell组合成Block再归一化,等于在一个更大范围里做对比度均衡,更具实际意义。

2.3 归一化:对抗光照变化的秘密武器

原论文对比了多种归一化方式,包括L1范数、L1平方根、L2范数,以及一个叫L2-Hys的方法。实验里表现最好的是L2-Hys,也就是先做L2范数归一化,把向量中超过0.2的分量裁剪掉,然后再做一次L2归一化。

L2范数归一化的公式很直观:对一个Block里的36维向量,计算所有分量的平方和,再开方,得到的就是这个向量的范数;然后把每个分量除以这个范数,再加一个极小的常数防止除零。这样处理后,Block特征向量的总长度会被压到单位长度附近,光照增强或减弱带来的整体幅值变化就不再直接反映到特征值上。

为什么要裁剪到0.2?因为如果不做裁剪,某个特别强的边缘方向可能占据向量太大比例,归一化后其他较弱但有判别力的方向会被压得几乎无法表达。裁剪就像是在开一个多人的讨论会时给嗓门最大的人限个音量,让他能表达观点,但不能淹没所有人的意见。做完裁剪之后还要再归一化一次,是为了让整个向量的尺度不会因为裁剪而收缩太多。

值得注意的是,HOG的归一化是在Block层面,而不是整张图层面。整图归一化对局部阴影变化无能为力,因为阴影往往只影响图像的一块区域。只有Block级归一化才能保证一个Block内部相对稳定,所以这个设计不是拍脑袋想出来的,而是通过大量对比实验验证过的选择。

2.4 特征维度的推导和取舍

以最常用的 64×128 行人检测窗口为例,我们来算一下最终特征向量到底有多少维。窗口里每 8×8 像素一个Cell,那么水平方向有 8 个Cell,垂直方向有 16 个Cell。每个Block是 2×2 个Cell,Block滑动步长是8像素,所以水平方向上Block数量是8 - 1 = 7个,垂直方向是16 - 1 = 15个。整个窗口共有7 × 15 = 105个Block。

每个Block包含4个Cell,每个Cell有9维方向直方图,所以一个Block对应36维特征。整窗口特征维度就是105 × 36 = 3780维。这个数字你会在很多工程实现里反复看到,也经常作为默认参数出现。

3780维在今天看来并不算高,但在传统机器学习时代,这个维度已经足够喂给线性分类器了。重叠Block会让一个Cell的特征被重复计算多次,看起来好像是浪费,实际上这种冗余对位置微小偏移很友好。检测窗口里的行人,手臂稍微晃动几个像素,单个Cell的输出会变化,但好几个Block的统计结果可能变化很小,整体特征依旧稳定。

不过工程上要明白,冗余也意味着内存和计算开销成倍增加。如果算力非常紧张,可以选择缩小Block重叠率,比如加大滑动步长,但代价是鲁棒性下降。这就是HOG参数调整中最重要的平衡点:空间分辨率和特征稳定性的平衡。几乎任何经典参数都不是越多越好,而是要围绕检测精度和运行速度做取舍。

3. 检测器构造与训练流程

3.1 滑窗扫描与多尺度检测

HOG特征本身不与检测窗口绑定,但要训练一个检测器,我们需要把分类器应用到整张图像上。经典做法是滑窗检测:先把原始图像不断缩小,构建一个图像金字塔,每一层图像都用固定的 64×128 检测窗口从左到右、从上到下扫描,每滑动一步提取一次特征,送进分类器判断是否有人。

为什么需要图像金字塔?因为行人在图像中的尺度变化很大。离镜头近的行人可能占据很大面积,远处的行人可能只有几十像素高。通过不同倍率的图像缩放,可以让同一个检测窗口覆盖不同物理尺度的人,实现多尺度匹配。一般金字塔缩放系数取1.05左右,也就是每层缩小5%,这个粒度能兼顾召回率和计算速度。窗口滑动步长通常取8像素,步长越小检测框越密,漏检越少,但计算量会暴涨。

这里有一个很基本的认知:HOG检测器缺乏全局上下文,它看到的只是一个固定大小的局部窗口。所以它很容易把路灯、树干这类竖直且与人腿轮廓相似的目标识别成人,尤其当背景中包含大量竖直线时。这是手工特征检测器共同的痛点,解决方式之一就是后处理非极大值抑制(NMS),把多个重叠的候选框合并成一个最终框。

NMS的直觉很简单:如果一个目标被多个相邻窗口同时检测到,那么这些窗口必然重叠很大,我们只保留得分最高的那个窗口,并且把所有与它重叠比例超过某个阈值的窗口删除。这个操作能大幅减少重复框,但不能完全解决误检。工程上真正有用的手段还是训练时的难例挖掘,这直接关系到分类器对“看起来像人但不是人”的样本的处理能力。

3.2 线性SVM分类与难例挖掘

在得到3780维HOG特征后,论文使用的分类器是线性SVM。为什么不用更复杂的核函数?因为检测时要在图像金字塔的每个位置提取特征并做分类,窗口数量非常高,核函数会显著拖慢运算。线性SVM本质上是计算一个特征向量与权重向量的点积,加上偏置,输出类别得分。在现代硬件上,这个操作可以优化得非常快。

线性SVM还有一个特别有意思的副产品:训练完成后,权重向量的大小与特征维度相同,所以可以把权重向量按HOG特征的结构重新排列,生成一张“模板图”。你会清楚地看到,分类器在数学上学习到的其实就是一个人形轮廓模板,哪些方向的特征更关键,权重就会更高。这种可解释性在深度学习分类器里很难直接获得。

训练HOG检测器时,一个极大的坑是负样本空间。负样本是指所有不包含行人的图像窗口,理论上几乎无限多:路面、墙面、栏杆、草丛都可能成为负样本。如果只随机抽取少量负样本训练,分类器永远不会接触到那些真正难分的背景。所以论文采用了一个非常关键的策略:难例挖掘,也叫自举法。

难例挖掘是一个迭代过程。先用随机负样本训练一个初版分类器,然后把该分类器放到大量负样本图上扫描,把所有被误判为人的窗口挑出来。这些窗口通常在人眼看来并不像人,但在特征层面已经非常接近正样本,所以被称为“难例”。把它们作为负样本加入训练集,重新训练分类器,再重复扫描。两三轮之后,误检率会显著下降。这个思想后来在很多现代检测器里也以“Hard Negative Mining”的形式保留了下来。

训练数据的正样本也需要仔细准备。经典的HOG行人检测通常使用包含各种姿态、光照、遮挡程度的行人图像,从更大的图像中裁剪出固定尺寸的窗口,窗口内部尽量包含完整行人,但也不必像素级对齐。对于负样本,采样时要注意覆盖各种场景,避免只选某一类简单背景,否则分类器会在实际场景中表现偏差。

4. 参数实验与工程调优心得

4.1 原论文里的关键参数结论

HOG参数看着多,其实遵循几条很明确的规律。原论文通过大量消融实验,总结出几个核心结论。我把最常被引用的实验结果整理成一个速查表,方便你复现和调参。

参数项推荐选择说明
颜色空间灰度即可RGB与灰度差异不大,彩色没有带来本质提升
梯度模板[-1, 0, 1]中心差分简单稳定,更大模板没有显著优势
方向区间数99个bin在精度和维度间最平衡
方向范围0到180度无符号对正反对比不敏感,行人检测中更稳
Cell大小8×8像素对64×128窗口来说是合理分辨率
Block大小2×2个Cell,即16×16兼顾局部归一化与鲁棒性
归一化方式L2-Hys先L2归一化、裁剪0.2、再L2归一化
检测窗口64×128经典行人比例,长宽比约1:2

这些结论看起来平淡无奇,但每一项都是通过改变单个变量、保持其他变量不变得到的。比如把方向区间数从9改成15,特征维度会上升,但精度没有明显提升;反而因为维度变高,模型更容易过拟合噪声。把Cell从8×8改成4×4,空间分辨率更高,但每个Cell统计样本太少,直方图波动大;改成16×16则细节丢失太多,精度反而下降。8×8是一个经验上很均衡的选择。

工程里不要盲目改这些参数。我见过有人为了压缩特征维度把方向bin从9改成6,结果误检率明显上升。压缩特征最安全的方式不是改bin数量,而是降低Block重叠率或者缩小检测窗口尺寸。当然,这也会带来副作用,所以改之前一定要在本地验证集上测一遍。

4.2 工程中的常见配置与落地细节

实际做项目时,大部分视觉库都内置了默认的HOG提取器,常见配置基本都是:检测窗口64×128、块尺寸16×16、块步长8×8、Cell尺寸8×8、方向bin数量9。这个配置和论文里的基准基本相同,可以直接作为起点。如果你的业务目标是行人检测,先跑通这个默认配置,再根据错误类型慢慢调。

工程上最容易忽略的一个细节是“训练和预测时的特征一致性”。这里的特征一致性不只是归一化方法一致,还包括图像缩放顺序、颜色空间转换顺序、边界填充方式。比如你训练时先用的是原尺寸图提取HOG,预测时却先用双线性插值缩小图像再提取,即使算法完全一样,特征分布也会产生偏移。对于线性SVM这种强分类器,一点点分布偏移都可能放大成大量误检。

再一个要注意的是正负样本比例。行人检测中正样本数量一般远少于负样本,训练时不是把全图所有窗口都拿来训练,而是每个大图抽取固定数量的正负窗口。负样本天然数量很多,但也不要让负样本数量过分碾压正样本。在难例挖掘的每一轮中,加入的难例数量要控制,比如每轮只加入得分最高的一批难例,否则训练集会迅速被难例占据,导致分类器偏置。

内存和显存方面,如果把整张图的HOG特征都提前提取出来再滑窗分类,会比较耗内存。更稳的做法是实时滑动提取:每次只计算当前窗口的Block特征,送进分类器得到得分,再滑动到下一个窗口。因为Block之间有重叠,实时计算需要重复一些卷积运算,但对于计算力普通的嵌入式设备来说,内存占用反而更可控。

5. 踩坑记录与排查速查

5.1 典型问题速查表

做HOG行人检测的过程中,我踩过很多坑,也见过不少同学在同样的地方反复折腾。下面这张表把最典型的现象、原因和排查方向整理了出来,希望能帮新手省掉一些试错时间。

现象常见原因排查思路
漏检很多分类器阈值太高、尺度步长太大、窗口滑动步长太大先降低分类阈值看召回率;再缩小金字塔尺度系数
误检很多负样本难例挖掘不充分、阈值过低增加难例挖掘轮数;提高阈值;补充和实际场景相似的负样本
检测框抖动图像金字塔尺度太密,多个窗口对应一个目标调整NMS重叠阈值,选择重叠度更高的框合并
速度明显慢滑动步长太小、金字塔层数太多把窗口步长从4改到8;尺度系数从1.02改到1.05
训练Loss和测试表现不一致特征预处理不一致、样本裁剪尺寸不一致检查训练和预测的数据流是否完全走同一套代码
近处行人漏检行人高度超过检测窗口范围,窗口内部不完整对图像做更高层金字塔展开,或加入近景专用检测流程

排查漏检时,我建议把分类器输出得分可视化,看是“低于阈值”还是“分类器根本不认为那有行人”。这两个问题的处理方法完全不同。前者更多是阈值和尺度的问题,后者则可能是训练正样本不够多样,或者特征质量本身有问题。

5.2 几个容易被忽略的细节

第一个容易踩的坑是方向范围混用。HOG的经典论文用的是0到180度的无符号方向,但很多做边缘检测的人习惯了0到360度的有符号方向。如果你在提取特征时用0到360,训练分类器时又用了0到180,特征维度的语义完全不同,模型几乎不可能正常工作。无论用哪种,都要确保整个特征提取管线、可视化工具和分类器输入都保持一致。

第二个细节是窗口边界填充。当一个Block滑到检测窗口边缘时,它可能需要一部分超出窗口的图像内容。标准的做法是对窗口边界做像素补齐,常用的是补0,但补0会让边缘Block包含大量不真实的梯度。更合理的做法是让Block在窗口内部可以继续滑到刚好完全包含最后一块Cell的位置,窗口边缘的Cell允许不完整或被裁剪,这在特征维度计算时要同步调整。如果你直接从开源库拿默认实现,大概率它已经处理好了,但如果你是自己手写特征提取,这个位置特别容易出bug。

第三个细节是图像缩放和灰度化的顺序。建议固定一个流程,比如“先缩放后灰度化”,并且训练和预测保持一致。原因在于彩色图像缩放时会涉及插值,灰度化后缩放则是对单通道做插值,两者产生的边缘宽度、梯度幅值会略有不同。这种微小的不一致在大量滑窗累加之后,可能直接导致性能下降。

第四个细节是正样本裁剪时的比例问题。有的同学会直接把原图里的行人抠出来拉伸到64×128,结果人体比例被拉变形。HOG统计的是局部方向分布,对轻微形变有一定容忍度,但过度拉抻会破坏行人轮廓。正确做法是在行人周围留出一定边距,然后按检测窗口的长宽比裁剪,而不是强行把人的边界贴满整个窗口。

6. 重读之后的一点个人体会

6.1 把HOG当作固定规则的手工卷积层

我最早接触HOG时,觉得它不过是一堆直方图拼接,没什么了不起。后来给模型做特征可视化,才意识到HOG的内在结构与现代卷积神经网络其实非常相似:先计算梯度,相当于第一层卷积提取边缘;再看局部方向的统计,相当于在不同方向上做特征聚合;再做Block归一化,类似卷积层的局部响应归一化。如果你把HOG的每个计算步骤当成一组固定权重的手工卷积,会很容易理解为什么CNN天然适合目标检测。

这也是为什么我建议新手不要一上来就抱着大模型跑Demo,先花几天把HOG的整个流程亲手实现一遍,效果可能不惊艳,但对特征提取、分类器训练、滑窗检测和后处理的理解会扎实很多。这批知识放到今天依然有效,因为在工程问题里,很多嵌入式设备没有充裕算力跑大型模型,轻量级特征加简单分类器可能反而是最稳的方案。

6.2 对现代检测开发的三点启发

第一,数据质量永远比模型复杂程度更重要。HOG分类器表现不好的时候,我第一反应通常不是调参,而是去看正样本是不是太单一、负样本是不是太少、难例挖掘是不是没做够。换成今天的深度学习任务,这个排查顺序也完全适用。数据集覆盖度不够,换更大网络也只是让模型继续记住错误模式。

第二,可解释性是传统特征的隐藏优势。线性SVM的权重可以可视化成人形模板,能让我直观判断分类器学的方向对不对。现在很多检测模型精度高,但出了问题很难定位。如果你在做一个对稳健性要求高的业务,保留一些传统特征作为辅助分析工具,往往比直接热力图解释更帮得上忙。

第三,算力受限时,传统特征依然有不小的存在价值。我在某个轻量级边缘检测场景里,试过用HOG加线性SVM做遮挡目标的预筛,再把候选区域交给复杂模型做精分类。在这个所谓“老旧”的组合下,预筛阶段的计算量只有完整检测的十分之一,误检也被限制在较低水平。所以我更倾向于把HOG当成一套成熟、透明的特征提取思路,而不是一个过时的冷知识。

重读这篇论文,最大的感受是:好的特征设计永远是在“表达力”和“稳定性”之间找平衡。HOG之所以能成为经典,不是因为它用了多么复杂的数学,而是因为它把对行人检测真正重要的局部轮廓信息提取了出来,并对光照、位移、背景这些干扰因素做了系统性的工程处理。这套解决思路,放到今天依然值得反复品味。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询