zoukankan      html  css  js  c++  java
  • Hog SVM 车辆 行人检测

    HOG SVM 车辆检测

      近期需要对卡口车辆的车脸进行检测,首先选用一个常规的检测方法即是hog特征与SVM,Hog特征是由dalal在2005年提出的用于道路中行人检测的方法,并且取的了不错的识别效果。在人脸检测方面目前主流的方法,先不考虑复杂的深度学习,大多采用Haar和Adaboost的手段来实现。我接下来将会用着两种方法来实现对卡口的车辆检测。
      首先引出 Hog特征,Hog特征是梯度方向直方图,是一种底层的视觉特征,主要描述的是图像中的梯度分布情况,而梯度分布信息主要是集中在图像中不同内容之间的边界之处,可以较好的反应图像的基本轮廓面貌。在此处并不展开对描述子的详细介绍,给出一个我当时看的博客链接,对描述子原理分析的比较透彻。hog特征原理

    接下来将整个特征提取、训练、检测的流程:
    1.首先是准备训练样本,分别是正样本和负样本以及测试样本。正负样本一般来说负样本最好是正样本的2-3倍比较好,覆盖面不要是乱七八糟的图像,要贴合实际应用时的场景来选取,样本对训练过程很重要,很重要,很重要,不要以为随随便便弄一些照片就OK。
    2.在程序中导入测试样本,分别提取相应的Hog特征,这个地方我有两点要说明
      2.1.样本的尺度要正则化,也就是样本的尺寸要一样,这样可以排除训练样本尺度对模型训练的影响,在正则化的时候,尽量是不要改变其比例。
      2.2.在hog特征描述子初始化的时候,需要设置窗口大小,块大小,块滑动大小,以及细胞大小和直方图相应的bin的数目,窗口大小要和输入的训练样本的尺寸一样。

    3.提取正负样本的hog特征,我在这里采用的是128128的规模,是正方形的车脸,描述子规模是8100维。
    4.SVM采用opencv中自带的,其实opencv中采用的也是某一个版本的LIBSVM,只是重新封装了借口的操作而已。
    5.在SVM处,需要注意的是如果之后你要用SVM中自带的detector,也就是用setSVMDetector的话,这个检测器已经是写好了的转门用了处理线性核训练的模型,因为当时dalal用的就是Hog与线性的SVM特征,而且opencv自带的只支持线性的,如果你要用高斯特征即RBF核,不可以采用setSVMDetector,你用了就会出错,根本检测不到真实的位置,这里非常关键,你如果要做分类的话可以直接调用predict,但此处应该只是对车脸与非车脸做,而不是在一张图中找出车脸,如果你要找出目标物,需要自己写相应的detector,来应用你训练好的模型!!!!!

    6在检测时,检测窗口的大小必须和训练样本的尺寸是一样的,就是训练时的Hog窗口大小和检测时Hog窗口大小必须保持一致,剩下的就是检测过程中看看没有没嵌套什么的,OK,一下是代码

    #include<opencv/cv.h>
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/opencv.hpp>
    #include<opencv2/gpu/gpu.hpp>
    #include<opencv2/ml/ml.hpp>
    #include<opencv2/objdetect/objdetect.hpp>
    #include<iostream>
    #include<fstream>
    #include<string>
    #include<vector>
    using namespace std;
    using namespace cv;
    
    
    #define TRAIN//开关控制是否训练还是直接载入训练好的模型
    
    class MySVM: public CvSVM
    {
    public:
    	double * get_alpha_data()
    	{
    		return this->decision_func->alpha;
    	}
    	double  get_rho_data()
    	{
    		return this->decision_func->rho;
    	}
    };
    
    void main(int argc, char ** argv)
    {
    
    	MySVM SVM;
    	int descriptorDim;
    	
    	string buffer;
    	string trainImg;
    	vector<string> posSamples;
    	vector<string> negSamples;
    	vector<string> testSamples;
    	int posSampleNum;
    	int negSampleNum;
    	int testSampleNum;
    	string basePath = "";//相对路径之前加上基地址,如果训练样本中是相对地址,则都加上基地址
    	double rho;
    
    #ifdef TRAIN
    		ifstream fInPos("D:\DataSet\CarFaceDataSet\PositiveSample.txt");//读取正样本
    		ifstream fInNeg("D:\DataSet\CarFaceDataSet\NegtiveSample.txt");//读取负样本
    	
    		while (fInPos)//讲正样本读入imgPathList中
    		{
    			if(getline(fInPos, buffer))
    				posSamples.push_back(basePath + buffer);
    		}
    		posSampleNum = posSamples.size();
    		fInPos.close();
    
    		while(fInNeg)//读取负样本
    		{
    			if (getline(fInNeg, buffer))
    				negSamples.push_back(basePath + buffer);
    		}
    		negSampleNum = negSamples.size();
    		fInNeg.close();
    
    		Mat sampleFeatureMat;//样本特征向量矩阵
    		Mat sampleLabelMat;//样本标签
    
    		HOGDescriptor * hog = new HOGDescriptor (cvSize(128, 128), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9);
    		vector<float> descriptor;
    
    		for(int i = 0 ; i < posSampleNum; i++)// 处理正样本
    		{
    			Mat inputImg = imread(posSamples[i]);
    			cout<<"processing "<<i<<"/"<<posSampleNum<<" "<<posSamples[i]<<endl;
    			Size dsize = Size(128,128);
    			Mat trainImg = Mat(dsize, CV_32S);
    			resize(inputImg, trainImg, dsize);
    
    			hog->compute(trainImg, descriptor, Size(8, 8));
    			descriptorDim = descriptor.size();
    
    			if(i == 0)//首次特殊处理根据检测到的维数确定特征矩阵的尺寸
    			{
    				sampleFeatureMat = Mat::zeros(posSampleNum + negSampleNum, descriptorDim, CV_32FC1);
    				sampleLabelMat = Mat::zeros(posSampleNum + negSampleNum, 1, CV_32FC1);
    			}
    
    			for(int j = 0; j < descriptorDim; j++)//将特征向量复制到矩阵中
    			{
    				sampleFeatureMat.at<float>(i, j) = descriptor[j];
    			}
    
    			sampleLabelMat.at<float>(i, 0) = 1;
    		}
    
    		cout<<"extract posSampleFeature done"<<endl; 
    
    		for(int i = 0 ; i < negSampleNum; i++)//处理负样本
    		{
    			Mat inputImg = imread(negSamples[i]);
    			cout<<"processing "<<i<<"/"<<negSampleNum<<" "<<negSamples[i]<<endl;
    			Size dsize = Size(128,128);
    			Mat trainImg = Mat(dsize, CV_32S);
    			resize(inputImg, trainImg, dsize);
    			hog->compute(trainImg, descriptor, Size(8,8));
    			descriptorDim = descriptor.size();
    
    			for(int j = 0; j < descriptorDim; j++)//将特征向量复制到矩阵中
    			{
    				sampleFeatureMat.at<float>(posSampleNum + i, j) = descriptor[j];
    			}
    
    			sampleLabelMat.at<float>(posSampleNum + i, 0) = -1;
    		}
    
    		cout<<"extract negSampleFeature done"<<endl; 
    
    		//此处先预留hard example 训练后再添加
    
    		ofstream foutFeature("SampleFeatureMat.txt");//保存特征向量文件
    		for(int i = 0; i <  posSampleNum + negSampleNum; i++)
    		{
    			for(int j = 0; j < descriptorDim; j++)
    			{
    				foutFeature<<sampleFeatureMat.at<float>(i, j)<<" ";
    			}
    			foutFeature<<"
    ";
    		}
    		foutFeature.close();
    		cout<<"output posSample and negSample Feature done"<<endl; 
    
    		CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_ITER, 1000, FLT_EPSILON);
    	    CvSVMParams params(CvSVM::C_SVC, CvSVM::LINEAR, 0, 1, 0, 0.01, 0, 0, 0, criteria);  //这里一定要注意,LINEAR代表的是线性核,RBF代表的是高斯核,如果要用opencv自带的detector必须用线性核,如果自己写,或者只是判断是否为车脸的2分类问题则可以用RBF,在此应用环境中线性核的性能还是不错的
        	cout<<"SVM Training Start..."<<endl;
    		SVM.train_auto(sampleFeatureMat, sampleLabelMat, Mat(), Mat(), params);
    		SVM.save("SVM_Model.xml");
    		cout<<"SVM Training Complete"<<endl;
    #endif
    
    #ifndef TRAIN
    		SVM.load("SVM_Model.xml");//加载模型文件
    #endif
    	descriptorDim = SVM.get_var_count();
    	int supportVectorNum = SVM.get_support_vector_count();
    	cout<<"support vector num: "<< supportVectorNum <<endl;
    
    	Mat alphaMat = Mat::zeros(1, supportVectorNum, CV_32FC1);
    	Mat supportVectorMat = Mat::zeros(supportVectorNum, descriptorDim, CV_32FC1);
    	Mat resultMat = Mat::zeros(1, descriptorDim, CV_32FC1);
    
    	for (int i = 0; i < supportVectorNum; i++)//复制支持向量矩阵
    	{
    		const float * pSupportVectorData = SVM.get_support_vector(i);
    		for(int j = 0 ;j < descriptorDim; j++)
    		{
    			supportVectorMat.at<float>(i,j) = pSupportVectorData[j];
    		}
    	}
    
    	double *pAlphaData = SVM.get_alpha_data();
    	for (int i = 0; i < supportVectorNum; i++)//复制函数中的alpha 记住决策公式Y= wx+b
    	{
    		alphaMat.at<float>(0, i) = pAlphaData[i];
    	}
    
    	resultMat = -1 * alphaMat * supportVectorMat; //alphaMat就是权重向量
    
    	//cout<<resultMat;
    
    	cout<<"描述子维数 "<<descriptorDim<<endl;
    	vector<float> myDetector;
    	for (int i = 0 ;i < descriptorDim; i++)
    	{
    		myDetector.push_back(resultMat.at<float>(0, i));
    	}
    
    	rho = SVM.get_rho_data();
    	myDetector.push_back(rho);
    	cout<<"检测子维数 "<<myDetector.size()<<endl;
    
    	HOGDescriptor myHOG (Size(128, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9); 
    	myHOG.setSVMDetector(myDetector);//设置检测子
    
    	//保存检测子
    	int minusNum = 0;
    	int posNum = 0;
    
    	ofstream foutDetector("HogDetectorForCarFace.txt");
    	for (int i = 0 ;i < myDetector.size(); i++)
    	{
    		foutDetector<<myDetector[i]<<" ";
    		//cout<<myDetector[i]<<" ";
    	}
    
    	//cout<<endl<<"posNum "<<posNum<<endl;
    	//cout<<endl<<"minusNum "<<minusNum<<endl;
    	foutDetector.close();
    	//test part
    	ifstream fInTest("D:\DataSet\CarFaceDataSet\testSample.txt");
    	while (fInTest)
    	{
    		if(getline(fInTest, buffer))
    		{
    			testSamples.push_back(basePath + buffer);
    		}
    	}
    	testSampleNum = testSamples.size();
    	fInTest.close();
    
    	for (int i = 0; i < testSamples.size(); i++)
    	{
    		Mat testImg = imread(testSamples[i]);
    		Size dsize = Size(320, 240);
    		Mat testImgNorm (dsize, CV_32S);
    		resize(testImg, testImgNorm, dsize);
    
    		vector<Rect> found, foundFiltered;
    		cout<<"MultiScale detect "<<endl; 
    		myHOG.detectMultiScale(testImgNorm, found, 0, Size(8,8), Size(0,0), 1.05, 2);
    		cout<<"Detected Rect Num"<< found.size()<<endl;
    
    		for (int i = 0; i < found.size(); i++)//查看是否有嵌套的矩形框
    		{
    			Rect r = found[i];
    			int j = 0;
    			for (; j < found.size(); j++)
    			{
    				if ( i != j && (r & found[j]) == r)
    				{
    					break;
    				}
    			}
    			if(j == found.size())
    				foundFiltered.push_back(r);
    		}
    		for( int i = 0; i < foundFiltered.size(); i++)//画出矩形框
    		{
    			Rect r = foundFiltered[i];  
    			rectangle(testImgNorm, r.tl(), r.br(), Scalar(0,255,0), 1);  
    		}
    
    		imshow("test",testImgNorm);
    		waitKey();
    	}
    
    	system("pause");
    
    }
    
    




    总体效果还是不错的,如果对hardexample,进行进一步训练,以及样本的数据进行clean,相信精度还可以进一步提高,并且现在维数也比价高,位了加快检测还可以用PCA进一步降维,但必须自己重新写detector了哦,一定要好好理解一下detector,其实hog + svm的代码很多,本质上都是差不多的。

  • 相关阅读:
    迷宫
    小猫
    数位DP模板
    摘桃子
    [USACO10HOL]牛的政治Cow Politics
    ArcEngine中合并断开的线要素(根据几何判断)
    ArcEngine环境下合并断开的线要素(根据属性)
    Web Service和WCF的到底有什么区别
    OGC 的 WMS、WFS 及WCS服务(转)
    ArrayList的使用方法详解(转)
  • 原文地址:https://www.cnblogs.com/louyihang-loves-baiyan/p/4658478.html
Copyright © 2011-2022 走看看