opencv中應用HOG特徵訓練SVM多分類器的一般框架
阿新 • • 發佈:2019-01-09
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度。
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;
}
結果: