Opencv之HOG特征与SVM相结合的人体检测(增加自举法)

来源:互联网 发布:淘宝哪些站外网站 编辑:程序博客网 时间:2024/05/17 07:11

Hello~洛基在上一篇关于人体检测的文章末尾提到了自举法,这里科普一下,所谓自举法,即在一个容量为n的原始样本中重复抽取一系列容量也是n的随机样本,并保证每次抽样中每一样本观察值被抽取的概率都为1/n。好像不是很通俗易懂,说人话就是——应用于行人检测中的自举方法是,对于训练好的HOG检测器,将原来进行训练的负样本当做检测样本,利用检测器进行检测,检测出来的图像必定是不包含人体的样本,这些不包含人体的样本集合便成为了自举训练样本,将自举训练样本处理之后重新作为负样本,输入到SVM中进行再一次训练,如此一来得到的检测器会“进化”为错检率更低的进化版检测器。

Of course,自举训练可以进行多次,这样分类的效果可能会更好,不过也不排除,在进行了多次自举训练之后,检测器出现大量漏检情况哦- -所以自举训练的次数,可能并不是越多越好,这是我在进行试验的小总结(认真脸)。

Okay进入正题,也就是代码部分。

#include <iostream>#include <fstream>#include <string>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/objdetect/objdetect.hpp>#include <opencv2/ml/ml.hpp>using namespace std;using namespace cv;#define str "E:\\INRIAPerson\\HardExample\\"int hardExampleCount = 0;//因为在生成setSVMDetector中用到的检测子参数时要用到训练好的SVM的decision_func的protected类型参数alpha和rho,故只能通过继承之后利用函数访问了class MySVM : public CvSVM{public://获得SVM的决策函数中的alpha数组double * get_alpha_vector(){return this->decision_func->alpha;}//获得SVM的决策函数中的rho参数,即偏移量float get_rho(){return this->decision_func->rho;}};

int main(){Mat src;char saveName[256];//剪裁出来的hard example图片的文件名string ImgName;//ifstream fin_detector("HOGDetectorParagram.txt");ifstream fin_imgList("neg.txt");//打开原始负样本图片文件列表        //每次在载入计算好的HOG描述子,然后设置SVM检测器时,都会报错——Assertion failed checkDetectorSize。令人抓狂的一个错误        //于是只能每次都重新根据训练好的SVM.xml,计算HOG描述子,然后再设置SVM检测器了。这个问题留到后面再解决!cout<<"载入训练好的SVM分类器"<<endl;int DescriptorDim;//特征向量维数,即HOG描述子的维数MySVM svm;svm.load("SVM_HOG.xml");DescriptorDim = svm.get_var_count();cout<<"描述子维数:"<<DescriptorDim<<endl;int supportVectorNum = svm.get_support_vector_count();//支持向量的个数cout<<"支持向量个数:"<<supportVectorNum<<endl;//Mat alphaMat = Mat::zeros(1, supportVectorNum, CV_32FC1);//alpha向量,长度等于支持向量个数Mat alphaMat = Mat::zeros(1, supportVectorNum, CV_32FC1);Mat supportVectorMat = Mat::zeros(supportVectorNum, DescriptorDim, CV_32FC1);//支持向量矩阵Mat resultMat = Mat::zeros(1, DescriptorDim, CV_32FC1);//alpha向量乘以支持向量矩阵的结果//将支持向量的数据复制到supportVectorMat矩阵中,共有supportVectorNum个支持向量,每个支持向量的数据有DescriptorDim维(种)for(int i=0; i<supportVectorNum; i++){const float * pSVData = svm.get_support_vector(i);//返回第i个支持向量的数据指针for(int j=0; j<DescriptorDim; j++)supportVectorMat.at<float>(i,j) = pSVData[j];//第i个向量的第j维数据}//将alpha向量的数据复制到alphaMat中//double * pAlphaData = svm.get_alpha_vector();//返回SVM的决策函数中的alpha向量double * pAlphaData = svm.get_alpha_vector();for(int i=0; i<supportVectorNum; i++){alphaMat.at<float>(0,i) = pAlphaData[i];//alpha向量,长度等于支持向量个数}//计算-(alphaMat * supportVectorMat),结果放到resultMat中resultMat = -1 * alphaMat * supportVectorMat;//上一篇博文中有解释为什么是负号,这里不赘述//得到最终的setSVMDetector(const vector<float>& detector)参数中可用的检测子vector<float> myDetector;//将resultMat中的数据复制到数组myDetector中for(int i=0; i<DescriptorDim; i++){myDetector.push_back(resultMat.at<float>(0,i));}myDetector.push_back(svm.get_rho());//最后添加偏移量rho,得到检测子cout<<"检测子维数:"<<myDetector.size()<<endl;//设置HOGDescriptor的检测子HOGDescriptor hog(Size(64,128),Size(16,16),Size(8,8),Size(8,8),9);hog.setSVMDetector(myDetector);//从文件中读入自己训练的SVM参数//float temp;//vector<float> myDetector;//自己的检测器数组//while(!fin_detector.eof())//{//fin_detector >> temp;//myDetector.push_back(temp);//}//cout<<"检测子维数:"<<myDetector.size()<<endl;//设置检测器参数为自己训练的SVM参数//HOGDescriptor hog;//hog.setSVMDetector(myDetector);//上面说过了,如果直接读入之前保存好的HOG描述子txt文件,会一直报错= =no idea to solve itwhile(getline(fin_imgList,ImgName)){cout<<"处理:"<<ImgName<<endl;string fullName = "E:\\INRIAPerson\\Negjpg_undesign\\" + ImgName;src = imread(fullName);Mat img = src.clone();vector<Rect> found;hog.detectMultiScale(src, found, 0, Size(8,8),Size(32,32), 1.05, 2);//处理得到的矩形框for(int i=0; i < found.size(); i++){//将矩形框轮廓限定在图像内部,r的x、y坐标是相对于源图像src来定义的Rect r = found[i];if(r.x < 0)r.x = 0;if(r.y < 0)r.y = 0;if(r.x + r.width > src.cols)r.width = src.cols - r.x;if(r.y + r.height > src.rows)r.height = src.rows - r.y;//将矩形框框出的图片保存为难例Mat hardExampleImg = src(r);//从原图上截取矩形框大小的图片resize(hardExampleImg,hardExampleImg,Size(64,128));//将剪裁出来的图片缩放为64*128大小sprintf(saveName,"hardexample%09d.jpg",hardExampleCount++);//生成hard example图片的文件名imwrite(saveName, hardExampleImg);//保存框出的图片部分,这里没办法添加一个路径名称,所以全部保存在VS项目里了,好乱= =//画矩形框,因为hog检测出的矩形框比实际人体框要稍微大些,所以这里需要做一些调整r.x += cvRound(r.width*0.1);r.width = cvRound(r.width*0.8);rectangle(img, r.tl(), r.br(), Scalar(0,255,0), 2);}imwrite(str+ImgName,img);//将处理过的图像保存下来,这里保存的就是原始负样本加上各种框框之后的结果图片imshow("src",src);waitKey(20);}system("pause");}

这个是经过自举法之后得到的新检测器的成果,是不是很Duang Duang Duang?来对比一下没有通过自举法得到的检测结果:

其实我只利用了一次自举训练,便得到了以上的良好检测效果,因为检测的正负样本基数不够大,所以不敢进行多次自举训练,担心会造成大量漏检。最后值得一提的是,利用原始1000多张负样本进行自举检测,竟然得到了7000+张新的负样本,这说明原来的检测器检测效果堪忧。。。不过好在拥有了自举训练这个关键的idea,使得我们的检测器功能优化了很多~

Last but not least,洛基祝大家新年快乐!大家在刻苦敲代码的同时也别忘了去锻炼身体哦!现在是成都时间2017年1月3日凌晨12点38分

Nighty night~ :-D

1 0