【opencv学习笔记】HOG+SVM特征点检测
作者:互联网
1、HOG特征:
方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。需要提醒的是,HOG+SVM进行行人检测的方法是法国研究人员Dalal在2005的CVPR上提出的,而如今虽然有很多行人检测算法不断提出,但基本都是以HOG+SVM的思路为主。
2、SIFT、HOG、LBP
SIFT、HOG、LBP,这三者都属于局部特征。
一、三者原理上的区别
- SIFT:Scale-Invariant Feature Taransform,尺度不变特征变换。
尺度空间的极值检测:搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和旋转鲁棒性较强的点。
特征点定位:在每个候选位置上,通过一个拟合精细模型(尺度空间DoG函数进行曲线拟合)来确定位置尺度,关键点的选取依据他们的稳定程度。
特征方向赋值:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
特征点描述:在每个特征点周围的领域内,在选定的尺度上测量图像的局部梯度,计算块内梯度直方图,生成具有独特性的向量。 - HOG:Histogram of Oriented Gradient,方向梯度直方图。
颜色空间归一化:为了减少光照因素的影响, 首先需要将整个图像归一化。因为颜色信息作用不大,通常先转化为灰度图。
梯度计算:计算图像横坐标和纵坐标方向的梯度,并据此计算每个像素位置的梯度方向值;求导操作不仅能捕获轮廓,还能进一步弱化光照的影响。
梯度方向直方图:将图像分成若干个cell,对cell内每个像素用梯度方向在直方图中进行加权投影(映射到固定的角度范围),就可以得到这个cell的梯度直方图。
将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。
组成特征:将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor。 - LBP:Local Binary Pattern,局部二值模式。
原始的LBP算子定义在像素33的邻域内,以邻域中心像素为阈值,相邻的8个像素的灰度值与邻域中心的像素值进行比较,若周围像素大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,33邻域内的8个点经过比较可产生8位二进制数,将这8位二进制数依次排列形成一个二进制数字,这个二进制数字就是中心像素的LBP值,LBP值共有28种可能,因此LBP值有256种。中心像素的LBP值反映了该像素周围区域的纹理信息。
3、HOG特征提取算法的实现过程:
大概过程:
HOG特征提取方法就是将一个image(你要检测的目标或者扫描窗口):
1)灰度化(将图像看做一个x,y,z(灰度)的三维图像);
2)采用Gamma(可以取1/2)校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;
3)计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。
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了。这个就是最终的可供分类使用的特征向量了。
总的来说就是计算方向梯度直方图
那么该怎么计算呢?
1、预处理
通常把图像调整成64*128。这里在opencv可以直接用resize()函数既可;
2、计算梯度图像
可以直接用OpenCV里面的kernel大小为1的Sobel算子来计算。
分别计算x、y方向的梯度;
Mat gx, gy;
Sobel(img, gx, CV_32F, 1, 0, 1);
Sobel(img, gy, CV_32F, 0, 1, 1);
接着,用下面的公式来计算梯度的幅值g和方向theta:
// C++ Calculate gradient magnitude and direction (in degrees)
Mat mag, angle;
cartToPolar(gx, gy, mag, angle, 1);
x轴方向的梯度主要凸显了垂直方向的线条,y轴方向的梯度凸显了水平方向的梯度,梯度幅值凸显了像素值有剧烈变化的地方。可以通过梯度图像轻易的寻找到人体
在每个像素点,都有一个幅值(magnitude)和方向,对于有颜色的图片,会在三个channel上都计算梯度。那么相应的幅值就是三个channel上最大的幅值,角度(方向)是最大幅值所对应的角。
3、在8*8像素的网格(cell)中计算梯度直方图
将图片分割成88的网格,每四个方格为一个块。为什么要用8 * 8?用特征描述子的一个主要原因是它提供了一个紧凑(compact)/压缩的表示。一个88的图像有883=192个像素值,每个像素有两个值(幅值magnitude和方向direction,三个channel取最大magnitude那个),加起来就是882=128,后面我们会看到这128个数如何用一个9个bin的直方图来表示成9个数的数组。
直方图是有9个bin的向量,代表的是角度0,20,40,60…160。
中间: 一个网格用箭头表示梯度 右边: 这个网格用数字表示的梯度
中间这个图的箭头是梯度的方向,长度是梯度的大小,可以发现箭头的指向方向是像素强度变化方向,幅值是强度变化的大小。
下一步就是为这些8*8的网格创建直方图,直方图包含了9个bin来对应0,20,40,…160这些角度。
下面这张图解释了这个过程。我们用了上一张图里面的那个网格的梯度幅值和方向。根据方向选择用哪个bin, 根据幅值来确定这个bin的大小。先来看蓝色圆圈圈出来的像素点,它的角度是80,幅值是2,所以它在第五个bin里面加了2,再来看红色的圈圆圈圈出来的像素点,它的角度是10,幅值是4,因为角度10介于0-20度的中间(正好一半),所以把幅值一分为二地放到0和20两个bin里面去。
梯度直方图
这里有个细节要注意,如果一个角度大于160度,也就是在160-180度之间,我们知道这里角度0,180度是一样的,所以在下面这个例子里,像素的角度为165度的时候,要把幅值按照比例放到0和160的bin里面去。
把这8*8的cell里面所有的像素点都分别加到这9个bin里面去,就构建了一个9-bin的直方图,上面的网格对应的直方图如下:
4、16*16块归一化
我们希望我们的特征描述子可以和光线变换无关,所以我们就想让我们的直方图归一化从而不受光线变化影响。
先考虑对向量用l2归一化的步骤是:
v = [128, 64, 32]
[(128^2) + (64^2) + (32^2) ]^0.5=146.64
把v中每一个元素除以146.64得到[0.87,0.43,0.22]
考虑另一个向量2*v,归一化后可以得到向量依旧是[0.87, 0.43, 0.22]。你可以明白归一化是把scale给移除了
5、计算HOG特征向量
为了计算这整个patch的特征向量,需要把36*1的向量全部合并组成一个巨大的向量。向量的大小可以这么计算:
我们有多少个16*16的块?水平7个,垂直15个,总共有7*15=105次移动。
每个16*16的块代表了(4个8*8 每个小cell中有9个bin)36*1的向量。所以把他们放在一起也就是36*105=3780维向量。
可视化HOG
通常HOG特征描述子是画出88网格中91归一化的直方图,见下图。你可以发现直方图的主要方向捕捉了这个人的外形,特别是躯干和腿。
行人检测(HOG+SVM)
原理可以查看:https://blog.csdn.net/qq_26898461/article/details/46786033
函数代码:
1、HOG构造函数
C++: gpu::HOGDescriptor::HOGDescriptor(Size win_size=Size(64, 128),
Size block_size=Size(16, 16),
Size block_stride=Size(8, 8),
Size cell_size=Size(8, 8),
int nbins=9,
double win_sigma=DEFAULT_WIN_SIGMA,
double threshold_L2hys=0.2,
bool gamma_correction=true,
int nlevels=DEFAULT_NLEVELS
)
参数注释:
<1> win_size:检测窗口大小。
<2> block_size:块大小,目前只支持Size(16, 16)。
<3> block_stride:块的滑动步长,大小只支持是单元格cell_size大小的倍数。
<4> cell_size:单元格的大小,目前只支持Size(8, 8)。
<5> nbins:直方图bin的数量(投票箱的个数),目前每个单元格Cell只支持9个。
<6> win_sigma:高斯滤波窗口的参数。
<7> threshold_L2hys:块内直方图归一化类型L2-Hys的归一化收缩率
<8> gamma_correction:是否gamma校正
<9> nlevels:检测窗口的最大数量
2、getDefaultPeopleDetector 函数
(1)作用:获取行人分类器(默认检测窗口大小)的系数(获得3780维检测算子)
(2)函数原型:
C++: static vector gpu::HOGDescriptor::getDefaultPeopleDetector()
3、setSVMDetector 函数
(1)作用:设置线性SVM分类器的系数
(2)函数原型:
C++: void gpu::HOGDescriptor::setSVMDetector(const vector& detector)
4、detect 函数
(1)作用:用没有多尺度的窗口进行物体检测
(2)函数原型:
C++: void gpu::HOGDescriptor::detect(const GpuMat& img,
vector& found_locations,
double hit_threshold=0,
Size win_stride=Size(),
Size padding=Size()
)
(3)参数注释
<1>img:源图像。只支持CV_8UC1和CV_8UC4数据类型。
<2>found_locations:检测出的物体的边缘。
<3>hit_threshold:特征向量和SVM划分超平面的阀值距离。通常它为0,并应由检测器系数决定。但是,当系数被省略时,可以手动指定它。
<4>win_stride:窗口步长,必须是块步长的整数倍。
<5>padding:模拟参数,使得CUP能兼容。目前必须是(0,0)。
5、detect 函数
(1)作用:用没有多尺度的窗口进行物体检测
(2)函数原型:
C++: void gpu::HOGDescriptor::detect(const GpuMat& img,
vector& found_locations,
double hit_threshold=0,
Size win_stride=Size(),
Size padding=Size()
)
(3)参数注释
<1>img:源图像。只支持CV_8UC1和CV_8UC4数据类型。
<2>found_locations:检测出的物体的边缘。
<3>hit_threshold:特征向量和SVM划分超平面的阀值距离。通常它为0,并应由检测器系数决定。但是,当系数被省略时,可以手动指定它。
<4>win_stride:窗口步长,必须是块步长的整数倍。
<5>padding:模拟参数,使得CUP能兼容。目前必须是(0,0)。
代码展示
# include<opencv2\opencv.hpp>
# include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat src, dst;
src = imread("E:/tuku/xingren1.jpg");
if (src.empty()) {
cout << "can't find this picture...";
return -1;
}
imshow("input", src);
namedWindow("out put", 1);
//HOG特征点检测
Mat dst_gray;
resize(src, dst, Size(64, 128));
cvtColor(dst, dst_gray, COLOR_BGR2GRAY);
HOGDescriptor detector(Size(64,128), Size(16, 16), Size(8, 8),Size(8,8),9);
vector<float> descripts;
vector<Point> location;
detector.compute(dst_gray, descripts, Size(0, 0), Size(0, 0), location);
printf("number of HOG descripts:%d\n", descripts.size());
//HOG+SVM行人检测
HOGDescriptor hog = HOGDescriptor();
hog.setSVMDetector(hog.getDefaultPeopleDetector());
vector<Rect> foundlocationgs;
Mat reslut = src.clone();
hog.detectMultiScale(src, foundlocationgs, 0, Size(8, 8), Size(4, 4), 1.05, 2);
for (size_t i = 0; i < foundlocationgs.size(); i++) {
rectangle(reslut, foundlocationgs[i], Scalar(0, 0, 255), 2, 8, 0);
}
imshow("out put", reslut);
waitKey(0);
return 0;
}
效果图展示
标签:HOG,SVM,梯度,像素,cell,opencv,直方图,Size 来源: https://blog.csdn.net/CSDNjpl/article/details/99003682