1. 程式人生 > >opencv中應用HOG特徵訓練SVM多分類器的一般框架

opencv中應用HOG特徵訓練SVM多分類器的一般框架

1.HOG特徵提取

opencv裡關於HOG特徵的提取是通過函式HOGDescriptor()來完成的,主要有下面4個建構函式:

CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8),  
    cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),  
    histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true),   
    nlevels(HOGDescriptor::DEFAULT_NLEVELS)  
{} 

CV_WRAP HOGDescriptor(Size _winSize, Size _blockSize, Size _blockStride,  
              Size _cellSize, int _nbins, int _derivAperture=1, double _winSigma=-1,  
              int _histogramNormType=HOGDescriptor::L2Hys,  
              double _L2HysThreshold=0.2, bool _gammaCorrection=false,  
              int _nlevels=HOGDescriptor::DEFAULT_NLEVELS)  
: winSize(_winSize), blockSize(_blockSize), blockStride(_blockStride), cellSize(_cellSize),  
nbins(_nbins), derivAperture(_derivAperture), winSigma(_winSigma),  
histogramNormType(_histogramNormType), L2HysThreshold(_L2HysThreshold),  
gammaCorrection(_gammaCorrection), nlevels(_nlevels)  
{} 
CV_WRAP HOGDescriptor(const String& filename)  
{  
    load(filename);  
}

HOGDescriptor(const HOGDescriptor& d)  
{  
    d.copyTo(*this);  
}

實際應用過程中主要是第二個,其中有幾個關鍵的引數:
  • winSize: 視窗的大小
  • blockSize:塊的大小
  • blockStride:塊滑動的增量
  • cellSize:元組的大小
  • nbins:梯度方向的陣列,例如nBins=9時,在一個胞元內統計9個方向的梯度直方圖,每個方向為180/9=20度。
實際應用中關鍵是弄清楚HOG特徵的維數的計算,例如出入一幅64*64的影象,輸出的結果就是一個很長的hog特徵向量,向量的維度與上面的幾個引數有關。例如按照第一個建構函式中預設的初始序列,可以計算得出hog的特徵向量維度是:9*(16/8)*(16/8)*[(64-16)/8+1]*[(128-16)/8+1]=3780
size_t HOGDescriptor::getDescriptorSize() const  
{  
    CV_Assert(blockSize.width % cellSize.width == 0 &&  
        blockSize.height % cellSize.height == 0);  
    CV_Assert((winSize.width - blockSize.width) % blockStride.width == 0 &&  
        (winSize.height - blockSize.height) % blockStride.height == 0 );  
    return (size_t)nbins*  
        (blockSize.width/cellSize.width)*  
        (blockSize.height/cellSize.height)*  
        ((winSize.width - blockSize.width)/blockStride.width + 1)*  
        ((winSize.height - blockSize.height)/blockStride.height + 1);  
} 
詳情關於HOG的理解可見: 2.提取HOG特徵,進行SVM訓練分類的流程
  • 首先得有訓練樣本集和測試樣本集,通常情況下,樣本集中的樣本的資訊都是儲存在文字檔案中方便讀取。通常情況下,如果樣本的數量比較多,需要用bat批處理程式來提取檔名資訊:dir /b/s/p/w *.jpg>train_list.txt    新建txt文件,將其儲存為.bat檔案,用Notepad儲存比較方便。
  • 考慮到樣本集一般比較多,數量也未知,一般需要用到STL中的vector
原始碼:
#include "cv.h"  
#include "highgui.h"  
#include <ml.h>  
#include <iostream>  
#include <fstream>  
#include <string>  
#include <vector>  
using namespace cv;  
using namespace std;  
  
int main()    
{    
	int ImgWidht = 64;
	int ImgHeight = 64;
    vector<string> img_path;  
    vector<int> img_catg;  
    int nLine = 0;  
    string buf;  
    ifstream svm_data( "tran.txt" );  
    unsigned long n;  
  
    while( svm_data )  
    {  
        if( getline( svm_data, buf ) )  
        {  
            //三種類別,前10個,中間10個,最後10個
            if( nLine <10 )  
            {  
				img_catg.push_back(0);//影象類別
				img_path.push_back( buf );//影象路徑 
            }  
            else  if(nLine <20 )
            {  
				img_catg.push_back(1);
				img_path.push_back( buf );//影象路徑 
            } 
			else
			{
				img_catg.push_back(2);
				img_path.push_back( buf );//影象路徑 
			}
			nLine ++;  
        }  
    }  
    svm_data.close();//關閉檔案  
  
    Mat data_mat, res_mat;  
    int nImgNum = nLine;            //讀入樣本數量  
    //樣本矩陣,nImgNum:行數代表樣本的數量,每一行就是由一張圖片計算得到HOG的特徵向量,
	data_mat = Mat::zeros( nImgNum, 1764, CV_32FC1 ); //HOG特徵的位數: 9*(16/8)*(16/8)*[(64-16)/8+1]*[(64-16)/8+1]=1764
    //型別矩陣,儲存每個樣本的型別標誌  
    res_mat = Mat::zeros( nImgNum, 1, CV_32FC1 );  
  
    Mat src;  
    Mat trainImg = Mat::zeros(ImgHeight, ImgWidht, CV_8UC3);//需要分析的圖片  
  
    for( string::size_type i = 0; i != img_path.size(); i++ )  
    {  
		src = imread(img_path[i].c_str(), 1);   
  
        cout<<" processing "<<img_path[i].c_str()<<endl;  
         
		resize(src, trainImg, cv::Size(ImgWidht,ImgHeight), 0, 0, INTER_CUBIC);
        HOGDescriptor *hog=new HOGDescriptor(cvSize(ImgWidht,ImgHeight),cvSize(16,16),cvSize(8,8),cvSize(8,8), 9);  //構造HOG,具體意思見參考文章1,2     
        vector<float>descriptors;//結果陣列     
        hog->compute(trainImg, descriptors, Size(1,1), Size(0,0)); //呼叫計算函式開始計算
		if (i==0)
		{
			 data_mat = Mat::zeros( nImgNum, descriptors.size(), CV_32FC1 ); //根據輸入圖片大小進行分配空間 
		}
        cout<<"HOG dims: "<<descriptors.size()<<endl;   
        n=0;  
        for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)  
        {  
			data_mat.at<float>(i,n) = *iter;  
            n++;  
        }  
        //cout<<SVMtrainMat->rows<<endl;  
	    res_mat.at<float>(i, 0) =  img_catg[i];  
        cout<<" end processing "<<img_path[i].c_str()<<" "<<img_catg[i]<<endl;  
    }  
               
    CvSVM svm ;
    CvSVMParams param;  
    CvTermCriteria criteria;    
    criteria = cvTermCriteria( CV_TERMCRIT_EPS, 1000, FLT_EPSILON );    
    param = CvSVMParams( CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria );   
	
/*    
    SVM種類:CvSVM::C_SVC    
    Kernel的種類:CvSVM::RBF    
    degree:10.0(此次不使用)    
    gamma:8.0    
    coef0:1.0(此次不使用)    
    C:10.0    
    nu:0.5(此次不使用)    
    p:0.1(此次不使用)    
    然後對訓練資料正規化處理,並放在CvMat型的數組裡。    
                                                        */       
    //☆☆☆☆☆☆☆☆☆(5)SVM學習☆☆☆☆☆☆☆☆☆☆☆☆         
    svm.train( data_mat, res_mat, Mat(), Mat(), param );    
    //☆☆利用訓練資料和確定的學習引數,進行SVM學習☆☆☆☆     
    svm.save( "SVM_DATA.xml" ); 
  
    //檢測樣本  
    vector<string> img_tst_path;  
    ifstream img_tst( "test.txt" );  
    while( img_tst )  
    {  
        if( getline( img_tst, buf ) )  
        {  
            img_tst_path.push_back( buf );  
        }  
    }  
    img_tst.close();  
  
	Mat test;
    char line[512];  
    ofstream predict_txt( "SVM_PREDICT.txt" );  
    for( string::size_type j = 0; j != img_tst_path.size(); j++ )  
    {  
        test = imread( img_tst_path[j].c_str(), 1);//讀入影象   
        resize(test, trainImg, cv::Size(ImgWidht,ImgHeight), 0, 0, INTER_CUBIC);//要搞成同樣的大小才可以檢測到       
        HOGDescriptor *hog=new HOGDescriptor(cvSize(ImgWidht,ImgHeight),cvSize(16,16),cvSize(8,8),cvSize(8,8),9);  //視窗大小,塊大小,塊滑動增量,cell的大小,bins的個數
        vector<float>descriptors;//結果陣列     
        hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //呼叫計算函式開始計算 
		cout<<"The Detection Result:"<<endl;
        cout<<"HOG dims: "<<descriptors.size()<<endl;  
        Mat SVMtrainMat =  Mat::zeros(1,descriptors.size(),CV_32FC1);  
        n=0;  
        for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)  
        {  
			SVMtrainMat.at<float>(0,n) = *iter;  
            n++;  
        }  
  
        int ret = svm.predict(SVMtrainMat);  
		std::sprintf( line, "%s %d\r\n", img_tst_path[j].c_str(), ret ); 
		printf("%s %d\r\n", img_tst_path[j].c_str(), ret);//輸出預測的結果,ret的值就代表類別
		//getchar();
        predict_txt<<line;  
    }  
    predict_txt.close();  
  system("PAUSE");
return 0;  
}  


結果: