HOG即histogram of oriented gradient, 是用于目标检测的特征描述子,该技术将图像局部出现的方向梯度次数进行计数,该方法和边缘方向直方图、scale-invariant feature transform类似,不同的是hog的计算基于一致空间的密度矩阵来提高准确率。Navneet Dalal and Bill Triggs首先在05年的CVPR中提出HOG,用于静态图像or视频的行人检测。
HOG特征原理:
HOG的核心思想是所检测的局部物体外形能够被光强梯度或边缘方向的分布所描述。通过将整幅图像分割成小的连接区域(称为cells),每个cell生成一个方向梯度直方图或者cell中pixel的边缘方向,这些直方图的组合可表示出(所检测目标的目标)描述子。为改善准确率,局部直方图可以通过计算图像中一个较大区域(称为block)的光强作为measure被对比标准化,然后用这个值(measure)归一化这个block中的所有cells.这个归一化过程完成了更好的照射/阴影不变性。
与其他描述子相比,HOG得到的描述子保持了几何和光学转化不变性(除非物体方向改变)。因此HOG描述子尤其适合人的检测。
通俗的讲:
HOG特征提取方法就是将一个image:
1. 灰度化(将图像看做一个x,y,z(灰度)的三维图像)
2. 划分成小cells(2*2)
3. 计算每个cell中每个pixel的gradient(即orientation)
4. 统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor
再小谈下Hog、SIFT与PCA-SIFT的应用与区别:
Hog没有旋转和尺度不变性,因此计算量小;而SIFT中每个feature需要用128维的向量来描述,因此计算量相对很大。
那么行人检测中怎么应用HOG呢?
对于解决Scale-invariant的问题:将图片进行不同尺度的缩放,就相当于对模板进行不同尺度scale的缩放
对于解决Rotation-invariant 的问题:建立不同方向的模版(一般取15*7的)进行匹配
总的来说,就是在不同尺度上的图像进行不同方向的模板(15*7)匹配,每个点形成一个8方向的梯度描述。
SIFT由于其庞大计算量不用与行人检测,而PCA-SIFT的方法过滤掉很多维度的信息,只保留20个主分量,因此只适用于行为变化不大的物体检测。
method | Time | Scale | Rotation | Blur | Illumination | Affine |
Sift | common | best | best | common | common | good |
PCA-sift | good | good | good | best | good | best |
Surf | best | common | common | good | best | good |
hog是一个基于梯度的直方图提取算法,用于人体检测十分有效。在opencv2.2+版本里面已经实现。
封装在HOGDescriptor类里。
hog其实就是对一副图片的指定大小区域进行梯度统计。可以直接调用。opencv把它过于复杂化了,用的时候分什么window,block,cell啥的。。。一大堆东西。
这里有三篇很好的文章介绍一下。
http://blog.csdn.net/raocong2010/archive/2011/03/11/6239431.aspx
这篇文章就是对window,block,cell的解释
http://gz-ricky.blogbus.com/logs/85326280.html
这篇文章是对得到的vector结果数组里的结果个数进行的分析。
http://www.cnblogs.com/Anykong/archive/2011/04/06/anykong_opencv1.html
这篇文章是教你如何在vs2010下安装opencv2.2 非常不错的一篇文章。我原来写sift的时候是用的vc6+opencv1.0。
不过有个地方要注意的是:这篇文章里说的附加依赖项中只让你加上基本的几个。如果你要用hog,必须再加上opencv_objdetect220d.lib
看文件名字就知道是什么。。。
好了背景资料介绍完了,给出个例子,就是我的main函数 放出来做个示范。
- int _tmain(int argc, char** argv)
- {
- Mat trainImg; //需要分析的图片
- trainImg=imread("1.jpg",1); //读取图片
- HOGDescriptor *hog=new HOGDescriptor(cvSize(3,3),cvSize(3,3),cvSize(5,10),cvSize(3,3),9); //具体意思见参考文章1,2
- vector<float>descriptors;//结果数组
- hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //调用计算函数开始计算
- printf("%d\n",descriptors.size()); //打屏一下结果数组的大小,看看是否符合文献2的预估, 发现完全一样,那篇文章很给力
- sift("1.jpg"); //这是我改写的sift...
- for (int i=0;i<kp.size();i++) { //这个循环就是我用来提前特征点附近3*3区域的梯度信息统计
- printf("keypoint %d at %f %f\n",i,kp[i].first,kp[i].second);
- if (kp[i].first==picw) kp[i].first--;
- if (kp[i].first==0) kp[i].first=1;
- if (kp[i].second==pich) kp[i].second--;
- if (kp[i].second==0) kp[i].second=1;
- int pos=(kp[i].second-1)*(picw-2)+kp[i].first-1;
- for (int j=0;j<9;j++) {
- res[j]+=descriptors[pos*9+j];
- }
- }
- puts("result:");
- for (int i=0;i<9;i++) printf("%lf ",res[i]); //结果以文字输出。。。
- puts("");
- IplImage * respic; //结果以直方图输出,里面有个res.jpg是我画的直方图背景图,没有这个图跑不了程序,把从这以下代码注释掉吧
- if ((respic = cvLoadImage("res.jpg", 1)) == 0) return -1;
- double maxx=0;
- for (int i=0;i<9;i++) if (maxx<res[i]) maxx=res[i];
- for (int i=0;i<9;i++) cvRectangle(respic, cvPoint(150+51*i,(maxx-res[i])/maxx*(352-77)+77),
- cvPoint(201+51*i,351), CV_RGB(0, 0, 0),
- 1, 8, 0);
- CvFont font;
- cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0 );
- itoa((int)maxx,outs,10);
- cvPutText(respic,outs,cvPoint(53,83),&font,CV_RGB(0, 0, 0));
- cvNamedWindow("image1",1);
- cvShowImage("image1", respic);
- cvWaitKey(0);
- cvDestroyWindow("image1");
- }
int _tmain(int argc, char** argv)
{
Mat trainImg; //需要分析的图片
trainImg=imread("1.jpg",1); //读取图片
HOGDescriptor *hog=new HOGDescriptor(cvSize(3,3),cvSize(3,3),cvSize(5,10),cvSize(3,3),9); //具体意思见参考文章1,2
vector<float>descriptors;//结果数组
hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //调用计算函数开始计算
printf("%d\n",descriptors.size()); //打屏一下结果数组的大小,看看是否符合文献2的预估, 发现完全一样,那篇文章很给力
sift("1.jpg"); //这是我改写的sift...
for (int i=0;i<kp.size();i++) { //这个循环就是我用来提前特征点附近3*3区域的梯度信息统计
printf("keypoint %d at %f %f\n",i,kp[i].first,kp[i].second);
if (kp[i].first==picw) kp[i].first--;
if (kp[i].first==0) kp[i].first=1;
if (kp[i].second==pich) kp[i].second--;
if (kp[i].second==0) kp[i].second=1;
int pos=(kp[i].second-1)*(picw-2)+kp[i].first-1;
for (int j=0;j<9;j++) {
res[j]+=descriptors[pos*9+j];
}
}
puts("result:");
for (int i=0;i<9;i++) printf("%lf ",res[i]); //结果以文字输出。。。
puts("");
IplImage * respic; //结果以直方图输出,里面有个res.jpg是我画的直方图背景图,没有这个图跑不了程序,把从这以下代码注释掉吧
if ((respic = cvLoadImage("res.jpg", 1)) == 0) return -1;
double maxx=0;
for (int i=0;i<9;i++) if (maxx<res[i]) maxx=res[i];
for (int i=0;i<9;i++) cvRectangle(respic, cvPoint(150+51*i,(maxx-res[i])/maxx*(352-77)+77),
cvPoint(201+51*i,351), CV_RGB(0, 0, 0),
1, 8, 0);
CvFont font;
cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0 );
itoa((int)maxx,outs,10);
cvPutText(respic,outs,cvPoint(53,83),&font,CV_RGB(0, 0, 0));
cvNamedWindow("image1",1);
cvShowImage("image1", respic);
cvWaitKey(0);
cvDestroyWindow("image1");
}
hift代码:http://www.cvchina.info/codes/
Reference:http://blog.csdn.net/abcjennifer/article/details/7365651