# 方向梯度直方图HOG

# 1.关于HOG

HOG是2005年发表的论文Histograms of Oriented Gradients for Human Detection (opens new window)中提出的,方向梯度直方图Histograms of Oriented Gradients用于早期的行人检测方法,文章至今引用已超过41500次,是一种非常经典的提取图像特征的方法。要想计算HOG需要先计算图像的梯度。

# 2.计算x,y方向的梯度

图像可以看成是离散二元函数,图像梯度就是这个二元离散函数的偏导。

离散二元函数的偏导数可以使用有限差分来近似计算,图像偏导可写成如下形式:

f(x,y)x=f(x+1,y)f(x,y)f(x,y)y=f(x,y+1)f(x,y)

上面是前向差分求梯度,还可以使用后向差分,

f(x,y)x=f(x,y)f(x1,y)f(x,y)y=f(x,y)f(x,y1)

除了前向差分/后向差分外,还可以使用中心差分:

f(x,y)x=f(x+1,y)f(x1,y)2f(x,y)y=f(x,y+1)f(x,y1)2

可以使用OpenCV种的Sobel函数来求图像的梯度:

grad_x = cv2.Sobel(image, ddepth, 1, 0, ksize=1, borderType=cv2.BORDER_DEFAULT)

上面代码dx=1,dy=0表示求图像x方向的一阶导数,ksize=1表示使用的是1x3的卷积核,如下:

grad_y = cv2.Sobel(image, ddepth, 0, 1, ksize=1, borderType=cv2.BORDER_DEFAULT)

上面代码dx=0,dy=1表示求图像y方向的一阶导数,ksize=1表示使用的是3x1的卷积核,如下:

borderType表示的是对图像边沿的处理方式,OpenCV中给出的图像边沿处理方式有:

Enumerator
BORDER_CONSTANT 
Python: cv.BORDER_CONSTANT

iiiiii|abcdefgh|iiiiiii with some specified i

BORDER_REPLICATE 
Python: cv.BORDER_REPLICATE

aaaaaa|abcdefgh|hhhhhhh

BORDER_REFLECT 
Python: cv.BORDER_REFLECT

fedcba|abcdefgh|hgfedcb

BORDER_WRAP 
Python: cv.BORDER_WRAP

cdefgh|abcdefgh|abcdefg

BORDER_REFLECT_101 
Python: cv.BORDER_REFLECT_101

gfedcb|abcdefgh|gfedcba

BORDER_TRANSPARENT 
Python: cv.BORDER_TRANSPARENT

uvwxyz|abcdefgh|ijklmno

BORDER_REFLECT101 
Python: cv.BORDER_REFLECT101

same as BORDER_REFLECT_101

BORDER_DEFAULT 
Python: cv.BORDER_DEFAULT

same as BORDER_REFLECT_101

BORDER_ISOLATED 
Python: cv.BORDER_ISOLATED

do not look outside of ROI

如下图,是一幅图像x,y方向梯度求解的过程

求得后,再求根据如下公式求梯度的幅值和角度:

G=Gx2+Gy2θ=artanGyGx

使用OpenCV中提供的函数可以很方便的求得上面的两个值:

mag, angle = cv2.cartToPolar(np.float64(grad_x), np.float64(grad_y), angleInDegrees=True)

到这里就计算得到了图像的梯度,下面开始介绍方向梯度直方图。

# 3.计算HOG

得到一幅图像的梯度后,接下来将图像分成cwxch大小的cellcwch通常取为8x8cwch的选择要结合自己的图像数据,HOG最早应用在行人识别上,图像大小为64×128,因此8x8cell足以用来表示人体的特征,如人脸等。

对于图像的每个8x8cell,取对应的梯度幅值和角度,如下图:

将梯度角度分成bins份来绘制8x8 cell中的梯度直方图,如分成9份,对应的角度分别为0,20,40,60,...160。上图中间的小图中,箭头表示梯度方向,箭头长度表示梯度幅值的大小。右边的图中梯度角度的范围为[0, 180],只表示是水平边沿还是垂直边沿,并不判断上下左右,被称为"无符号梯度"。

如上图表示一个cell梯度直方图的生成过程,蓝色位置,角度为80,幅值为2,加到对应直方图向量上,红色位置,角度为10,幅值为4,分到0处的梯度幅值,分到20处的梯度幅值。同样,对于其他位置的计算也类似。特别的,当角度为165幅度为85时,将幅值对应到0度和160度上算直方图,分到0处的梯度幅值,分到160处的梯度幅值。最后可以求得一个cell的梯度直方图如下:

根据前面介绍的梯度计算过程,可知梯度幅度受光照影响大,当灰度值变大时,梯度值也会跟着变大,为了减小光照的影响,可以对梯度直方图做归一化。

对彩色像素,使用L2范数的归一化过程为:

0.87=1281282+642+3220.43=641282+642+3220.22=321282+642+322

上面,当向量变成后依然有相同的归一化后的值。

HOG的计算中,会将2x2个前面介绍的8x8 cell组合到一起成block,将每个cell的直方图向量拼接到一起,作为这个block的特征向量。前面介绍的每个cell的直方图包含9bin,组合后每个block包含36个bin,对这个36个元素的向量使用L2范数归一化得到每个block的特征向量。

# 4.skimage计算HOG

代码来自skimage手册

import matplotlib.pyplot as plt

from skimage.feature import hog
from skimage import data, exposure


image = data.astronaut()

fd, hog_image = hog(image, orientations=8, pixels_per_cell=(16, 16),
                    cells_per_block=(1, 1), visualize=True, channel_axis=-1)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True)

ax1.axis('off')
ax1.imshow(image, cmap=plt.cm.gray)
ax1.set_title('Input image')

# Rescale histogram for better display
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))

ax2.axis('off')
ax2.imshow(hog_image_rescaled, cmap=plt.cm.gray)
ax2.set_title('Histogram of Oriented Gradients')
plt.show()

(adsbygoogle = window.adsbygoogle || []).push({});

# 参考资料