一、前言
之前做视觉工程的时候,用的是opencv2.4.9,成功用上了SVM做几何体分类,但自从opencv3对SVM的调用方法大改后,套用之前的工程就遇到了很多问题,为了给以后的人正确的指引,也为了给自己提个醒,便有了写下这篇博文的心。正文开始之前,不妨听我絮叨一下。
铁人战队自RM第一次比赛之初创立已有5年历史,最近学校批准我们可以参加RM2020赛季机甲大师赛,大家兴奋不已。之前哭着喊着要参加RM,终于有机会参赛了,但真正落到自己头上的时候,还是有诸多问题,队员资质、比赛资金、老师支持、队伍人心、规则大改都是问题,我才意识到长路漫漫,其修远兮。这一年对我们来说是机会也是难题,难在队伍招新不达标,人员配置有很大问题,好在规则大改,意味着所有队伍都没有经验,我们更能成为一匹黑马,大杀四方。未来还有更多的年轻人来到铁人战队,有望学以成才,活出不一样的人生,甚至看着今天我们铁人战队的荣耀,为自己是铁人战队的一员而感到骄傲、自豪。我们有着责任,有着义务,排除一切艰难险阻,为了心中的白月光,热血奋斗到2020赛季结束。总之,一起加油吧,青年工程师们。
二、在opencv3.0.0中遇到的SVM坑
1、设置SVM类型,内核类型,迭代终止条件时,参数设置按网上某篇教程有错,下面的代码里面是正确的设置方法。
2、使用trainAuto时,参数不能直接给(训练数据,类型,标签),要以TrainData类对象的形式给定。
3、标签的类型必须是CV_32S,不能是CV_32F。
4、标签不能是负数,即不能给负样本设置标签为-1。
5、训练数据最好不要用push_back的形式给定,会错行,而用range方式给定,则不会错行,我用debug看过二者矩阵大小的区别,用push_back会多个30多行,另外这样拿去作为创建TrainData的参数会报错。
下面是我测试过的opencv3.0.0 SVM识别装甲板数字的工程代码,仅作为参考用。
#include "./SVM_TrainAuto/svm_trainauto.h"
using namespace std;
using namespace cv;
int main()
{
// initial SVM
Ptr<ml::SVM> _svmClassifier = ml::SVM::create();
_svmClassifier->setType(ml::SVM::C_SVC); //SVM类型(允许用异常值惩罚因子C进行不完全分类)
_svmClassifier->setKernel(ml::SVM::LINEAR); //SVM的内核类型,一般情况下使用径向基核可以很好处理大部分情况
_svmClassifier->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10000, 1e-6)); //指定迭代终止的条件
RM::MySVM mySVM;
printf("\t\t\t\t开始获取训练集数据\n");
mySVM.getTrainData();
printf("\t\t\t\t成功获取训练集数据\n");
printf("\t\t\t\tSVM训练样本开始\n");
//SVM的训练函数是ROW_SAMPLE类型的,也就是说,送入SVM训练的特征需要reshape成一个行向量,所有训练数据全部保存在一个Mat中,一个训练样本就是Mat中的一行
Ptr<ml::TrainData> tData = ml::TrainData::create(mySVM._trainData, ml::ROW_SAMPLE, mySVM._trainClasses);
_svmClassifier->trainAuto(tData);//自动训练并优化参数
_svmClassifier->save("svm.xml");
printf("\t\t\t\tSVM训练样本结束\n");
Mat srcImg = imread(ARMOR_IMAGE_PATH,IMREAD_COLOR);
Mat grayImg,imageNewSize;
cvtColor(srcImg,grayImg,COLOR_BGR2GRAY);
resize(grayImg, imageNewSize, Size(mySVM._sampleData.width,mySVM._sampleData.height)); //统一摄像头画面里面采集到的轮廓图像的尺寸
grayImg.release(); //把image的矩阵信息释放(清除)
grayImg = imageNewSize.reshape(0, 1); //图像深度不变,把图片矩阵转为一行储存
grayImg.convertTo(grayImg, CV_32FC1);
int response = (int)_svmClassifier->predict(grayImg);
switch (response)
{
case RM::Hero:
putText(srcImg,mySVM._sampleData.str,Point(srcImg.size().width/2,srcImg.size().height/2),
FONT_HERSHEY_SIMPLEX,0.5,Scalar(0, 0, 255));
break;
default : break;
}
imshow("【效果图】",srcImg);
waitKey(0);
return 0;
}
SVM模型训练好后,加载模型用如下语句:
Ptr<ml::SVM> _svmClassifier = ml::SVM::load<ml::SVM>("svm.xml");
识别到的效果图如下: