HOG算子——计算方式,opencv函数详解,行人检测code

      梯度直方图特征(HOG) 是一种对图像局部重叠区域的密集型描述符它通过计算局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。需要提醒的是,HOG+SVM进行行人检测的方法是法国研究人员Dalal2005CVPR上提出的,而如今虽然有很多行人检测算法不断提出,但基本都是以HOG+SVM的思路为主。

        HOG特征是一种局部区域描述符,它通过计算局部区域上的梯度方向直方图来构成人体特征,能够很好地描述人体的边缘。它对光照变化和小量的偏移不敏感。

图像中像素点(x,y)的梯度为

    Dalal提出的Hog特征提取的过程:把样本图像分割为若干个像素的单元(cell),把梯度方向平均划分为9个区间(bin),在每个单元里面对所有像素的梯度方向在各个方向区间进行直方图统计,得到一个9维的特征向量,每相邻的4个单元构成一个块(block),把一个块内的特征向量联起来得到36维的特征向量,用块对样本图像进行扫描,扫描步长为一个单元。最后将所有块的特征串联起来,就得到了人体的特征。例如,对于64*128的图像而言,每2*2的单元(16*16的像素)构成一个块,每个块内有4*9=36个特征,以8个像素为步长,那么,水平方向将有7个扫描窗口,垂直方向将有15个扫描窗口。也就是说,64*128的图片,总共有36*7*15=3780个特征。

在行人检测过程中,除了上面提到的HOG特征提取过程,还包括彩图转灰度,亮度校正等步骤。总结一下,在行人检测中,HOG特征计算的步骤:

(1)将输入的彩图转换为灰度图;

(2)采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);   目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;

(3)计算梯度;主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。

(4)将梯度投影到单元的梯度方向;目的是为局部图像区域提供一个编码,

(5)将所有单元格在块上进行归一化;归一化能够更进一步对光照、阴影和边缘进行压缩,通常,每个单元格由多个不同的块共享,但它的归一化是基于不同块的,所以计算结果也不一样。因此,一个单元格的特征会以不同的结果多次出现在最后的向量中。我们将归一化之后的块描述符就称之为HOG描述符。

(6)收集得到检测空间所有块的HOG特征;该步骤就是将检测窗口中所有重叠的块进行HOG特征的收集,并将它们结合成最终的特征向量供分类使用。

opencv参数详解

     最近要做图像特征提取,可能要用下HOG特征,所以研究了下OpenCV的HOG描述子。OpenCV中的HOG特征提取功能使用了HOGDescriptor这个类来进行封装,其中也有现成的行人检测的接口。然而,无论是OpenCV官方说明文档还是各个中英文网站目前都没有这个类的使用说明,所以在这里把研究的部分心得分享一下。

      首先我们进入HOGDescriptor所在的头文件,看看它的构造函数需要哪些参数。 

  1. CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8),  
  2.     cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),  
  3.     histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true),   
  4.     nlevels(HOGDescriptor::DEFAULT_NLEVELS)  
  5. {}  
  1. CV_WRAP HOGDescriptor(Size _winSize, Size _blockSize, Size _blockStride,  
  2.               Size _cellSize, int _nbins, int _derivAperture=1, double _winSigma=-1,  
  3.               int _histogramNormType=HOGDescriptor::L2Hys,  
  4.               double _L2HysThreshold=0.2, bool _gammaCorrection=false,  
  5.               int _nlevels=HOGDescriptor::DEFAULT_NLEVELS)  
  6. : winSize(_winSize), blockSize(_blockSize), blockStride(_blockStride), cellSize(_cellSize),  
  7. nbins(_nbins), derivAperture(_derivAperture), winSigma(_winSigma),  
  8. histogramNormType(_histogramNormType), L2HysThreshold(_L2HysThreshold),  
  9. gammaCorrection(_gammaCorrection), nlevels(_nlevels)  
  10. {}  
  1. CV_WRAP HOGDescriptor(const String& filename)  
  2. {  
  3.     load(filename);  
  4. }  
  1. HOGDescriptor(const HOGDescriptor& d)  
  2. {  
  3.     d.copyTo(*this);  
  4. }  

      我们看到HOGDescriptor一共有4个构造函数,前三个有CV_WRAP前缀,表示它们是从DLL里导出的函数,即我们在程序当中可以调用的函数;最后一个没有上述的前缀,所以我们暂时用不到,它其实就是一个拷贝构造函数。

      下面我们就把注意力放在前面的构造函数的参数上面吧,这里有几个重要的参数要研究下:winSize(64,128), blockSize(16,16), blockStride(8,8), cellSize(8,8), nbins(9)。上面这些都是HOGDescriptor的成员变量,括号里的数值是它们的默认值,它们反应了HOG描述子的参数。这里做了几个示意图来表示它们的含义。

      窗口大小 winSize

      块大小 blockSize

      胞元大小 cellSize

      梯度方向数 nbins

      nBins表示在一个胞元(cell)中统计梯度的方向数目,例如nBins=9时,在一个胞元内统计9个方向的梯度直方图,每个方向为180/9=20度。

      HOG描述子维度

      在确定了上述的参数后,我们就可以计算出一个HOG描述子的维度了。OpenCV中的HOG源代码是按照下面的式子计算出描述子的维度的。 

  1. size_t HOGDescriptor::getDescriptorSize() const  
  2. {  
  3.     CV_Assert(blockSize.width % cellSize.width == 0 &&  
  4.         blockSize.height % cellSize.height == 0);  
  5.     CV_Assert((winSize.width - blockSize.width) % blockStride.width == 0 &&  
  6.         (winSize.height - blockSize.height) % blockStride.height == 0 );  
  7.     return (size_t)nbins*  
  8.         (blockSize.width/cellSize.width)*  
  9.         (blockSize.height/cellSize.height)*  
  10.         ((winSize.width - blockSize.width)/blockStride.width + 1)*  
  11.         ((winSize.height - blockSize.height)/blockStride.height + 1);  
  12. }  

      参考文献

      OpenCV中的HOG算法来源于Histograms of Oriented Gradients for Human Detection, CVPR 2005。详细的算法可以参考这个文章。

HOG的优缺点
优点:

(1)HOG表示的是边缘(梯度)的结构特征,因此可以描述局部的形状信息;

(2)位置和方向空间的量化一定程度上可以抑制平移和旋转带来的影响;

(3)采取在局部区域归一化直方图,可以部分抵消光照变化带来的影响;

(4)由于一定程度忽略了光照颜色对图像造成的影响,使得图像所需要的表征数据的维度降低了;

(5)而且由于这种分块分单元的处理方法,也使得图像局部像素点之间的关系可以很好得到表征。

缺点:

(1)描述子生成过程冗长,导致速度慢,实时性差;

(2)很难处理遮挡问题;

(3)由于梯度的性质,该描述子对噪点相当敏感


原文链接:

http://blog.csdn.net/carson2005/article/details/7782726  
http://blog.csdn.net/raodotcong/article/details/6239431  

http://blog.sina.com.cn/s/blog_59fabe030101i43v.html

行人检测code

int people_test()
{
	Mat img=imread("D:\\faceData\\web\\3.jpg");;
vector<Rect> found, found_filtered;
cv::HOGDescriptor people_dectect_hog;
//采用默认的已经训练好了的svm系数作为此次检测的模型
people_dectect_hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());
//对输入的图片img进行多尺度行人检测
//img为输入待检测的图片;found为检测到目标区域列表;参数3为程序内部计算为行人目标的阈值,也就是检测到的特征到SVM分类超平面的距离;
//参数4为滑动窗口每次移动的距离。它必须是块移动的整数倍;参数5为图像扩充的大小;参数6为比例系数,即测试图片每次尺寸缩放增加的比例;
//参数7为组阈值,即校正系数,当一个目标被多个窗口检测出来时,该参数此时就起了调节作用,为0时表示不起调节作用。
people_dectect_hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32,32), 1.05, 2);

//从源码中可以看出:
//#define __SIZE_TYPE__ long unsigned int
//typedef __SIZE_TYPE__ size_t;
//因此,size_t是一个long unsigned int类型
size_t i, j;
for (i = 0; i < found.size(); i++ )
{
	Rect r = found[i];

	//下面的这个for语句是找出所有没有嵌套的矩形框r,并放入found_filtered中,如果有嵌套的
	//话,则取外面最大的那个矩形框放入found_filtered中
	for(j = 0; j <found.size(); j++)
		if(j != i && (r&found[j])==r)
			break;
	if(j == found.size())
		found_filtered.push_back(r);
}

//在图片img上画出矩形框,因为hog检测出的矩形框比实际人体框要稍微大些,所以这里需要
//做一些调整
for(i = 0; i <found_filtered.size(); i++)
{
	Rect r = found_filtered[i];
	r.x += cvRound(r.width*0.1);
	r.width = cvRound(r.width*0.8);
	r.y += cvRound(r.height*0.07);
	r.height = cvRound(r.height*0.8);
	rectangle(img, r.tl(), r.br(), Scalar(0, 255, 0), 3);
}
//imwrite("../hog_test_result.jpg", img);
imshow("test",img);
waitKey(0);
return 0;
}


阅读更多
个人分类: code 数字图像处理
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭