在很多文献中,都把HOG和SVM结合起来使用,可以得到很不错的分类效果,SVM就不用再复习啦,就是一个线性分类器,给一个简单的图就能很好理解,
对于有标签的正样本和负样本,如果只有两个维度,也就是只有两个特征,要做的就是找到一条线,能够很好的划分为两个区域分别包含正负样本,并且这条线尽可能远离两个样本中心
(摘自上课的PPT)
但是这是针对二维的情况,如果扩展到多维,可能就无法找到一条线,即使找到了也可能有很多样本发生了错分,所以就需要在更高维度上考虑,高斯是最常用的。
目前我是打算将23维图像降为1维,空间信息损失实在太多啦,但是也想看看效果,就跑了跑。
一、代码及解释
//使用SVM分类器训练
//设置参数的方式1
Ptr<SVM> svm = SVM::create();
svm->setKernel(SVM::RBF);
svm->setType(SVM::C_SVC);
svm->setC(10);
svm->setCoef0(1.0);
svm->setP(1.0);
svm->setNu(0.5);
svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));
//使用SVM学习
//
svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);
//保存分类器
svm->save("Classifier.xml");
cout<<"//-------------开始训练-------//" <<endl;
clock_t start, end;
start = clock();
//使用SVM分类器训练
//设置参数,注意Ptr的使用
cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::create();
svm->setType(cv::ml::SVM::C_SVC);
svm->setKernel(cv::ml::SVM::LINEAR);//注意必须使用线性SVM进行训练,因为HogDescriptor检测函数只支持线性检测!!!
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10, FLT_EPSILON)); //迭代终止条件
//使用SVM学习
svm->train(samFeatureMat,cv::ml::ROW_SAMPLE, samLabelMat);
//保存分类器(里面包括了SVM的参数,支持向量,α和rho)
svm->save("Classifier.xml");
end = clock();
cout << "The obtain hog feature run time is :" << (double)(end - start) / CLOCKS_PER_SEC << "s" << endl;
/*
SVM训练完成后得到的XML文件里面,有一个数组,叫做support vector,还有一个数组,叫做alpha,有一个浮点数,叫做rho;
将alpha矩阵同support vector相乘,注意,alpha*supportVector,将得到一个行向量,将该向量前面乘以-1。之后,再该行向量的最后添加一个元素rho。
如此,变得到了一个分类器,利用该分类器,直接替换opencv中行人检测默认的那个分类器(cv::HOGDescriptor::setSVMDetector()),
*/
//获取支持向量机:矩阵默认是CV_32F
Mat supportVector = svm->getSupportVectors();//
//获取alpha和rho
Mat alpha;//每个支持向量对应的参数α(拉格朗日乘子),默认alpha是float64的
Mat svIndex;//支持向量所在的索引
float rho = svm->getDecisionFunction(0, alpha, svIndex);
//转换类型:这里一定要注意,需要转换为32的
Mat alpha2;
alpha.convertTo(alpha2, CV_32FC1);
//结果矩阵,两个矩阵相乘
Mat result(1, 3780, CV_32FC1);
result = alpha2*supportVector;
//乘以-1,这里为什么会乘以-1?
//注意因为svm.predict使用的是alpha*sv*another-rho,如果为负的话则认为是正样本,在HOG的检测函数中,使用rho+alpha*sv*another(another为-1)
for (int i = 0; i < 3780; ++i)
result.at<float>(0, i) *= -1;
//将分类器保存到文件,便于HOG识别
//这个才是真正的判别函数的参数(ω),HOG可以直接使用该参数进行识别
FILE *fp = fopen("HOG_SVM.txt", "wb");
for (int i = 0; i<3780; i++)
{
fprintf(fp, "%f \n", result.at<float>(0,i));
}
fprintf(fp, "%f", rho);
fclose(fp);
cout <<"真正参数得到啦"<< endl;
二、总结
如果要使用SVM进行训练的话,可能需要GPU,又或者是我维度太高了,想要二分类比较困难,这还是一个问题,等后面来解决!