HOG算法的实现

查看了一些博客对HOG算子进行代码实现,该算法常和SVM算法相结合,我个人觉得原因在于,该算法可以提取特征。

该特征是利用图像本身的梯度及角度得到的信息,作为输入的矢量,因此有了svm的输入数据,而后根据每张图对应一个

y标签,则有了训练的真实y值。这样根据svm的分类去训练,将可以对某些目标进行检测了。

基于此,我将借鉴其它博客大佬的解释,对其进行了代码实现,如下:

 

大概过程:

HOG特征提取方法就是将一个image(你要检测的目标):

1)灰度化(将图像看做一个x,y,z(灰度)的三维图像)(必须灰度化);

2)采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;

3)计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰(可以用sobel算法)。

4)将图像划分成小cells(例如6*6像素/cell);

5)统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;

6)将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。

7)将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了。

代码实现如下:

# HOG

#first part
import cv2
import numpy as np
import math


cell_size = 8 # 每个cell像素
bin_size = 8

img = cv2.imread('D:\\YOLO3\\expert\\0.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('Image_gray', img)
cv2.imwrite("D:\\YOLO3\\expert\\original_1.jpg", img)
print('img.shape=',img.shape)
# print(img)
img = np.sqrt(img / float(np.max(img))) # 归一化且gamma变换
cv2.imshow('Image', img)
cv2.imwrite("D:\\YOLO3\\expert\\gamma_1.jpg", 256*img)
# second part
'''
计算图像横坐标和纵坐标方向的梯度,并据此计算每个像素位置的梯度方向值;
求导操作不仅能够捕获轮廓,人影和一些纹理信息,还能进一步弱化光照的影响。
在求出输入图像中像素点(x,y)处的水平方向梯度、垂直方向梯度和像素值,从而求出梯度幅值和方向。

常用的方法是:首先用[-1,0,1]梯度算子对原图像做卷积运算,
得到x方向(水平方向,以向右为正方向)的梯度分量gradscalx,
然后用[1,0,-1]T梯度算子对原图像做卷积运算,得到y方向(竖直方向,以向上为正方向)的梯度分量gradscaly。
然后再用以上公式计算该像素点的梯度大小和方向。
'''

height, width = img.shape
img=cv2.resize(img,(int(height / cell_size), int(width / cell_size)) ) # 因为cell需要是整数个,若图像有超出,则需resize图像尺寸
# height, width = img.shape
# cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) 该函数表示对图像img进行求偏导,cv2.CV_64F表示浮点数,
# 1, 0,表示对x偏导,0表示对y不偏导,ksize=5表示sobel算子尺寸
gradient_values_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) # 对x进行偏导
cv2.imshow('gradient_x=',gradient_values_x)
cv2.imwrite("D:\\YOLO3\\expert\\gradient_values_x.jpg", gradient_values_x*256)
gradient_values_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5) # 对y进行偏导
cv2.imshow('gradient_y=',gradient_values_y)
cv2.imwrite("D:\\YOLO3\\expert\\gradient_values_y.jpg", gradient_values_y*256)
gradient_magnitude = cv2.addWeighted(gradient_values_x, 0.5, gradient_values_y, 0.5, 0) # 按照各自权重相加
cv2.imshow('gradient=',gradient_magnitude)
cv2.imwrite("D:\\YOLO3\\expert\\gradient_magnitude.jpg", gradient_magnitude*256)
gradient_angle = cv2.phase(gradient_values_x, gradient_values_y, angleInDegrees=True) # 求角度 angleInDegrees=True为角度,为False为弧度
cv2.imshow('gradient_angle=',gradient_angle)
cv2.imwrite("D:\\YOLO3\\expert\\gradient_angle.jpg", gradient_angle)
print(np.max(gradient_angle))
print (gradient_magnitude.shape, gradient_angle.shape)
#thrid part
'''
我们将图像分成若干个“单元格cell”,默认我们将cell设为8*8个像素。
假设我们采用8个bin的直方图来统计这6*6个像素的梯度信息。
也就是将cell的梯度方向360度分成8个方向块,例如:如果这个像素的梯度方向是0-22.5度,
直方图第1个bin的计数就加一,这样,对cell内每个像素用梯度方向在直方图中进行加权投影(映射到固定的角度范围),
就可以得到这个cell的梯度方向直方图了,就是该cell对应的8维特征向量而梯度大小作为投影的权值。

'''

angle_unit = 360 / bin_size
gradient_magnitude = abs(gradient_magnitude) # 梯度值


cell_gradient_vector = np.zeros((int(height / cell_size), int(width / cell_size), bin_size)) # 记录每个cell的特征值,每个cell有bin_size个特征值

print (cell_gradient_vector.shape)

def cell_gradient(cell_magnitude, cell_angle):
'''
该函数将同一个cell的梯度值根据分的角度值用一权重分别赋给bin_size个维度数据
'''
orientation_centers = [0] * bin_size # [0,0,0,0,0,0,0,0]
for k in range(cell_magnitude.shape[0]): # 遍历每个cell高
for l in range(cell_magnitude.shape[1]): # 遍历每个cell宽
gradient_strength = cell_magnitude[k][l] # 得到该位置的梯度值
gradient_angle = cell_angle[k][l] # 得到该位置的角度值
min_angle = int(gradient_angle / angle_unit) % bin_size # 找到该角度处于bin_size角度范围的哪个最小区间
max_angle = (min_angle + 1) % bin_size # 找到该角度处于bin_size角度范围的哪个最大区间
mod = gradient_angle % angle_unit
orientation_centers[min_angle] += (gradient_strength * (1 - (mod / angle_unit)))
orientation_centers[max_angle] += (gradient_strength * (mod / angle_unit))
return orientation_centers

for i in range(cell_gradient_vector.shape[0]): # 高
for j in range(cell_gradient_vector.shape[1]): # 宽
'''
这里的2个循环,相当于给每个cell添加了bin_size维度的特征,简单说就是求解每个cell对应角度范围的梯度值的累加
'''
cell_magnitude = gradient_magnitude[i * cell_size:(i + 1) * cell_size, j * cell_size:(j + 1) * cell_size]
cell_angle = gradient_angle[i * cell_size:(i + 1) * cell_size, j * cell_size:(j + 1) * cell_size]
cell_gradient_vector[i][j] = cell_gradient(cell_magnitude, cell_angle) # 参数为每个cell 梯度与角度 return值为cell的的梯度,并将其填充到之前建立的矩阵中


# fifth part
'''
统计Block的梯度信息

把细胞单元组合成大的块(block),块内归一化梯度直方图
由于局部光照的变化以及前景-背景对比度的变化,使得梯度强度的变化范围非常大。这就需要对梯度强度做归一化。归一化能够进一步地对光照、阴影和边缘进行压缩。
把各个细胞单元组合成大的、空间上连通的区间(blocks)。这样,一个block内所有cell的特征向量串联起来便得到该block的HOG特征。这些区间是互有重叠的,
本次实验采用的是矩阵形区间,它可以有三个参数来表征:每个区间中细胞单元的数目、每个细胞单元中像素点的数目、每个细胞的直方图通道数目。
本次实验中我们采用的参数设置是:2*2细胞/区间、8*8像素/细胞、8个直方图通道,步长为1。则一块的特征数为2*2*8。
'''
hog_vector = []
for i in range(cell_gradient_vector.shape[0] - 1): #
for j in range(cell_gradient_vector.shape[1] - 1):
'''
这里的2个循环,相当于给每个block添加了4*bin_size维度的特征,简单说就是求解每个block对应cell梯度特征,在标准化了。
若cell有408*250个,则block有407*249个
'''
block_vector = []
block_vector.extend(cell_gradient_vector[i][j]) # list.extend(sequence) 把一个序列seq的内容添加到列表中
block_vector.extend(cell_gradient_vector[i][j + 1])
block_vector.extend(cell_gradient_vector[i + 1][j])
block_vector.extend(cell_gradient_vector[i + 1][j + 1])
mag = lambda vector: math.sqrt(sum(i ** 2 for i in vector))
magnitude = mag(block_vector) # 得到一个值
if magnitude != 0:
normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]
block_vector = normalize(block_vector, magnitude) # block_vector每个block有
hog_vector.append(block_vector)
print (np.array(hog_vector).shape)



cv2.waitKey(0)

某些结果如下:

 

 

 

 

 

 

 

 



















posted @ 2019-12-23 23:27  tangjunjun  阅读(2811)  评论(1编辑  收藏  举报
https://rpc.cnblogs.com/metaweblog/tangjunjun