一、方向梯度直方图
1.1 实验介绍
- 介绍方向梯度直方图
- 图片转换为方向梯度直方图的流程
- 通过
scikit-image
工具实现方向梯度直方图。
1.1.1 知识点
- 方向梯度直方图的基本概念
- 梯度的计算
- 计算方向梯度直方图
- 归一化
- 使用
scikit-image
实现方向梯度直方图
1.2 什么是方向梯度直方图?
回顾:传统的目标检测流程可分为三个步骤
- 第一步是使用滑动窗口和图像金字塔从图片中选择一些区域。
- 第二步是将选择出来的区域转化为人工设计的特征,可称为
特征提取
。 - 第三步是将这些特征输入分类器进行分类。
方向梯度直方图(Histogram of Oriented Gradients):
- 以下简称
HOG
- 就是一种人工设计的特征
- 用来简化图像表述的特征描述符。
- 下图中左边的图片是一只猫,我们不仅可以看出猫身体上的特征、颜色、纹理,而且还能看到背景。
- 右边的图片是使用 HOG 来表示的图片
- 除了可以看到图中能看出猫的外形,其他的细节包括背景几乎都被去除了
- 故右边的图片是左边图片的一种简化表示形式。
HOG 可以用来表示物体的形状、外形特征
- 将这些特征输入分类器就可以实现目标的分类。
在传统的算法中,使用 HOG 描述图片:
- 可以保留有用信息,剔除无用的信息
- 这样不仅减少计算量,还使得分类器的效果更好。
HOG 可以分为以下几个步骤:
- 预先归一化
- 计算水平和垂直方向梯度
- 计算梯度直方图
- 区域(Blocks)归一化。
二、方向梯度直方图流程
2.1 预先归一化(Normalization)
- 在计算梯度前可对图片归一化(Normalization)处理
- 归一化的目的:
- 使所有的数值落入到统一的范围内
- 从而使算法能有更好的表现。
在 HOG 的原论文中提到使用伽马矫正的方法处理输入图片,伽马矫正可以增加图像的对比度。
但是在很多情况下,伽马矫正对提升算法效果不明显,
- 我们可以跳过图片预先归一化,直接计算图片梯度。
2.2 计算梯度
2.2.1 水平方向和垂直方向上的梯度
前面提到可以不用预先对图片进行归一化,故可以说
-
HOG 特征是从计算图像在水平方向和垂直方向上的梯度开始的。
-
图像的梯度计算是使用卷积核对图像进行卷积操作
-
例如:
- 我们可以使用矩阵
[[-1, 0, 1]]
和[[-1], [0], [1]]
分别与图像上的每个像素进行运算 - 来获得水平和垂直方向上的梯度。
- 我们可以使用矩阵
G
x
=
I
×
W
x
G_x = I \times W_x
Gx=I×Wx
G
y
=
I
×
W
y
G_y = I \times W_y
Gy=I×Wy
- 上面两个公式分别计算水平、垂直方向上的梯度 G x G_x Gx、 G y G_y Gy。
- 其中:
- I I I 是输入图片
- W x W_x Wx 表示水平方向的卷积核
-
W
y
W_y
Wy 表示垂直方向的卷积核。
下面两张图片给我们展示了经过梯度计算后的图片是什么样子
- 左图表示计算图片的水平梯度
- 右图表示计算图片的垂直梯度。
相较于原始图片,两张图片中的信息减少了很多
- 整个 HOG 的过程就是:逐步减少图片中的无用信息,只展示给我们感兴趣的特征。
2.2.2 计算梯度的幅值和方向
-
现在已经有了水平方向的梯度 G x G_x Gx 和垂直方向的梯度 G y G_y Gy
-
接下来我们要计算梯度的幅值和方向。
计算梯度幅值和方向是为了进一步计算方向梯度直方图。 -
两个公式分别计算每个像素的梯度幅值 G G G 和梯度方向 θ \theta θ。
G = G x 2 + G y 2 G = \sqrt {G_x^2 + G_y^2} G=Gx2+Gy2
θ = arctan G y G x \theta = \arctan \frac{G_y}{G_x} θ=arctanGxGy
下面左图是合并水平、垂直方向上的梯度获得的梯度幅值
- 可以看到相较于水平、垂直方向上的图片,左图中猫的轮廓更清晰明显。
- 右图表示图片中的梯度方向。
2.3 方向梯度直方图
2.3.1 平均分割
- 现在我们已经有了梯度幅值 G G G 和梯度方向 θ \theta θ
- 接下来我们就可以计算方向梯度直方图了。
在计算方向梯度直方图之前: - 我们需要将图片分成若干个小方格(Cells)
- 为避免歧义下文皆书写为
Cell
或Cells
。
上图是一张宽高为 649 × 385 649\times 385 649×385 的图片
- 我们将其平均分割成若干个 Cells
- 每个 Cell 内包含 8 × 8 8\times 8 8×8 个像素
所以:
- 图片的高被分为 ⌊ 385 ÷ 8 ⌋ = 48 \lfloor 385\div8 \rfloor = 48 ⌊385÷8⌋=48 份
- 图片的宽被分为 ⌊ 649 ÷ 8 ⌋ = 81 \lfloor 649\div8 \rfloor = 81 ⌊649÷8⌋=81 份( ⌊ ⌋ \lfloor\quad\rfloor ⌊⌋ 表示向下取整)
- 故整张图片有 48 × 81 48\times 81 48×81 个 Cells。
2.3.2 构建方向梯度直方图
- 至此我们已经将图片分成许多 Cells
对于每一个 Cell,使用 G G G 和 θ \theta θ 来构建方向梯度直方图。
- 首先我们先选择梯度方向的范围
- 梯度方向的范围可分为 **0 到 180 度(无符号)**和 0 到 360 度(有符号)
- 通常使用 0 到 180 度的范围。
- 然后将 0 到 180 度的范围分成 9 个区间(bins)
分别为 0 到 20 度,20 到 40 度, 40 到 60 度 …… 160 到 180 度。
每个像素都有一个梯度幅值和一个梯度方向:
- 所以方向梯度直方图的计算就是每个像素所对应的梯度方向落在 9 个区间中的哪一个
- 那么该像素的梯度幅值就在该区间中累加。
上图是一个计算方向梯度图的例子
- 对于红色方框中的像素:
- 假设其中有些像素对应的梯度方向落在 0 到 20 区间
- 那么将这些像素对应的梯度幅值在 0 到 20 区间内进行累加
- 同理其他区间也做同样的运算
- 最终得到上图中右边的方向梯度直方图。
同样地,整张图片中的所有 Cells 都用同样的方法计算方向梯度直方图。
2.4 区域(Blocks)归一化
- 我们已经将图像分成若干个 Cells,并且计算了每个 Cell 的方向梯度直方图。
- 接下来我们要对图像进行区域归一化处理
归一化的目的:
- 是减少光照变化对图像梯度的影响。
2.4.1 什么是区域(Blocks)?
- 首先我们先来介绍什么是区域(Blocks),为避免歧义下文皆书写为
Block
或Blocks
- 前面我们将图像分成若干个
Cells
,每个Cell
内有若干个像素 - 类似地,一个
Block
是一块由若干个Cells
组成的矩形。
2.5 归一化
- 对图像进行归一化的过程:
- 类似于前面实验学习的滑动窗口
- 将一个
Block
从左向右、从上向下在图中滑动 - 然后在每个 Block 区域内进行归一化计算。
在每个 Block 内有
2
×
2
2\times 2
2×2 个 Cells
- 前面我们将梯度方向分为 9 个区间并为每个
Cell
计算了方向梯度直方图,故每个 Cell 有 9 个向量 - 则在一个
Block
内共有 2 × 2 × 9 2\times 2\times 9 2×2×9 个向量。
然后我们可以使用 L 1 L1 L1 范数或 L 2 L2 L2 范数对 Block 内的向量进行归一化。
- 其中使用 L 2 L2 L2 范数进行归一化的效果相对较好
- 下面就是使用 L 2 L2 L2 范数归一化的公式
即 Block 内的每个向量除以由 Block 内所有向量计算得到的 L 2 L2 L2 范数。
- 其中 v i v_i vi 表示 Block 内的向量
-
ϵ
\epsilon
ϵ 的作用是防止出现分母为 0的情况,它是一个很小的值。
从上图中可以看出每一个 Cell
不止出现在一个 Block
内:
- 也就是说一个
Cell
将被重复的用于归一化计算中 - 这样做会看似比较冗余,但是会提高特征描述的表现。
- 最后对所有的 Block 完成归一化计算,合并所有获得的归一化后的向量
- 这样我们就完成了图像的 HOG 特征化表示。
2.6 使用 Scikit-image 实现方向梯度直方图
以上所述的概念或公式也许没法在短时间内理解消化,但这并不影响我们接下来的实验
- 因为 HOG 已经在现有的一些算法库中实现,所以我们完全不用花时间再去复现它。
- 通过几行简单的代码来实现 HOG 算法。
2.6.1 Scikit-image
- 从 Scikit-image 导入
feature
和exposure
模块。feature
模块里存放着一些用于计算特征的算法exposure
模块具有一些直方图处理的功能。
- 我们还需要导入
cv2
模块用于图片的读取。
from skimage import feature
from skimage import exposure
from matplotlib import pyplot as plt
import cv2
%matplotlib inline
接着使用 cv2.imread
函数读取图片。
image = cv2.imread("pets.jpg")
2.6.2 feature.hog
- 使用
feature.hog
用于计算图片的方向梯度直方图。
该函数的参数意义如下所示:
- 第一个参数
image
表示输入图像。 orientations
表示要将梯度方向分成几个区间
这里我们将梯度方向分为 9 个区间。pixels_per_cell
表示Cell
的尺寸- 即一个 Cell 中有几个像素,需要传递一个元组给该参数
- 我们将
(8, 8)
传递给该参数。
cells_per_block
表示每个Block
的尺寸- 即一个 Block 中有几个 Cells,这里需要传递一个元组给该参数
- 我们将
(2, 2)
传递给该参数。
transform_sqrt
表示伽马校正- 我们将
True
传递给该参数 - 表示使用伽马校正预先对图片进行归一化处理。
- 我们将
visualize
表示可视化- 将
True
传递给该参数 - 表示返回 HOG 图像。
- 将
(o, hog) = feature.hog(image, orientations=9, pixels_per_cell=(8, 8),
cells_per_block=(2, 2), transform_sqrt=True, visualize=True)
该函数返回 2 个值:
- 第一个值
o
是 HOG 图像的一维展开数组。 - 第二个值
hog
表示返回一个高和宽同输入图像一样的二维数组
这个值可用于可视化方向梯度直方图。
2.6.3 exposure.rescale_intensity
接下来我们使用 exposure.rescale_intensity
来调整的输入图片的像素值大小或像素强度。
- 因为获得的
hog
中的元素值都为被归一化了,所以这些值都比较小 - 如果直接将这些值作为图像的像素值,那么该图像看起来像一张全黑图片
- 我们需要用该函数将这些值拉伸到一个较大的范围。
hog = exposure.rescale_intensity(hog, out_range=(0, 255))
hog = hog.astype("uint8")
hog[hog > 50] = 255
我们传递 2 个参数给该函数
- 第一个参数
hog
表示前面获取的可视化二维数组。 - 第二个参数
out_range
表示将输入图片的像素强度拉伸到设定的范围
- 这里我们将
hog
中的每个元素值拉伸到(0, 255)
范围内。 - 接下来我们使用
astype
方法将hog
中的元素转换为uint8
类型。 - 最后我们将
hog
中大于50
的元素赋值为255
。
- 使用
plt.figure
创建画板 - 参数
dpi=150
表示图像的分辨率设置为150
。 - 最后使用
plt.imshow
函数显示图片。
plt.figure(dpi=150)
plt.imshow(hog, cmap = 'gray', aspect='auto')
下图:
- 左边图片是输入图片,右边是对输入图片使用 HOG 提取特征后的图片,
- 可以看到相较于左边图片,右边图片仅保留了动物的外形特征。
三、实验总结
- 学习了方向梯度直方图的基本概念
- 对图片使用 HOG 提取特征的几个关键步骤。
- 通过调用算法库中的相关算法来对一张图片提取 HOG 特征。
HOG 方法是传统目标检测算法中有名的方法。
- 如何使用 HOG 特征提取特征并实现一个目标检测的小项目?