opencv2以后加入了hog相关的内容,并且给出了示例,用的是法国人Navneet Dalal最早在CVPR2005会议上提出的方法。
先是使用HOG进行People Detection的,已经提供了完整的方法,在peopledetect.cpp中,主要的方法有HOG特征提取以及训练还有识别,你可以通过 hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());用已经训练好的模型直接检测。用hog.detectMultiScale(...)进行检测。
#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个块中执行循环,每个块内容为:通过getblock函数计算HOG特征并归一化,36个数分别与算子中对应数进行相应运算;判断105个块的总和 s >= hitThreshold 则认为检测到目标
4)主体部分就是以上这些,但很多细节还需要进一步弄清。
详细的步骤说明看作者的论文吧。
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只是一个向量,可以通过分类器直接转化。
关于如何训练样本,且利用分类器求取detector的方法,下一篇博文介绍
参考:
OpenCV2.0 peopledetect 学习体会:http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=9146