OpenCV的HoG特徵點詳解
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啥的。。。一大堆東西。
這裡有三篇很好的文章介紹一下。
這篇文章就是對window,block,cell的解釋
這篇文章是對得到的vector結果數組裡的結果個數進行的分析。
這篇文章是教你如何在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");
}