Hog(Histograms of Oriented Gradients,又称方向梯度直方图)概念出自于一篇2005年CVPR论文(Author:Navneet Dalal、Bill Triggs),内容为:使用HOG+SVM做行人检测
论文链接:Histogram of oriented gradients for human detection
由于它的巨大成功和可靠性,HOG 已成为计算机视觉中应用最广泛的目标检测算法之一。
Hog流程
Hog特征原理
1.梯度方向和模的计算
假设有这么一幅图片(gray格式),取出64*128大小的部分,通过选择其中的一个像素点及其周围的3x3区域,计算梯度和大小,如下:
A是像素点,周围的灰度值大小依次为32、20、30、64
所以,可以得到梯度长度/梯度方向
这里需要注意的是:梯度方向和图像边缘方向是互相正交的
2.计算cell的梯度直方图
经过上面的计算,我们知道每一个像素点都有两个值:梯度长度/梯度方向
我们将图像分为若干个的8x8的小单元(这里我们简称为cell),并计算每个cell的梯度直方图。
为什么我们要把图像分成若干个8x8的小单元?
这是因为对于一整张梯度图,其中的有效特征是非常稀疏的,不但运算量大,而且效果可能还不好。于是我们就使用特征描述符来表示一个更紧凑的特征。一个8x8的小单元就包含了8x8x2 = 128个值,8x8是像素的个数,2是因为每个像素都包括梯度的大小和方向。在HOG中,每个8x8的cell的梯度直方图本质是一个由9个数值组成的向量, 对应于0、20、40、60…160的梯度方向(角度)。那么原本cell中8x8x2 = 128个值就由长度为9的向量来表示,用这种梯度直方图的表示方法,大大降低了计算量,同时又对光照等环境变化更加地鲁棒。
我们来看一下图片中的一个cell中的梯度
- 我们先按第一步,计算出每一个像素点的梯度方向(G.Direction)和大小(G.Magnitude)
- 从图片中取出8x8的cell(中间那张图),箭头方向表示梯度方向,箭头长度表示梯度大小。在这里,0°和180°的方向不考虑相反,视作同一方向
- 现在我们来计算cell中像素的梯度直方图,先将角度范围分成9份,也就是9 bins,每20°为一个单元,也就是这些像素可以根据角度分为9组。将每一份中所有像素对应的梯度值进行累加,可以得到9个数值。直方图就是由这9个数值组成的数组,对应于角度0、20、40、60… 160。
比如上面方向图中蓝圈包围的像素,角度为80度,这个像素对应的幅值为2,所以在直方图80度对应的bin加上2。红圈包围的像素,角度为10度,介于0度和20度之间,其幅值为4,那么这个梯度值就被按比例分给0度和20度对应的bin,也就是各加上2。
还有一个细节需要注意,如果某个像素的梯度角度大于20°,也就是在20°到40°之间,那么把这个像素对应的梯度值按比例分给20°和40°对应的bin
例如:某像素的梯度幅值为13.6,方向为36,36°两侧的角度bin分别为20°和40°,那么就按一定加权比例分别在20°和40°对应的bin加上梯度值,加权公式为:
20°对应的bin:((40-36)/20) * 13.6,分母的20表示20等份,而不是20°;
40°对应的bin:((36-20)/20) * 13.6,分母的20表示20等份,而不是20°;
如果某个像素的梯度角度大于160°,也就是在160°到180°之间,那么把这个像素对应的梯度值按比例分给0°和160°对应的bin。如左下图绿色圆圈中的角度为165°,幅值为85,则按照同样的加权方式将85分别加到0°和160°对应的bin中
这样,我们就得到了一个cell中的梯度直方图
x轴是角度的范围,而y轴是对应角度的加权幅度
3.block归一化
HOG特征将8×8的一个局部区域作为一个cell,再以2×2个cell作为一组,称为一个block,也就是说一个block表示16x16的区域
归一化作用
图像的梯度对整体光照非常敏感,比如通过将所有像素值除以2来使图像变暗,那么梯度幅值将减小一半,因此直方图中的值也将减小一半。我们可以通过归一化使得梯度直方图不会受到光照变化的影响
例:长度为3的向量是如何归一化的?
假设我们有一个向量 [128,64,32],向量的长度为146.64,这叫做向量的L2范数。将这个向量的每个元素除以146.64就得到了归一化向量 [0.87, 0.43, 0.22]。
假设环境光照增强,那么会有一个新向量,是第一个向量的2倍 [128x2, 64x2, 32x2],也就是 [256, 128, 64],我们将这个向量进行归一化,你可以看到归一化后的结果与第一个向量归一化后的结果相同。所以,对向量进行归一化可以消除整体光照的影响
一个block有4个cell,一个cell有4个数值,所以一个block有36个数值
而block每一次只滑动一个cell的步长
例如:一幅图像划分成cell的个数为8x16,就是横向有8个cell,纵向有16个cell。每个block有2x2个cell的话,那么cell的个数为:(16-1)x(8-1)=105。即有7个水平block和15个竖直block。
再将这105个block合并,就得到了整个图像的特征描述符,长度为 105×36=3780
这样,我们就大大压缩了图像的数据,仅保留这105个数据
这个特征描述符是一个一维的向量,长度为3780
理解直方图
下面分析一下上图的几个静态截图,看看所选单元格的直方图如何表示边缘?我们选取不同地方的单元格,并观察其直方图和Hog特征的缩放窗口
1.三角形内部
在这种情况下,由于三角形几乎都是相同的颜色,因此在所选单元格中不应存在任何主要梯度。 正如我们在缩放窗口和直方图中可以清楚地看到的那样,情况确实如此。 我们有很多渐变但没有一个明显地支配着另一个。
2.靠近水平边缘
边缘是图像中强度突然变化的区域。 在这些情况下某个特定方向上具有高强度的梯度。 这正是我们在所选单元格的相应直方图和缩放窗口中看到的内容。 在缩放窗口中可以看到主导梯度向上,几乎在90°,因为这是强度急剧变化的方向。 因此,我们应该期望直方图中的90°区域比其他区域更强。 这实际上就是我们所看到的。
3.靠近垂直边缘
在这种情况下,我们期望单元中的主导梯度是水平的,接近 180° ,因为这是强度急剧变化的方向。 因此,我们应该期望直方图中的 170° 区域比其他区域梯度影响更大。 这就是我们在直方图中看到的,但我们也看到单元中还有另一个主导梯度,即 10 度 bin 中的梯度。这是因为 HOG 算法使用无符号梯度,这意味着 0° 和 180° 被认为是相同的。 因此,当创建直方图时,160 度和 180 度之间的角度与 10 °(bin)和 170 °(bin)成比例(上文已提过)。 这导致在垂直边缘附近的单元中存在两个主要梯度而不是仅一个
4.靠近对角线边缘
为了理解我们所看到的,让我们首先记住梯度由 x 部分(分量)和 y 部分(分量)组成,就像向量一样。因此,梯度的最终方向将由其分量的向量和给出。因此,在垂直边缘上,渐变是水平的,因为它们只有 x 分量,如上图所示。在水平边缘上,渐变是垂直的,因为它们只有 y 分量,正如上上图所示。因此,在对角线边缘,梯度也将是对角线,因为 * x * 和 * y * 分量都是非零的。由于图像中的对角线边缘接近 45 °,我们应该期望在 50 °(bin)中看到显著的梯度方向。而这实际上就是我们在直方图中看到的,但是,如上图所示,我们看到有两个主导梯度而不是一个。其原因在于,当创建直方图时,靠近区间边界的角度与相邻区间成比例地起作用。例如,角度为 40 °的梯度位于 30 °和 50 °的中间。因此,梯度的大小均匀地分成30°和50°的bin。这导致在对角线边缘附近的单元中存在两个主要梯度而不是仅一个
Hog可视化
导入scikit-image库
from skimage import feature,exposure import cv2 image = cv2.imread('D:\opencv\CV\Haar\girl.jpg') image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) fd, hog_image = feature.hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(2, 2), visualize=True) hog_image_rescaled = exposure.rescale_intensity(hog_image,in_range=(0,10)) cv2.imshow('img',image) cv2.imshow('hog',hog_image_rescaled) cv2.waitKey() cv2.destroyAllWindows()
注意事项
注意一定要把图像转为灰度图,否则会出现报错
ValueError: Only images with two spatial dimensions are supported. If using with color/multichannel images, specify channel_axis.
运行python代码报错“TypeError: hog() got an unexpected keyword argument ‘visualise’”
将fd, hog_image = hog(image, orientations=8, pixels_per_cell=(12, 12),
cells_per_block=(1, 1), visualise=True) 中的 visualise改成visualize便可正常,即(将字母s改成z)—版本问题
feature.hog()函数
feature.hog()函数
输入参数:
1.orientations=9:方向
2.pixels_per_cell=(8, 8):每一个cell包含8x8个像素
3.cells_per_block:每一个block包含2x2个cell
4.visualize=True:必需,否则报错
rescale_intensity是skimage.exposure.exposure 模块中的函数,在对图像进行拉伸或者伸缩强度水平后返回修改后的图像
参考资料
一文讲解方向梯度直方图(hog) - 知乎 (zhihu.com)
计算机视觉基础:HOG特征描述算⼦ - 知乎 (zhihu.com)
HOG 特征提取算法(实践篇) - Alex777 - 博客园 (cnblogs.com)