cs231A Homework-3:ps3_code-Histogram-of-Oriented-gradients
三、 Histogram of Oriented Gradients(方向梯度直方图)
本部分内容主要实现HoG,然后进行一个简单的应用。
- 首先,进行梯度计算;
- 计算直方图
- 计算HoG特征
3.1 计算梯度: compute_gradient():
参数:
- im: 灰度图片 (H, W)
返回值:
- angles: 梯度角度 (H-2, W-2)
- magnitudes: 梯度 (H-2, W-2)
梯度的计算如下:
- 给定一个像素和周围8个方向的像素,
P1 P2 P3
P4 P5 P6
P7 P8 P9
arctan(dy/dx) = (P2 - p8)/(p4 - P6) [可以使用np.arctan2 : 更稳定],结果为[-180,180)
如果想要得到[0, 180],需要简单地增加180到给负的角度
梯度: sqrt((P4 - P6)^2 + (p2- P8)^ 2)
def compute_gradient(im):
H, W = im.shape
angles = np.zeros((H-2, W-2))
magnitudes = np.zeros((H-2, W-2)) # 建立两个返回值矩阵
for i in range(1, H-1):
for j in range(1, W-1): # 最外圈的点不计算
top = im[i-1, j]
down = im[i+1, j]
left = im[i, j-1]
right = im[i, j+1] # 上下左右
angle = np.arctan2(top - down, left - right) * (180 / math.pi) # 转化为度
magn = np.sqrt((top-down)**2 + (left - right)**2)
if(angle < 0): # 加180
angle += 180
angles[i-1, j-1] = angle
magnitudes[i-1, j-1] = magn
return angles, magnitudes
3.2 生成直方图: generate_histogram()
通过给定的角度矩阵和梯度矩阵,生成角度直方图
参数:
- angles: (M, N)
- magnitudes:(M,N)
- nbins: 直方图区间数
返回值:
- histogram: 多维数组(nbins,),包含梯度角的分布
实现:
- 每个直方图应该被划分为0到180度,nbins决定区间数
- 迭代,将每个对应的梯度角度放到对应的直方图区间中,为了合理分配到两个相近的区间,使用如下公式:
# center_angle 为 区间的中心,比如 20度的区间,第一个和第二个区间的中心为10,30
histogram[bin1] += magnitude * |angle - center_angle2 | / (180 / nbins)
histogram[bin2] += magnitude * |angle - center_angle1 | / (180 / nbins)
认为第1个区间和最后一个区间相邻;
def generate_histogram(angles, magnitudes, nbins = 9):
histogram = np.zeros(nbins)
bin_size = 180 / nbins
center_angles = np.zeros_like(histogram)
for i in range(nbins):
center_angles[i] = (0.5 + i) * bin_size # 计算每个区间中心
M, N = angles.shape
for m in range(M):
for n in range(N):
angle = angles[m, n]
magn = magnitudes[m, n]
abs_diff = np.abs(center_angles - angle)
# 当 angle 趋于0度
if (180 - center_angles[-1] + angle) < abs_diff[-1]: # 更新下最后区间的大小
abs_diff[-1] = 180 - center_angles[-1] + angle
# angle趋于180度
if(180 + center_angles[0] - angle) < abs_diff[0]:
abs_diff[0] = 180 - angle + center_angles[0]
# 统计直方图
bin1, bin2 = np.argsort(abs_diff)[0:2] # 取最近的两个
histogram[bin1] += magn * abs_diff[bin2] / (180.0 / nbins)
histogram[bin2] += magn * abs_diff[bin1] / (180.0 / nbins)
return histogram
3.3 计算HoG特征: compute_hog_features()
参数:
- im: 图片矩阵
- pixels_in_cell: 每个单元格的大小
- cenlls_in_block: 每个block中的cell数量
- nbins: histogram bins
返回值:
- features: hog 特征(H_blocks, W_blocks, cells_in_block * cells_in_block * nbins)
实现:
- 计算每个图片的梯度和角度
- 定义一个cell和block
- 定义一个滑动窗口,大小为一个block的大小,滑动步长为block的一半,每个block中的cell存储一个直方图中的梯度,
每个block 特征被表示为(cells_in_block, cells_in_block, nbins),也可表示为( cells_in_block * cells_in_block * nbins )
,确保归一化 - 返回这些所有网格的HoG特征
def compute_hog_features(im, pixels_in_cell, cells_in_block, nbins):
# 第一步: 获得角度和梯度
angles, magnitudes = compute_gradient(im)
# 第二步: 划分block 和cell
cell_size = pixels_in_cell
block_size = pixels_in_cell * cells_in_block
# 第三步:计算滑动窗口
H, W = angles.shape
stride = int(block_size / 2)
H_blocks = int((H - block_size) / stride) + 1
W_blocks = int((W - block_size) / stride) + 1
# 第四步: 计算每个cell的 histogram
hog_fe = np.zeros((H_blocks, W_blocks, cells_in_block * cells_in_block * nbins))
for h in range(H_blocks):
for w in range(W_blocks):
block_angles = angles[h*stride: h * stride + block_size,
w * stride: w*stride + block_size] # 一个block的角度
block_magn = magnitudes[h*stride: h*stride+block_size,
w*stride: w*stride+block_size] # 梯度
# 将一个block中的每个cell表示为一个方向直方图
block_hog_fe = np.zeros((cells_in_block, cells_in_block, nbins))
for i in range(cells_in_block):
for j in range(cells_in_block):
cell_angles = block_angles[i*pixels_in_cell : (i+1)*pixels_in_cell,
j*pixels_in_cell : (j+1)*pixels_in_cell]
cell_magns = block_magn[i*pixels_in_cell : (i+1) * pixels_in_cell,
j*pixels_in_cell : (j+1) * pixels_in_cell]
cell_hist = generate_histogram(cell_angles, cell_magns, nbins)
block_hog_fe[i, j, :] = cell_hist
# 归一化
block_hog_fe = np.reshape(block_hog_fe, -1)
block_hog_fe /= np.linalg.norm(block_hog_fe)
hog_fe[h, w, :] = block_hog_fe
return hog_fe