opencv2以後加入了hog相關的內容,並且給出了樣本,用的是法國人Navneet Dalal最早在CVPR2005會議上提出的方法。
先是使用HOG進行People Detection的,已經提供了完整的方法,在peopledetect.cpp中,主要的方法有HOG特徵提取以及訓練還有識別,你可以通過
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());用已經訓練好的模型直接檢測。用hog.detectMultiScale(...)進行檢測。
1.程式說明
#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/objdetect/objdetect.hpp"#include "opencv2/highgui/highgui.hpp"#include <stdio.h>#include <string.h>#include <ctype.h>using namespace cv;using namespace std;//const char* image_filename = "people.jpg";const char* image_filename = "./../2.jpg";void help(){printf("\nDemonstrate the use of the HoG descriptor using\n"" HOGDescriptor::hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());\n""Usage:\n""./peopledetect (<image_filename> | <image_list>.txt)\n\n");}int main(int argc, char** argv){ Mat img; FILE* f = 0; char _filename[1024]; if( argc == 1 ) { printf("Usage: peopledetect (<image_filename> | <image_list>.txt)\n"); //return 0; }if (argc >1){image_filename = argv[1];} img = imread(image_filename);if (!img.data){printf( "Unable to load the image\n" "Pass it as the first parameter: hogpeopledetect <path to people.jpg> \n" );return -1;} if( img.data ) { strcpy(_filename, image_filename); } else { f = fopen(argv[1], "rt"); if(!f) { fprintf( stderr, "ERROR: the specified file could not be loaded\n"); return -1; } } HOGDescriptor hog; hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); namedWindow("people detector", 1); for(;;) { char* filename = _filename; if(f) { if(!fgets(filename, (int)sizeof(_filename)-2, f)) break; //while(*filename && isspace(*filename)) //++filename; if(filename[0] == '#') continue; int l = strlen(filename); while(l > 0 && isspace(filename[l-1])) --l; filename[l] = '\0'; img = imread(filename); } printf("%s:\n", filename); if(!img.data) continue; fflush(stdout); vector<Rect> found, found_filtered; double t = (double)getTickCount(); // run the detector with default parameters. to get a higher hit-rate // (and more false alarms, respectively), decrease the hitThreshold and // groupThreshold (set groupThreshold to 0 to turn off the grouping completely). hog.detectMultiScale(img, found, 0, Size(8,8), Size(32,32), 1.05, 2); t = (double)getTickCount() - t; printf("tdetection time = %gms\n", t*1000./cv::getTickFrequency()); size_t i, j; for( i = 0; i < found.size(); i++ ) { Rect r = found[i]; for( j = 0; j < found.size(); j++ ) if( j != i && (r & found[j]) == r) break; if( j == found.size() ) found_filtered.push_back(r); } for( i = 0; i < found_filtered.size(); i++ ) { Rect r = found_filtered[i]; // the HOG detector returns slightly larger rectangles than the real objects. // so we slightly shrink the rectangles to get a nicer output. 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(), cv::Scalar(0,255,0), 3); } imshow("people detector", img); int c = waitKey(0) & 255; if( c == 'q' || c == 'Q' || !f) break; } if(f) fclose(f); return 0;}
程式碼簡要說明
1) getDefaultPeopleDetector() 獲得3780維檢測運算元(105 blocks with 4 histograms each and 9 bins per histogram there are 3,780 values)
2).cv::HOGDescriptor hog; 建立類的對象 一系列變數初始化
winSize(64,128), blockSize(16,16), blockStride(8,8),
cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),
histogramNormType(L2Hys), L2HysThreshold(0.2), gammaCorrection(true)
3). 調用函數:detectMultiScale(img, found, 0, cv::Size(8,8), cv::Size(24,16), 1.05, 2);
參數分別為待檢映像、返回結果清單、門檻值hitThreshold、視窗步長winStride、映像padding margin、比例係數、門檻值groupThreshold;通過修改參數發現,就所用的某圖片,參數0改為0.01就檢測不到,改為0.001可以;1.05改為1.1就不行,1.06可以;2改為1可以,0.8以下不行,(24,16)改成(0,0)也可以,(32,32)也行
該函數內容如下
(1) 得到層數 levels
某圖片(530,402)為例,lg(402/128)/lg1.05=23.4 則得到層數為24
(2) 迴圈levels次,每次執行內容如下
HOGThreadData& tdata = threadData[getThreadNum()];
Mat smallerImg(sz, img.type(), tdata.smallerImgBuf.data);
調用以下核心函數
detect(smallerImg, tdata.locations, hitThreshold, winStride, padding);
其參數分別為,該比例像、返回結果清單、門檻值、步長、margin
該函數內容如下:
(a)得到補齊映像尺寸paddedImgSize
(b)建立類的對象 HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); 在建立過程中,首先初始化 HOGCache::init,包括:計算梯度 descriptor->computeGradient、得到塊的個數105、每塊參數個數36
(c)獲得視窗個數nwindows,以第一層為例,其視窗數為(530+32*2-64)/8+1、(402+32*2-128)/8+1 =67*43=2881,其中(32,32)為winStride參數,也可用(24,16)
(d)在每個視窗執行迴圈,內容如下
在105個塊中執行迴圈,每個塊內容為:通過getblockFunction ComputeHOG特徵並歸一化,36個數分別與運算元中對應數進行相應運算;判斷105個塊的總和 s >= hitThreshold 則認為檢測到目標
4)主體部分就是以上這些,但很多細節還需要進一步弄清。
詳細的步驟說明看作者的論文吧。
2.存在的誤區
OpenCV內建的分類器是利用Navneet Dalal和Bill Triggs提供的樣本進行訓練的,不見得能適用於你的應用場合。因此,針對你的特定應用情境,很有必要進行重新訓練得到適合你的分類器。
在上一個專題中()曾經提到利用SVM訓練樣本得到分類器並且可以儲存成XML檔案
svm.train( data_mat, res_mat, Mat(), Mat(), param ); //☆☆利用訓練資料和確定的學習參數,進行SVM學習☆☆☆☆ svm.save( "E:/apple/SVM_DATA.xml" );
那這些檔案是否可以直接利用去檢測目標呢?
HOGDescriptor hog1;hog1.load("SVM_DATA.xml");hog1.detectMultiScale(img,found);
答案顯然是否定的,SVM訓練出來的是分類器,但hogdescriptor需要的是一個detector,二者是有本質區別的。
下面貼出上個專題中訓練得到的分類器(xml檔案),其中支援向量太多省略,其中各個參數的含義,不做詳細介紹了,明白SVM的應該很容易理解。
<?xml version="1.0"?><opencv_storage><my_svm type_id="opencv-ml-svm"> <svm_type>C_SVC</svm_type> <kernel><type>RBF</type> <gamma>8.9999999999999997e-002</gamma></kernel> <C>10.</C> <term_criteria><epsilon>1.1920928955078125e-007</epsilon> <iterations>2147483647</iterations></term_criteria> <var_all>1764</var_all> <var_count>1764</var_count> <class_count>2</class_count> <class_labels type_id="opencv-matrix"> <rows>1</rows> <cols>2</cols> <dt>i</dt> <data> 0 1</data></class_labels> <sv_total>5</sv_total> <support_vectors> <_>支援向量省略</_></support_vectors> <decision_functions> <_> <sv_count>5</sv_count> <rho>-2.9438931041848948e-001</rho> <alpha> 3.0069228814749499e-001 4.8593661661382231e-001 3.3096444767386463e-001 3.3785525729586080e-001 -1.4554486097310426e+000</alpha> <index> 0 1 2 3 4</index></_></decision_functions></my_svm></opencv_storage>
detector只是一個向量,可以通過分類器直接轉化。
3.解決方案
關於如何訓練樣本,且利用分類器求取detector的方法,下一篇博文介紹
參考:
OpenCV2.0 peopledetect 學習體會:http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=9146