Opencv的使用小教程4——HOG特征及其python代码实现

好好学习噢!
在这里插入图片描述

hog特征是什么

HOG特征即方向梯度直方图 (Histogram of Oriented Gradient, HOG),源于2005年一篇CVPR论文,使用HOG+SVM做行人检测,由于效果良好而被广泛应用。HOG是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。

该算法能够工作的原因为:

在一副图像中,局部目标的边缘存在梯度特性,而且这个特征梯度可以很好的描述这个目标。

hog的实现步骤

在这里插入图片描述
简略版实现方法:
1、将图像分成小的连通区域,又名分为多个细胞单元。
2、然后采集细胞单元中各像素点的梯度的或边缘的方向直方图。
3、最后把这些直方图组合起来就可以构成特征描述器。

详细版实现方法:
1、灰度化。
2、采用Gamma校正法对输入图像进行颜色空间的标准化,目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;
3、计算图像每个像素的梯度,主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。
4、将图像划分成多个细胞单元
5、统计每个细胞单元的梯度直方图,获得每个细胞单元的特征描述子。
6、将每几个细胞单元组成一个块(例如3*3个细胞/块),一个块内所有细胞的特征描述子串联起来便得到该块的HOG特征描述子。
7、将图像内的所有块的HOG特征描述子串联起来就可以得到该图像的HOG特征描述子了。

梯度直方图的概念:

1、利用任意一种梯度算子,例如:sobel,laplacian等,对一个块进行卷积,计算得到每个像素点处的梯度方向和幅值。具体公式如下:
在这里插入图片描述
其中,Ix和Iy代表水平和垂直方向上的梯度值,M(x,y)代表梯度的幅度值,θ(x,y)代表梯度的方向。

2、将360度(2*PI)根据需要分割成若干个方向,例如:分割成6个方向,每个bin包含60度,整个直方图包含6维,即6个方向的特征描述子。然后根据每个像素点的梯度方向,利用双线性内插法将其幅值累加到直方图中。

python代码实现:

1、使用scikit-image库:

from skimage import feature as ft
img = cv2.imread('timg.jpg', cv2.IMREAD_GRAYSCALE)
features = ft.hog(img,orientations=6,pixels_per_cell=[20,20],cells_per_block=[2,2],visualize=True)
plt.imshow(features[1],cmap=plt.cm.gray)
plt.show()

其中:
1、image:输入图像
2、orientation: 指定块的个数。把所有的方向都转换为0°~180°内,然后按照orientation划分块,如果你选定的orientation= 6, 则bin一共有6个, 每30°有一个。
3、pixels_per_cell : 每个细胞单元的像素数, 是一个tuple类型数据,例如(20,20)
4、cell_per_block : 每个块内有多少个细胞单元, tuple类型, 例如(2,2), 意思是将block均匀划分为2x2的块。

实现效果为:
在这里插入图片描述

2、源码代码实现:

代码源自https://blog.csdn.net/ppp8300885/article/details/71078555

import cv2
import numpy as np
import math
import matplotlib.pyplot as plt

class Hog_descriptor():
    #---------------------------#
    #   初始化
    #   cell_size每个细胞单元的像素数
    #   bin_size表示把360分为多少边
    #---------------------------#
    def __init__(self, img, cell_size=16, bin_size=8):
        self.img = img
        self.img = np.sqrt(img / np.max(img))
        self.img = img * 255
        self.cell_size = cell_size
        self.bin_size = bin_size
        self.angle_unit = 360 / self.bin_size
    #---------------------------#
    #   获取hog向量和图片
    #---------------------------#
    def extract(self):
        # 获得原图的shape
        height, width = self.img.shape
        # 计算原图的梯度大小
        gradient_magnitude, gradient_angle = self.global_gradient()
        gradient_magnitude = abs(gradient_magnitude)

        # cell_gradient_vector用来保存每个细胞的梯度向量
        cell_gradient_vector = np.zeros((int(height / self.cell_size), int(width / self.cell_size), self.bin_size))
        height_cell,width_cell,_ = np.shape(cell_gradient_vector)
        #---------------------------#
        #   计算每个细胞的梯度直方图
        #---------------------------#
        for i in range(height_cell):
            for j in range(width_cell):
                # 获取这个细胞内的梯度大小
                cell_magnitude = gradient_magnitude[i * self.cell_size:(i + 1) * self.cell_size,
                                 j * self.cell_size:(j + 1) * self.cell_size]
                # 获得这个细胞内的角度大小
                cell_angle = gradient_angle[i * self.cell_size:(i + 1) * self.cell_size,
                             j * self.cell_size:(j + 1) * self.cell_size]
                # 转化为梯度直方图格式
                cell_gradient_vector[i][j] = self.cell_gradient(cell_magnitude, cell_angle)

        # hog图像
        hog_image = self.render_gradient(np.zeros([height, width]), cell_gradient_vector)
        hog_vector = []
        # block为2x2
        for i in range(height_cell - 1):
            for j in range(width_cell - 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, hog_image
    #---------------------------#
    #   计算原图的梯度大小
    #   角度大小
    #---------------------------#
    def global_gradient(self):
        gradient_values_x = cv2.Sobel(self.img, cv2.CV_64F, 1, 0, ksize=5)
        gradient_values_y = cv2.Sobel(self.img, cv2.CV_64F, 0, 1, ksize=5)
        gradient_magnitude = cv2.addWeighted(gradient_values_x, 0.5, gradient_values_y, 0.5, 0)
        gradient_angle = cv2.phase(gradient_values_x, gradient_values_y, angleInDegrees=True)
        return gradient_magnitude, gradient_angle
    #---------------------------#
    #   分解角度信息到
    #   不同角度的直方图上
    #---------------------------#
    def cell_gradient(self, cell_magnitude, cell_angle):
        orientation_centers = [0] * self.bin_size
        for i in range(cell_magnitude.shape[0]):
            for j in range(cell_magnitude.shape[1]):
                gradient_strength = cell_magnitude[i][j]
                gradient_angle = cell_angle[i][j]
                min_angle, max_angle, mod = self.get_closest_bins(gradient_angle)
                orientation_centers[min_angle] += (gradient_strength * (1 - (mod / self.angle_unit)))
                orientation_centers[max_angle] += (gradient_strength * (mod / self.angle_unit))
        return orientation_centers
    #---------------------------#
    #   计算每个像素点所属的角度
    #---------------------------#
    def get_closest_bins(self, gradient_angle):
        idx = int(gradient_angle / self.angle_unit)
        mod = gradient_angle % self.angle_unit
        return idx, (idx + 1) % self.bin_size, mod
    #---------------------------#
    #   将梯度直方图进行绘图
    #---------------------------#
    def render_gradient(self, image, cell_gradient):
        cell_width = self.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 = self.angle_unit
                for magnitude in cell_grad:
                    angle_radian = math.radians(angle)
                    x1 = int(x * self.cell_size + magnitude * cell_width * math.cos(angle_radian))
                    y1 = int(y * self.cell_size + magnitude * cell_width * math.sin(angle_radian))
                    x2 = int(x * self.cell_size - magnitude * cell_width * math.cos(angle_radian))
                    y2 = int(y * self.cell_size - magnitude * cell_width * math.sin(angle_radian))
                    cv2.line(image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
                    angle += angle_gap
        return image

img = cv2.imread('timg.jpg', cv2.IMREAD_GRAYSCALE)
hog = Hog_descriptor(img, cell_size=20, bin_size=12)
vector, image = hog.extract()
plt.imshow(image, cmap=plt.cm.gray)
plt.show()

实现效果为:
在这里插入图片描述

  • 35
    点赞
  • 178
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bubbliiiing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值