基于OpenCV的行人检测 基于OpenCV提取HOG特征(二)

任务描述
上一个任务我们一起学习了HOG特征提取的步骤,并实现了其中的颜色空间归一化和计算梯度图,在本次任务中我们将继续学习并实践接下来的步骤。

相关知识
为了完成本关任务,你需要了解:

计算梯度方向直方图;
重叠块直方图归一化,计算HOG特征向量。
计算梯度方向直方图
在这一步,我们先把整个图像划分为若干个8x8的小单元,称为cell,并计算每个cell的梯度直方图。这个cell的尺寸也可以是其他值,根据我们想要的特征尺度来确定。HOG特征最早提出是用于行人检测的,64x128大小的图像使用8x8的cell已经足够捕捉到感兴趣的特征了。

现在我们要把这个8x8的小单元用长度为9的数组来表示,这个数组就是梯度直方图。这种表示方法不仅使得特征更加紧凑,而且对单个像素值的变化不敏感,也就是能够抗噪声干扰。

我们来看一下图片中的一个cell中的梯度:

 

中间那张图中的箭头表示梯度,箭头方向表示梯度方向,箭头长度表示梯度大小。

右图是 8×8 的cell中表示梯度的原始数字,注意角度的范围介于0到180度之间,而不是0到360度, 这被称为“无符号”梯度,因为两个完全相反的方向被认为是相同的。

现在我们来计算cell中像素的梯度直方图,先将角度范围分成9份,也就是9 bins,每20°为一个单元,也就是这些像素可以根据角度分为9组。将每一份中所有像素对应的梯度值进行累加,可以得到9个数值。直方图就是由这9个数值组成的数组,对应于角度0、20、40、60... 160。

 

比如上面方向图中蓝圈包围的像素,角度为80度,这个像素对应的幅值为2,所以在直方图80度对应的bin加上2。红圈包围的像素值,角度为10度,介于0度和20度之间,其幅值为4,那么这个梯度值就被按比例分给0度和20度对应的bin,也就是各加上2。

此外还需要注意的是,若某个像素的梯度角度大于160度,也就是在160度到180度之间,那么把这个像素对应的梯度值按比例分给0度和160度对应的bin。

 

将这 8x8 的cell中所有像素的梯度值加到各自角度对应的bin中,就形成了长度为9的直方图:

 

从上图可以看到,更多的点的梯度方向是倾向于0度和160度,也就是说这些点的梯度方向是向上或者向下,表明图像这个位置存在比较明显的横向边缘。因此HOG是对边角敏感的,由于这样的统计方法,也是对部分像素值变化不敏感的,所以能够适应不同的环境。

def compute_cell_gradient(cell_magnitude, cell_angle, bin_size, unit):
    centers = [0] * bin_size
    # 遍历`cell`,统计梯度方向
    for i in range(cell_magnitude.shape[0]):
        for j in range(cell_magnitude.shape[1]):
            strength = cell_magnitude[i][j]
            gradient_angle = cell_angle[i][j]
            # 将像素归到某一个bin
            idx = int(gradient_angle / unit)
            mod = gradient_angle % unit
            min_angle = idx
            max_angle = (idx + 1) % bin_size
            # 根据角度的相近程度分别对邻近的两个区间进行加权
            centers[min_angle] += (strength * (1 - (mod / unit)))
            centers[max_angle] += (strength * (mod / unit))
    return centers
重叠块直方图归一化,计算HOG特征向量
为了降低光照的影响,HOG将图像中8x8的一个区域作为一个cell,再将2x2个cell作为一个block,对每一个block进行了归一化操作。由于每个cell有9个值,2×2个cell则有36个值,通过步长为一个cell的滑动窗口来得到block的。所以窗口每滑动一次,得到一个block,一个block有四个cell,在前面的步骤中,我们基于图像的梯度对每个cell创建了一个直方图,即有四个直方图,将这4个直方图拼接成长度为36的向量,然后使用L2范数对这个向量进行归一化。

 

def normalized(cell_gradient_vector):
    hog_vector = []
    for i in range(cell_gradient_vector.shape[0] - 1):
        for j in range(cell_gradient_vector.shape[1] - 1):
            block_vector = []
            block_vector.extend(cell_gradient_vector[i][j])
            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)
            hog_vector.append(block_vector)
    return hog_vector
编程要求
本次任务主要使用OpenCV实现HOG特征提取的后面几个步骤:计算梯度方向直方图;重叠块直方图归一化,计算HOG特征向量。

根据提示,在右侧编辑器补充代码,针对给定的注释提示进行对应操作。

测试说明
平台会对你编写的代码进行测试:

预期输出:
(64, 64, 9)
3969

import cv2
import numpy as np
import math
from utils import *

def compute_image_gradient(img):
    x_values = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
    y_values = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
    magnitude = abs(cv2.addWeighted(x_values, 0.5, y_values, 0.5, 0))
    angle = cv2.phase(x_values, y_values, angleInDegrees=True)
    return magnitude, angle


def compute_cell_gradient(cell_magnitude, cell_angle, bin_size, unit):
    centers = [0] * bin_size
    for i in range(cell_magnitude.shape[0]):
        for j in range(cell_magnitude.shape[1]):
            strength = cell_magnitude[i][j]
            gradient_angle = cell_angle[i][j]
            
            ######## Begin1 ########
            
            # 将像素归到某一个bin
            idx = int(gradient_angle / unit)
            mod = gradient_angle % unit
            min_angle = idx
            max_angle = (idx + 1) % bin_size
            
            # 根据角度的相近程度分别对邻近的两个区间进行加权
            centers[min_angle] += (strength * (1 - (mod / unit)))
            centers[max_angle] += (strength * (mod / unit))
            
            ######## En1 ########
            
    return centers


def normalized(cell_gradient_vector):
    hog_vector = []
    for i in range(cell_gradient_vector.shape[0] - 1):
        for j in range(cell_gradient_vector.shape[1] - 1):
            block_vector = []
            
            ######## Begin2 ########
            
            # 拼接四个cell
            block_vector.extend(cell_gradient_vector[i][j])
            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:
                
                # L2范数归一化
                normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]
                block_vector = normalize(block_vector, magnitude)
                
            # 统计HOG特征
            hog_vector.append(block_vector)
            
            ######## End2 ########
    return hog_vector


def main(img):
    cell_size = 8
    bin_size = 9
    unit = 180
    height, width = img.shape

    magnitude, angle = compute_image_gradient(img)

    cell_gradient_vector = np.zeros((height // cell_size, width // cell_size, bin_size))
    for i in range(cell_gradient_vector.shape[0]):
        for j in range(cell_gradient_vector.shape[1]):
            cell_magnitude = magnitude[i * cell_size:(i + 1) * cell_size,j * cell_size:(j + 1) * cell_size]
            cell_angle = angle[i * cell_size:(i + 1) * cell_size,j * cell_size:(j + 1) * cell_size]
            cell_gradient_vector[i][j] = compute_cell_gradient(cell_magnitude, cell_angle, bin_size, unit)
            
    hog_vector = normalized(cell_gradient_vector)
    
    test_task5(cell_gradient_vector,hog_vector)
    

if __name__ == '__main__':
    # 读取图像:/data/workspace/myshixun/src/data/Lena.jpg
    img = cv2.imread("/data/workspace/myshixun/src/data/Lena.jpg", 0)
    main(img)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值