介绍
方向梯度直方图(Histogram of OrientedGradient,HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。主要思想是在一副图像中,局部目标的表象和形状(appearanceand shape)能够被梯度或边缘的方向密度分布很好地描述。(本质:梯度的统计信息,而梯度主要存在于边缘的地方)。
【优点】:
首先,由于HOG是在图像的局部方格单元上操作,所以它对图像几何的和光学的形变都能保持很好的不变性。其次,在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。因此HOG特征是特别适合于做图像中的人体检测的。
计算步骤
-
灰度图方式加载图片,假设该图像大小为(128,64)
-
灰度图像gamma 矫正
-
梯度计算
利用一个微分Sobel函数,分别计算灰度图像X和Y方向上的梯度图像,根据着两幅图像,计算出梯度幅值图像
-
8 × 8 8\times8 8×8 Cell 梯度直方图
每个Cell 为 8 × 8 8\times8 8×8,
所以,一共有 个Cell(灰度图像的大小为 128 × 64 128\times64 128×64)
将每个Cell 根据角度值(0-180)分成9个bin,并计算每个Cell的梯度直方图,每个Cell 有9个值,如:
1). 蓝色的线((1, 1)处的值 ):该像素点的梯度方向为10,在直方图的取值中没有10,而10介于0和20之间,10 = 0 × 0.5 0\times0.5 0×0.5 + 20 × 0.5 20\times0.5 20×0.5 ,所以,将表示该像素点的梯度方向大小的值在梯度直方图按相同比例分配: 0: 4 × 0.5 4\times0.5 4×0.5= 2 ;20: 4 × 0.5 4\times0.5 4×0.5 = 2;
2). 红色的线((1, 4)处的值 ):该像素点的梯度方向为80,在直方图的取值中恰好有80,则直接把其梯度大小填在梯度直方图中
3). 黄色的线((7, 6)处的值 )当像素点梯度方向大小的取值大于160时,则为 160 – 0180 (0)之间,如:165 = 160 × 0.75 160\times0.75 160×0.75 + 180 × 0.25 180\times0.25 180×0.25 ,则160: 85 × 0.75 85\times0.75 85×0.75 = 63.75,180(0): 85 × 0.25 85\times0.25 85×0.25 = 21.25 -
16 × 16 16\times16 16×16 Block 归一化
每( 2 × 2 2\times2 2×2)个Cell为一个block,总共 15 × 7 15\times7 15×7个block(相当于对已经分成16*8个Cell做 2 × 2 2\times2 2×2的卷积)
计算每个block 的梯度直方图,并归一化,每个block都有9◊4个值(一共有4个Cell,直方图以(0-180)分成了9个bin) -
计算Hog特征描述
15 × 7 × 36 ( 4 × 9 ) = 3780 15\times7\times36 (4\times9)=3780 15×7×36(4×9)=3780
最后得到一个长度为3780的特征向量(梯度方向直方图特征)
代码
在初始的时候,为了能够使该图像能够被均等分成 8 × 8 8\times8 8×8 的 Cell ,找到离原图像行列值最近的能够被8整除的数( x, y ),将其用 resize 到新的行列大小( x, y )下,也可以直接指定一个大小,如:(128,64),其后则按照前面所讲述的计算步骤。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
def gamma(img):
return np.power(img / 255.0, 1)
# 渲染图像,即将计算出来的该图像的梯度方向和梯度幅值显示出来
def render_gradient( image, cell_gradient):
cell_size = 16
bin_size = 9
angle_unit = 180// bin_size
cell_width = cell_size / 2
max_mag = np.array(cell_gradient).max()
for x in range(cell_gradient.shape[0]):
for y in range(cell_gradient.shape[1]):
cell_grad = cell_gradient[x][y]
cell_grad /= max_mag
angle = 0
angle_gap = angle_unit
for magnitude in cell_grad:
angle_radian = math.radians(angle)
x1 = int(x * cell_size + magnitude * cell_width * math.cos(angle_radian))
y1 = int(y * cell_size + magnitude * cell_width * math.sin(angle_radian))
x2 = int(x * cell_size - magnitude * cell_width * math.cos(angle_radian))
y2 = int(y * cell_size - magnitude * cell_width * math.sin(angle_radian))
cv2.line(image, (y1, x1), (y2, x2), int(255 * math.sqrt(abs(magnitude))))
angle += angle_gap
return image
# 获取梯度值cell图像,梯度方向cell图像
def div(img, cell_x, cell_y, cell_w):
cell = np.zeros(shape=(cell_x, cell_y, cell_w, cell_w))
img_x = np.split(img, cell_x , axis=0)
for i in range(cell_x):
img_y = np.split(img_x[i], cell_y, axis=1)
for j in range(cell_y):
cell[i][j] = img_y[j]
return cell
# 获取梯度方向直方图图像,每个像素点有9个值
def get_bins(grad_cell, ang_cell):
bins = np.zeros(shape=(grad_cell.shape[0], grad_cell.shape[1], 9))
for i in range(grad_cell.shape[0]):
for j in range(grad_cell.shape[1]):
binn = np.zeros(9)
grad_list = np.int8(grad_cell[i, j].flatten()) # .flatten()为降维函数,将其降维为一维,每个cell中的64个梯度值展平,并转为整数
ang_list = ang_cell[i, j].flatten() # 每个cell中的64个梯度方向展平
ang_list = np.int8(ang_list / 20.0) # 0-9
ang_list[ang_list >= 9] = 0
for m in range(len(ang_list)):
binn[ang_list[m]] += int(grad_list[m]) # 直方图的幅值
bins[i][j] = binn
return bins
# 计算图像HOG特征向量并显示
def hog(img, cell_x, cell_y, cell_w):
gradient_values_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) # x
gradient_values_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5) # y
gradient_magnitude = np.sqrt(np.power(gradient_values_x, 2) + np.power(gradient_values_y, 2))
gradient_angle = np.arctan2(gradient_values_x, gradient_values_y)
print(gradient_magnitude.shape, gradient_angle.shape)
gradient_angle[gradient_angle > 0] *= 180 / 3.14
gradient_angle[gradient_angle < 0] = (gradient_angle[gradient_angle < 0] + 3.14) * 180 / 3.14
# plt.imshow()是以图像的大小,显示当前每一个像素点计算出来的梯度方向值
# plt.imshow(gradient_magnitude ) #显示该图像的梯度大小值
plt.imshow(gradient_angle ) #显示该图像的梯度方向值
# 该图像的梯度大小值和方向值只能显示一个,如果用 plt.imshow()想要同时显示,则要分区
plt.show()
grad_cell = div(gradient_magnitude, cell_x, cell_y, cell_w)
ang_cell = div(gradient_angle, cell_x, cell_y, cell_w)
bins = get_bins(grad_cell, ang_cell)
hog_image = render_gradient(np.zeros([img.shape[0], img.shape[1]]), bins)
plt.imshow(hog_image, cmap=plt.cm.gray)
plt.show()
feature = []
for i in range(cell_x - 1):
for j in range(cell_y - 1):
tmp = []
tmp.append(bins[i, j])
tmp.append(bins[i + 1, j])
tmp.append(bins[i, j + 1])
tmp.append(bins[i + 1, j + 1])
tmp -= np.mean(tmp)
feature.append(tmp.flatten())
return np.array(feature).flatten()
def main():
cell_w = 8
img = cv2.imread('3.png', cv2.IMREAD_GRAYSCALE)
print(img.shape)
x = img.shape[0] - img.shape[0] % cell_w #找到离原图像行值最近的能够被8整除的数
y = img.shape[1] - img.shape[1] % cell_w #找到离原图像列值最近的能够被8整除的数
resizeimg = cv2.resize(img, (y, x), interpolation=cv2.INTER_CUBIC)
cv2.imshow("resizeimg",resizeimg)
cell_x = int(resizeimg.shape[0] // cell_w) # cell行数
cell_y = int(resizeimg.shape[1] // cell_w) # cell列数
gammaimg = gamma(resizeimg) * 255
feature = hog(gammaimg, cell_x, cell_y, cell_w)
print(feature.shape)
if __name__ == '__main__':
main()
cv2.waitKey(0)
cv2.destroyAllWindows()