CV下-HOG特征描述算子


在深度学习之前非常流行的图像特征提取技术–方向梯度直方图,简称HOG特征。在行人检测领域获得极大成功。学习HOG特征的思想有助于我们很好了解传统图像特征描述和图像识别方法。
学习内容:

  • 使用OpenCV的HOG算法实现行人检测

1 HOG算法

1.1 HOG特征简介

HOG特征是一种图像局部特征,其基本思想是对图像局部的梯度幅值和反向进行投票统计,形成基于梯度特性的直方图,然后将局部特征拼接起来作为总特征。局部特征在这里指的的是将图像划分为多个子块(Block),每个Block内的特征进行联合以形成最终的特征。、
HOG+SVM的工作流程如下:
输入图像---->Gamma矫正----->计算图像梯度—>计算梯度直方图—>Block特征归一化---->得到HOG特征—SVM
首先对输入的图片进行预处理,然后计算像素点的梯度特性,包括梯度幅度和梯度方向。然后投票形成梯度直方图,然后对blocks进行normalize,最后收集到HOG的feature(其实是一行多微的vector)放到SVM里进行监督学习,从而实现行人的检测。

1.2 HOG特征原理

预处理包括灰度化和Gamma变换。
灰度处理是可选操作(一般都要进行灰度处理),因为灰度图像和彩色图像都可以用于计算梯度图。对于彩色图像,先对三通道颜色值分别计算梯度,然后梯度值取最大的那个作为该像素的梯度。
然后进行伽马矫正,调节图像对比度,减少光照对图像的影响(包括光照不均和局部阴影),使过曝或者欠曝的的图像恢复正常,更接近人眼看到的图像。
伽马矫正公式: ,I表示图像(矩阵),gamma表示幂指数,、
该函数图像如下图所示:

由图像可知,当gamma取不同的值对应的输入输出曲线

  • 当gamma<1时,输入图像的低灰度区域动态范围变大,进而图像低灰度值区域对比度得以增强;在高灰度值区域,动态范围变小,进而图像高灰度值区域对比度得以降低。最终,图像整体的灰度变亮。
  • γ>1\gamma>1时,输入图像的高灰度值区域动态范围变小,进而图像低灰度值区域对比度得以降低;在高灰度值区域,动态范围变大,进而图像高灰度值区域对比度得以增强。 最终,图像整体的灰度变暗。
    代码实现
# 导入库函数
import cv2 
import numpy as np
from matplotlib import pyplpt as plt

# 加载图像和灰度化处理
img=cv2.imread("*.png",0)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

img2=np.power(img/float(np.max(img),1/2,2))
plt.imshow(omg2)
plt.axis('off')
plt.show()

1.3 计算图像梯度

为了得到梯度直方图,首先需要计算图像水平方向和垂直方向梯度。一般使用特定的卷积核对图像滤波实现,可选用的卷积模板有:sobel算子,prewitt算子核Roberts模板等。

  • 一般采用soble算子,OpenCV也是如此,利用soble水平和垂直算子与输入图像卷积计算dxdxdydy

dx = f(x,y)* Sobelx(x,y)
dy = f(x,y)*Sobel(x,y)

进一步可以得到图像梯度的幅值:
M(x,y)=(d2(x,y)+d2y(x,y))^1/2
为了简化计算,幅度可以做如下近似:
M(x,y)= | d^2(x,y)| + |d^2(x,y) |
角度为:
这里需要注意的是:梯度方向和图像边缘方向是互相正交的。

python 实现代码:

import cv2 
import numpy as np 
# read image 
img =cv2.imread('*.ipg')
img = np.float32(img)/ 255.0 # 归一化

# 计算x和y方向的梯度
gx = cv2.Sobel(img,cv2.CV_32F,1,0,ksize-1)
gy = cv2.Sobel(img,cv2.CV_32F,0,1,ksize-1)

# 计算合梯度的幅值和方向(角度)
mag,angle = cv2.cartToPolar(gx,gy,angleInDegrees=True) 


1.4 计算梯度直方图

经过上一步计算,每一个像素点都会两个值:梯度幅值/梯度方向。

在这一步中,图像被分成若干个8×8的cell,例如我们将图像resize至64x128的大小,那么这幅图像就被划分为8x16个8x8的cell单元,并为每个8×8的cell计算梯度直方图。当然,cell的划分也可以是其他值:16x16,8x16等,根据具体的场景确定。
在计算梯度直方图,我们为什么将图像分成若干个cell?
这是因为如果对一整张梯度图逐像素计算,其中的有效特征是非常稀疏的,不但运算量大,而且会受到一些噪声干扰。于是我们就使用局部特征描述符来表示一个更紧凑的特征,计算这种局部cell上的梯度直方图更具鲁棒性。
【鲁棒性】:
左图是衣服64x128的图像,被划分为8x16个8x8的cell;中间的图像表示一个cell中的梯度矢量,箭头朝向代表梯度方向,箭头长度代表梯度大小。

1.5 Block归一化

HOG特征将8x8的一个局部区域作为一个cell,再以2x2个cell作为一组,称为一个block,也就是说一个block表示16x16的区域。
归一化的目的是为了降低光照的影响,因为梯度对整体光照非常敏感,比如通过将所有像素值除以2来使图像变暗,那么梯度幅值将减小一半,因此直方图中的值也将减小一半,我们就需要将直方图“归一化”。
归一化的方法有很多:L1-norm、L2-norm、max/min等等,一般选择L2-norm。

  • 计算方法
    例如对于一个[128,64,32]的三维向量来说,模长是1282+642+322=146.64\sqrt{128^2+64^2+32^2}=146.64,这叫做向量的L2范数。将这个向量的每个元素除以146.64就得到了归一化向量 [0.87, 0.43, 0.22]。
    采用同样的方法,一个cell有一个梯度方向直方图,包含9个数值,一个block有4个cell,那么一个block就有4个梯度方向直方图,将这4个直方图拼接成长度为36的向量,然后对这个向量进行归一化。
    而每一个block将按照上图滑动的方式进行重复计算,直到整个图像的block都计算完成。
    基于OpenCV实现
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

if __name__ == '__main__':
    src = cv.imread("*.jpg")
    cv.imshow("input", src)
    
    hog = cv.HOGDescriptor()
    hog.setSVMDetector(cv.HOGDescriptor_getDefaultPeopleDetector())
    # Detect people in the image
    (rects, weights) = hog.detectMultiScale(src,
                                            winStride=(2,4),
                                            padding=(8, 8),
                                            scale=1.2,
                                            useMeanshiftGrouping=False)
    for (x, y, w, h) in rects:
        cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv.imshow("hog-detector", src)
    cv.imwrite("hog-detector.jpg",src)
    cv.waitKey(0)
    cv.destroyAllWindows()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章