zoukankan      html  css  js  c++  java
  • 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)

    某些结果如下:

     

     

     



















  • 相关阅读:
    记一次unity3d游戏的misc
    【靶场练习_upload-labs复现】Pass01-020
    bugku | login2(SKCTF) 200
    bugku | sql注入2
    buuctf | [强网杯 2019]随便注
    Linux 中 cp 命令(文件复制)
    linux之cp/scp命令+scp命令详解
    StatsD 的使用小结
    深入 webpack 打包机制
    利用webpack打包自己的第一个Vue组件库
  • 原文地址:https://www.cnblogs.com/tangjunjun/p/12089065.html
Copyright © 2011-2022 走看看