图像方向梯度直方图 HOG 特征

本文最后更新于:2022年10月10日 中午

HOG全称histogram of oriented gradients.如果翻译成中文就是方向梯度直方图。它可以用来表示图像的物体特征,因此能够检测出这类物体。。

简介

  • 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。

  • 特征在对象识别与模式匹配中是一种常见的特征提取算法,是基于本地像素块进行特征直方图提取的一种算法,对象局部的变形与光照影响有很好的稳定性。

  • 它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。

  • 论文链接:Histogram of oriented gradients for human detection

主要思想

  • HOG 的本质是 梯度的统计信息
  • 在一副图像中,局部目标的表象和形状(appearance and shape)能够被梯度或边缘的方向密度分布很好地描述。

核心步骤

HOG特征提取算法的过程主要分为四大步骤:

  1. 输入图像,对图像进行颜色空间的归一化;
  2. 计算图像的梯度;
  3. 为每个cell单元构建梯度方向直方图
  4. 把cell单元组合成block,块内归一化梯度直方图。

优点

  • 由于HOG是在图像的局部方格单元上操作,所以它对图像几何的和光学的形变都能保持很好的不变性,这两种形变只会出现在更大的空间领域上。
  • 在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。

梯度方向

  • 梯度方向指的是图像梯度的方向或朝向。Hog就是一张有关图像梯度方向的直方图。首先HOG会接受一张图像,然后计算每个像素的梯度幅度和方向

  • 获取梯度方向后 HOG 根据特征角度统计直方图

HOG 特征提取

图像预处理

Gamma矫正

可选操作

  • 为了减少光照因素的影响,首先需要将整个图像进行归一化。由于在图像纹理强度中,局部的表层曝光贡献的比重较大,所以进行Gamma归一化处理,可以有效地降低图像局部阴影和光照的变化,还可以抑制噪音的干扰。
  • 常用的 Gamma 归一化处理可以是:
    1. 对每个颜色通道分别计算平方根
    2. 对每个颜色通道分别求log
灰度化

可选操作

  • 灰度化是将彩色图片变成灰度图。其实彩色图片也可以直接处理。不过是分别对三通道的颜色值进行梯度计算,最后选择梯度最大的那个。

计算图像梯度

  • 计算图像的梯度主要是为了通过梯度信息来描述图像中物体的边缘、轮廓、形状等纹理信息。在这个步骤中,首先需要计算图像的横纵坐标方向的梯度,然后根据计算出的图像横坐标和纵坐标方向梯度算出每个像素位置的梯度方向值。
  • 假设一张图像中像素点用$(x,y)$表示,则该像素点的水平梯度和垂直梯度分别为$G_x(x,y)$和$G_y(x,y)$,计算如公式如下:
$$ \begin{array}{c} G_{x}(x, y)=H(x+1, y)-H(x-1, y) \\ G_{y}(x, y)=H(x, y+1)-H(x, y-1) \end{array} $$
  • 其中$H(x,y)$表示输入的图像在像素点(x,y)的像素值。根据得到的像素点水平梯度和垂直梯度就可以得到图像中素点$(x,y)$处的梯度幅值$G(x,y)$和梯度方向$α(x,y)$如下所示:
$$ \begin{array}{c} G(x, y)=\sqrt{G(x, y)^{2}+G_{y}(x, y)^{2}} \\ \alpha(x, y)=\tan ^{-1}\left(\frac{G_{y}(x, y)}{G_{x}(x, y)}\right) \end{array} $$
Opencv 实现
  • 使用 Opencv 中的 sobel 算子可以出水平和垂直方向的梯度:
1
2
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)
  • 利用公式求取梯度幅值和方向:
  • Opencv 中使用 cartToPolar 可以计算角度:
1
mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

为每个cell单元构建梯度方向直方图

此步骤主要是为局部图像区域提供一个编码,同时能够保持对图像中人体对象的姿势和外观的弱敏感性。

  • 首先将图像划分成若干个块(Block),每个块又由若干个细胞单元(cell)组成,细胞单元由更小的单位像素(Pixel)组成,然后把所有cell单元的全部像素通过梯度方向在直方图中执行加权投影的操作,则可获得每个cell单元格的梯度方向直方图。

计算算例

  • 按照梯度计算的结果,每一个像素点都会有两个值:梯度强度/梯度方向。

  • 梯度直方图是在一个 $8\times8$ 的cell里面计算的。那么在8*8的cell里面就会有 $8\times8\times2=128$ 个值(2 是包括了梯度强度和梯度方向)。

  • 通过统计形成梯度直方图,128 个值将会变成 9 个值,大大降低了计算量,同时又对光照等环境变化更加地robust。

计算方法
  • 首先,将 $0-180$ 度分成 9 个bins,分别是 $0,20,40…160$。然后统计每一个像素点所在的 bin。
  • 根据梯度方向选择当前梯度会投影在哪个bin 里,根据梯度方向距 bin 的距离加权分配梯度强度值到对应的 bin 中

  • 左上图是 $8\times8$ 的梯度方向值,右上图是 $8\times8$ 的梯度强度值,下图是 9 个bins。

  • 蓝色圈圈: 蓝圈的方向是 80 度,大小是 2,所以该点就将梯度强度 —— 2 投给 80 这 个bin;

  • 红色圈圈: 红色圈圈的方向是10,大小是4,因为 10 距 0 度的 bin距离 为10,距 20度的 bin 距离也为10,那么有一半的大小是投给 0-bin,还有一半的大小(即是2)投给20-bin。

统计直方图
  • 那么统计完64个点的投票数以后,每个bin就会得到一个数值,可以得到一个直方图,在计算机里面就是一个大小为9的数组。

  • 从上图可以看到,更多的点的梯度方向是倾向于0度和160度,也就是说这些点的梯度方向是向上或者向下,表明图像这个位置存在比较明显的横向边缘。因此HOG是对边角敏感的,由于这样的统计方法,也是对部分像素值变化不敏感的,所以能够适应不同的环境。

把cell单元组合成block,块内归一化梯度直方图

  • 由于局部光照的变化以及前景-背景对比度的变化,使得梯度强度的变化范围非常大。所以必须归一化梯度强度,归一化梯度强度可以实现对边缘、光照和阴影的压缩。最后再合并每个块的特征描述子就能够获得这张图像的 HOG 特征描述子,也就是 HOG 特征向量。
  • 每个 block 可以得到 4 个 9 维的向量,需要再次进行一次归一化,这样可以进一步提高泛化能力,使用 L2-nrom 进行归一化 (还有L1-norm, L1-sqrt,etc.)
$$ v=\frac{v}{\sqrt{\|v\|_{2}^{2}+\varepsilon^{2}}} $$
  • 其中范数操作定义为:
$$ \|x\|_{p}:=\left(\sum_{i=1}^{n}\left|x_{i}\right|^{p}\right)^{1 / p} $$

归一化图示

block 归一化

  • 绿色方块是 $8\times8$ 大小的 cell,蓝色方块就是由 4 个 cell 组成的 block。
  • 作者提出要对 block 进行 normalize。那么由于一个cell就会有大小为9的vector,4 个 cell 就有 36 大小的vector。
  • 对 block 进行 normalize 就是对这大小为 36 的 vector 进行归一化。

整体流程图

HOG提取流程

整体图示

  • 默认HOG的描述子窗口为 $64\times128$, 窗口移动步长为 $8\times8$
  • 每个窗口的 cell 为 $8\times8$,每个block由4个cell组成,block移动步长为一个cell,因此可以得到 $7\times15$个 block

HOG

  • 直方图把 180 度分为 9 个 bin,每个区间为 20 度,如果像素落在某个区间,就把该像素的直方图累计到对应区间的直方图上

  • 每个 block 有 4 个cell,每个 cell 有 9 个向量值,即每个 block 有 36 个向量,所以整个窗口有 $7\times15\times36=3780$ 个特征描述子。

OpenCV 实现 HOG 示例

行人检测代码示例

  1. hog = cv2.HOGDescriptor() :创建HOG特征描述;
  2. hog.setSVMDetector(cv.HOGDescriptor_getDefaultPeopleDetector()) :创建HOG+SVM行人检测器;
  3. 多尺度检测API:
1
2
3
4
5
6
rects, weights = hog.detectMultiScale(img, foundLocations,
hitThreshold = 0,
winStride, padding,
scale = 1.05,
finalThreshold = 2.0,
useMeanshiftGrouping = false)
  • 输入
参数 含义
Img 输入图像
foundLocations 发现对象矩形框
hitThreshold SVM距离度量(特征与SVM分类超平面之间距离),默认0表示
winStride 窗口步长
padding 填充
scale 尺度空间
finalThreshold 最终阈值,默认为2.0
useMeanshiftGrouping 不建议使用,速度太慢

PS:其中窗口步长与Scale对结果影响最大,特别是 Scale ,小的尺度变化有利于检出低分辨率对象,同时也会导致FP发生,高的可以避免FP但是会产生FN(对象漏检)。

  • 示例图像(来自度娘)

  • 示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import cv2 as cv

src = cv.imread("people.jpg")
cv.imshow("input", src)
# hog特征描述
hog = cv.HOGDescriptor()
# 创建SVM检测器
hog.setSVMDetector(cv.HOGDescriptor_getDefaultPeopleDetector())
# 检测行人
(rects, weights) = hog.detectMultiScale(src,
winStride=(4, 4),
padding=(8, 8),
scale=1.25,
useMeanshiftGrouping=False)
for (x, y, w, h) in rects:
cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv.imshow("hog-people", src)
cv.waitKey(0)
cv.destroyAllWindows()
  • 识别结果

事实上这个算法还是挺挑图像和参数的,只是恰好这张数据比较好看

HOG 特征提取示例

  • 更灵活的用法是亲自提取数据的 HOG 特征
  • 示例代码
1
2
3
4
5
6
7
8
9
import cv2
import numpy as np

data = (np.random.rand(128, 64) * 255).astype('uint8')
hog = cv2.HOGDescriptor()
descriptors = hog.compute(data, winStride=(8, 8), padding=(0, 0))
print(descriptors.shape)

>> (3780,)

参考资料


图像方向梯度直方图 HOG 特征
https://www.zywvvd.com/notes/study/image-processing/feature-extraction/hog/hog/
作者
Yiwei Zhang
发布于
2022年10月8日
许可协议