您的位置:首页 > 运维架构

opencv中应用HOG特征训练SVM多分类器的一般框架

2016-09-28 19:08 661 查看


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的理解可见:

http://blog.csdn.net/zouxy09/article/details/7929348/

http://blog.csdn.net/raodotcong/article/details/6239431

2.提取HOG特征,进行SVM训练分类的流程

首先得有训练样本集和测试样本集,通常情况下,样本集中的样本的信息都是保存在文本文件中方便读取。通常情况下,如果样本的数量比较多,需要用bat批处理程序来提取文件名信息:dir /b/s/p/w *.jpg>train_list.txt 新建txt文档,将其保存为.bat文件,用Notepad保存比较方便。
考虑到样本集一般比较多,数量也未知,一般需要用到STL中的vector
opencv中SVM的使用可以参考官方文档:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html

源码:
#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;
}


结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: