机器学习图像处理 HOG 算法原理

机器学习图像处理 HOG 算法原理

HOG 算法思想

HOG算法(方向梯度直方图)是在2005年由Navneet Dalal在论文Histograms of Oriented Gradients for Human Detection中首次提出的,主要是为了基于各像素点的梯度提取出图像中目标的轮廓,用较少的特征更好地表达图像中目标信息

主要思想:

官方解释:在一幅图像中,局部目标的表象和形状能够被梯度或边缘的方向密度分布很好的描述

个人理解:比如我们在做动物识别时,给我们一张图,判断其为猫还是狗,首先我们已经有许多猫狗图片的训练集,但是怎么将图片中的特征提取出来,一张彩色图片其实就是一个三维矩阵,如果我们直接将这三维矩阵合并成一个向量,那么这个向量在后面进行训练时会出现几个问题:

  1. 目标会受到目标所处的环境影响,环境是我们不关心的特征,如果使用整个图片的信息,将会加大我们后面识别目标的难度,所以我们主要通过提取目标的轮廓信息

  2. 原始彩图的特征信息太多,可能后面用分类算法时训练不充分,用hog算法可以对特征进行压缩,经研究实验发现,经过hog提取特征后识别的成功率会大大提升

HOG 算法设计

HOG 图像特征提取算法主要可以分为如下的几个步骤:

  • 图像预处理

  • 计算图像梯度

  • 计算梯度直方图

  • 图像HOG特征向量

HOG 算法的过程主要如下:首先读取彩色图像并将其转化为灰度图像;并对灰度图像矩阵归一化,目的是为了减少光照和背景等因素的影响,本文主要用伽马校正法;选择合适的梯度算子来计算梯度图,主要分为 x 和 y 方向上的梯度;然后计算出合梯度的幅值和方向;划分检测窗口成大小相同的 cell 单元;组合相邻的 cell 单元成更大的相互重叠的块(block),便于充分利用重叠的边缘信息,然后统计整个块的直方图;对每个块内的梯度直方图归一化,综合所有块的信息,对 HOG 特征描述符可视化

图像预处理

这一步主要是将图像裁剪缩放到 512x512 的尺度

其他的还有灰度化操作与 Gamma 矫正,这两个都是可选的操作

无论是灰度图还是 RGB 图都可以计算图像梯度,不过颜色图像分别对 RGB 通道进行计算选择其中最大的梯度作为最终的梯度即可

Gamma 矫正是为了减少光照对实验的影响,但是实验证明对结果影响不大

读取灰度图片(彩色图片颜色信息作用不大,只需要灰度图的轮廓信息):

import cv2
img = cv2.imread('yang.jpg', 0)     #参数0表示读取灰度图片
cv2.imshow('IMREAD_gray', img)
cv2.waitKey(0)   #避免图片一闪而过,保证其长时间停留窗口
cv2.deatroyAllWindows()     #避免打开图片引起未响应结果

所读取的灰度图如下:

采用伽马校正法对得到的灰度图进行归一化,伽马校正可以理解通过对原灰度图归一化后的结果经过一个幂函数转化,目的是为了调节图像的对比度,降低图片背景和光照的影响,同时可以抑制噪声的干扰。本文伽马校正指数选择的是 1.5:

img2 = np.power(img/float(np.max(img)), 1.5)    #幂运算,参数一为底数,参数二为指数,这是伽马矫正,使得图像更接近人眼所见

校正后的图片如下:

通过比较原灰度图和经过伽马校正后的图片发现,伽马校正的灰度图目标轮廓更加明显

计算图像梯度

直接使用一维的 sober 算子分别计算像素点的水平以及竖直方向的梯度

对于一个像素点 \(I(x,y)\),及其 8 邻域如下图所示

A0 A1 A2
A7 I(x,y) A3
A6 A5 A4

其水平方向梯度为:

\[G_x = A3 - A7 \]

竖直方向梯度为:

\[G_y = A5 - A1 \]

最终像素点 \(I(x,y)\) 的梯度强度以及方向为:

\[G = \sqrt{G_x^2 + G_y^2}\\ \theta = tan^{-1}(\frac{G_y}{G_x}) \]

对伽马校正后的图像计算各个像素点的梯度(包含大小和方向),目的捕获轮廓信息。常用于计算梯度的梯度算子主要有Prewitt算子、Sobel算子等等(参考几种梯度算子),本文选择最简单的Prewitt算子,在python中主要通过对cv2.Sobel函数的ksize参数选择来决定使用哪种算子,需要分别计算x方向和y方向上的梯度,代码如下

gx = cv2.Sobel(img2, cv2.CV_64F, 1, 0, ksize=1)   #ksize为Sobel算子大小,3是矩阵大小为3
gy = cv2.Sobel(img2, cv2.CV_64F, 0, 1, ksize=1)
gx1 = np.abs(gx)    ##在计算梯度时可能存在右侧像素减去左侧像素为负值的情况,所以对其取绝对值后进行比较
gy1 = np.abs(gy)
cv2.imshow('IMREAD_gray2_gx', gx1)
cv2.imshow('IMREAD_gray2_gy', gy1)
cv2.waitKey(0)   #避免图片一闪而过,保证其长时间停留窗口
cv2.destroyAllWindows()

注意:上述代码中要有一个对计算的梯度取绝对值的过程,因为当计算梯度时右侧像素减去左侧像素为负值时,图片显示会将负数视作 0

x方向上的梯度图如下:

y方向上的梯度图:

计算梯度直方图

在上一步中,计算得到图像中所有像素点的梯度强度以及方向

将伽马校正后的图像划分成cell单元,本文是将其划分成16 * 16大小的cell(即每16 * 16个像素为一个cell),并计算每个cell的梯度直方图,此时每个cell单元所含有的信息有16 × 16 × 2 = 512 16 x 16 x 2=51216×16×2=512个值,因为每个像素点处有梯度的大小和方向两个值

接下来针对每个cell计算其梯度直方图,也就是将这512个值的信息能压缩成更少的特征,所以就用长度为9的数组来表示这16 × 16的信息。将角度范围0-180°划分成9份,即9 bins,也就是将像素点的梯度累计加到对应角度数组中,这也就是将一个cell中的梯度大小和方向信息用梯度直方图来表示

在获得每个 cell 的直方图后,将 2x2 个 cell 组合为一个 block,将次 block 中计算得到的梯度直方图 concat 得到包含一个 4x9=36 长度的 vector 作为此 block 的特征向量

然后利用 L2Norm 对这个 36 维的特征向量进行规范化

图像 HOG 特征向量

确定block大小,本文选择将2 × 2 2\times22×2个cell作为一组,即为一个block,最终整张图片信息是通过滑动block窗口来获取的,此时一个block中包含的是4个cell,每个cell中包含的是9个值(梯度直方图),故而一个block中包含36个值

接下来是对每个block进行归一化,之所要归一化的原因是图像的梯度对光照环境等因素非常敏感,比如将图片像素值都除以2,此时图片光线会变暗,那么梯度的幅度值也会减少一半,因此梯度直方图中的值也就会减少一半,为了避免特征描述符不受环境影响,我们需要将直方图归一化

将一个block中4个cell拼接成长度为36的向量,通过对向量每个元素除以该向量的L2范数对block归一化

计算HOG特征向量,首先明确其大小,由于block从图片左上角向右下角滑动,x方向上可滑动(32-1)次,同理y方向上也可滑动(32-1)次,所以最终图片的HOG特征向量由31 × 31 × 36 = 34596个值组成。然后对图像的HOG特征进行可视化,注意重新缩放直方图以获得更好的展示,代码如下:

from skimage import feature
from skimage import exposure
fd, hog_image = feature.hog(img, orientations=9, pixels_per_cell=(16, 16), cells_per_block=(2, 2), visualize=True)
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))
cv2.imshow('img', img)
cv2.imshow('hog', hog_image_rescaled)
cv2.waitKey(0)
cv2.destroyAllWindows()

最后基于HOG特征向量重新缩放的直方图可视化如下:

总结

HOG算法是一个将图片原始信息最终压缩成一个向量的过程,当我们将一张照片通过HOG算法得到此HOG特征描述符后,相当于我们得到了图片的样本数据,就可以拿来用于机器学习中对模型进行训练,然后结合机器学习模型比如SVM来实现路上行人检测等目的。当然HOG虽然存在维度降低,忽略光照等环境因素的影响,但也存在因梯度使得描述子对噪声敏感的问题。所以可以结合SIFT和LBP算法来对图像进行处理

posted @ 2023-06-05 23:31  ppqppl  阅读(94)  评论(0编辑  收藏  举报