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
γ>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水平和垂直算子与输入图像卷积计算 d x dx dx、 d y dy dy:
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]的三维向量来说,模长是 12 8 2 + 6 4 2 + 3 2 2 = 146.64 \sqrt{128^2+64^2+32^2}=146.64 1282+642+322=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()