一般用到hog做特征描述子的情况就是,这个window只包含目标物体,很少含其他物体。例如经过目标检测提取的行人roi图像,我们用这只包含人的roi图像进行hog特征描述子提取。当然,我们可以直接把这roi部分的图像直接resize为一个行向量,作为SVM的输入,但是这样会有很多干扰,如果进一步提取hog的话,就会有很多边缘细节,能让svm更快收敛,并且模型识别度更好。
cv::HOGDescriptor
类的构造函数的各参数的定义:
CV_WRAP HOGDescriptor() :
winSize(64,128), // detect window
blockSize(16,16), // block 大小
blockStride(8,8), // overlap block的滑动步长
cellSize(8,8), // cell 大小
nbins(9), // 直方图的bin个数
derivAperture(1), // 微分算子核
winSigma(-1), // 在window上进行高斯加权
histogramNormType(HOGDescriptor::L2Hys), // 直方图归一化类型
L2HysThreshold(0.2), // L2-norm followed by clipping (limiting the maximum values of v to 0.2) and renormalising
gammaCorrection(true), // Gamma校正,去除光照影响
nlevels(HOGDescriptor::DEFAULT_NLEVELS) // 分层数
以下是一个hog_descriptor.xml
的举例:
<?xml version="1.0"?>
<opencv_storage>
<hog_descriptor type_id="opencv-object-detector-hog">
<winSize>
16 16</winSize>
<blockSize>
8 8</blockSize>
<blockStride>
4 4</blockStride>
<cellSize>
4 4</cellSize>
<nbins>9</nbins>
<derivAperture>1</derivAperture>
<winSigma>2.</winSigma>
<histogramNormType>0</histogramNormType>
<L2HysThreshold>2.0000000000000001e-01</L2HysThreshold>
<gammaCorrection>1</gammaCorrection>
<nlevels>64</nlevels>
<signedGradient>1</signedGradient></hog_descriptor>
</opencv_storage>
下面我们来介绍HOG特征的提取过程:
1.图像预处理
- 灰度化,因为Hog特征提取的是纹理特征,颜色信息不起作用,所以现将彩色图转为灰度图;
- 归一化,为了提高检测器对光照等干扰因素的鲁棒性,需要对图像进行
Gamma校正
,以完成对整个图像的归一化,目的是调节图像的对比度,降低局部光照和阴影所造成的影响,同时也可以降低噪音的干扰;
未经gamma校正
和经过gamma校正保存图像信息:未经gamma校正的情况下,低灰度时,有较大范围的灰度值被保存成同一个值,造成信息丢失;同时高灰度值时,很多比较接近的灰度值却被保存成不同的值,造成空间浪费。经过gamma校正后,改善了存储的有效性和效率。
γ值以1为分界,值越小,对图像低灰度部分的扩展作用就越强,值越大,对图像高灰度部分的扩展作用就越强,通过不同的γ值,就可以达到增强低灰度或高灰度部分细节的作用。
伽马变换对于图像对比度偏低,并且整体亮度值偏高(对于于相机过曝)情况下的图像增强效果明显。
先把像素值 f ( x , y ) f(x,y) f(x,y)进行归一化,把 f ( x , y ) f(x,y) f(x,y)变为(0~1)范围,可以是 f ( x , y ) 255 \frac{f(x,y)}{255} 255f(x,y), 然后进行gamma校正:
f γ ( x , y ) = 255 ∗ ( f ( x , y ) 255 ) γ , . . . . . . . . . . . . . . . . f ( x , y ) → ( 0 , 255 ) f_{\gamma}(x,y)=255*{(\frac{f(x,y)}{255})}^{\gamma} ,................f(x,y)\to(0,255) fγ(x,y)=255∗(255f(x,y))γ,................f(x,y)→(0,255)
乘以255是为了恢复为原来的像素值。
float fGamma=0.5;
pixel_value = std::pow((float)((pixel_value/255.0), fGamma) * 255.0;
即gamma校正就是对像素值做指数运算。
γ \gamma γ一般取1/2, 我们一般使用归一化后的灰度值进行gamma校正。
2.统计细胞单元(Cell)的梯度方向直方图:
将图像划分成小的Cell,将梯度方向映射到180度的范围内,将像素的梯度幅值作为权值进行投影,用梯度方向决定向哪一维进行投影;
假如该像素的梯度方向为20度,梯度幅值为10,那么直方图的第二维就加10
。
下图是一个细胞单元内的方向梯度直方图,角度分辨率是在180度的范围内,以20度等分,即一个细胞单元的HOG特征是一个9维的向量。
3.统计块(Block)的梯度方向直方图:
统计每个细胞单元内的梯度直方图,形成每个细胞单元的描述子,由cell组成更大的描述子,称为块,将一个块内四个cell的特征向量串联
起来就构成了该块的梯度方向直方图,按照一个细胞单元是9维的Hog特征,则一个Block块的Hog特征为4x9=36维。
4.梯度做局部对比度归一化:
由于局部光照的变化,以及前景背景对比度的变化,使得梯度强度的变化范围非常大,这就需要对梯度做局部对比度归一化。这里的策略是针对每个block块进行对比度归一化,一般使用L2-norm。
5.统计窗口(Window)的梯度方向直方图:
只需要将窗口内所有块的Hog特征向量串联
起来就得到了Window的Hog特征;
对于一个window窗口,会分成好多个block,是通过设置block滑动的步长stride来求出来的,
例如window是16x16 pixel大小,Block是8x8 pixel大小,blockStride步长是4,那么最终一个window会有(16-8)/4+1=3,即3x3=9个block,那么一个window就是9x36=324维向量,这就是最终的hog描述子。
6.统计整幅图像的梯度方向直方图:
一幅图像可以无重叠的划分为多个Window,这时将所有Window的特征向量串联起来就是整幅图像的Hog特征了,如果Window的大小和图像的大小相同,那么Window的Hog特征就是整幅图像的Hog特征,这也是最终分类使用的特征向量。
为什么要先将图像划分成Cell,再将Cell组成更大的块?
这里的Cell相当于局部特征提取,Block块是几个Cell的组合,是比Cell更大的局部特征,事实上,block块之间是有重叠的,也就是说,每个细胞单元的直方图都会被多次用于最终的描述子的计算。相互重叠是为了区分更细的局部特征,最后由于局部光照和背景的变化,再对块内的特征进行归一化。此方法看起来有冗余,但可以显著的提升性能。实验表明:小尺度梯度,精细的方向采样,相对粗糙的空间采样,和重叠描述子块中高质量的局部对比度归一化都对好的检测效果至关重要。
本文参考了下面的博客,链接:https://www.jianshu.com/p/6f69c751e9e7