OpenCV+SVM简单应用-------路面箭头分类

问题内容路面箭头检测与识别(该部分主要介绍基于SVM的分类,也就是识别,检测问题暂不谈)

问题背景:路面箭头识别在无人车中有着重要的作用:

                  1.车道线等重要信息的检测,需要排除路面箭头的干扰;

                  2.需要根据路面箭头的指示,完成下一步动作

问题描述:为了简化这个问题,重点描述基于OpenCV的SVM路面箭头分类,我们将预先检测到的待分类                     路面箭头给出如下,样本均从俯视图(逆透视)中检测获得:
       
 
图片1-11中包括:1-3、10为虚线的误检、4-5为直右箭头、6-9为误检、11为倒着的直左箭头

问题难点(本文中未解决):为何找到一个合适的特征,来解决箭头的旋转不变、尺度不变问题

作者在有限的时间中尝试过Hu矩特征、SIFT特征,效果都不是很理想,具体情况表现为:

Hu矩特征:虽然有旋转不变特性,但是将信息压缩太严重,最后得到的仅是一个七维特征,放入SVM中训练效果很差;

SIFT特征:其也具有尺度和旋转不变性,且对光照不均等现象具有抑制作用,但是作者尝试发现,样本和测试集的特征点检测较少,无法聚类得到定长的特征算子,故无法进行SVM分类

 本文采用方法:考虑到路面箭头的形式、形状甚至大小比较单一,仅仅是位置、角度不同,故可以放入较多的模板来反映同一标志不同角度的形态特征,最后将整图信息压缩到固定大小作为特征算子输入SVM中,仍可以取得不错的效果。

样本准备:
      

不同样式、相同样式不同角度以及负样本,本文采用374个样本。
 代码操作:

 首先在工程的路径下放入样本集以及测试集,如下图所示sample和test文件夹

上代码:

 

Cpp代码   收藏代码
  1. #include <opencv2/core/core.hpp>  
  2. #include <opencv2/highgui/highgui.hpp>  
  3. #include <opencv2/ml/ml.hpp>  
  4. #include <opencv2\opencv.hpp>    
  5. #include "opencv2/core/core.hpp"    
  6. #include "highgui.h"    
  7. #include "opencv2/imgproc/imgproc.hpp"    
  8. #include "opencv2/features2d/features2d.hpp"    
  9. #include "opencv2/nonfree/nonfree.hpp"    
  10. #include <iostream>  
  11. #include <time.h>  
  12. #include "MomentFeature.h"  
  13. #include <iostream>    
  14. #include <fstream>    
  15. #include <iterator>    
  16. #include <vector>    
  17. using namespace cv;  
  18. using namespace std;  
  19. #define trainnum 374//374  
  20.   
  21. #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))  
  22. /* 
  23.     //标签值,角度未合并 
  24.     float label[trainnum] =  
  25.     { 
  26.     1,1,1,1,1,1,1,1,1,1, 
  27.     2,2,2,2,2,2,2,2,2,2, 
  28.     3,3,3,3,3,3,3,3,3,3, 
  29.     4,4,4,4,4,4,4,4,4,4, 
  30.     5,5,5,5,5,5,5,5,5,5, 
  31.     6,6,6,6,6,6,6,6,6,6, 
  32.     7,7,7,7,7,7,7,7,7,7, 
  33.     8,8,8,8,8,8,8,8,8,8, 
  34.     9,9,9,9,9,9,9,9,9,9, 
  35.     10,10,10,10,10,10,10,10,10,10, 
  36.     11,11,11,11,11,11,11,11,11,11, 
  37.     12,12,12,12,12,12,12,12,12,12, 
  38.     13,13,13,13,13,13,13,13,13,13, 
  39.     14,14,14,14,14,14,14,14,14,14, 
  40.     15,15,15,15,15,15,15,15,15,15, 
  41.     16,16,16,16,16,16,16,16,16,16, 
  42.     17,17,17,17,17,17,17,17,17,17, 
  43.     18,18,18,18,18,18,18,18,18,18, 
  44.     1,1,1,1,1,1,1,1,1,1, 
  45.     9,9,9,9,9,9,9,9,9,9, 
  46.     4,4,4,4,4,4,4,4,4,4, 
  47.     6,6,6,6,6,6,6,6,6,6, 
  48.     8,8,8,8,8,8,8,8,8,8, 
  49.     3,3,3,3,3,3,3,3,3,3, 
  50.     6,6,6,6,6,6,6,6,6,6, 
  51.     6,6,6,6,6,6,6,6,6,6, 
  52.     11,11,11,11,11,11,11,11,11,11, 
  53.     16,16,16,16,16,16,16,16,16,16, 
  54.     12,12,12,12,12,12,12,12,12,12, 
  55.     12,12,12,12,12,12,12,12,12,12, 
  56.     3,3,3,3,3,3,3,3,3,3, 
  57.     3,3,3,3,3,3,3,3,3,3, 
  58.     19,19,19,19,19,19,19,19,19,19, 
  59.     19,19,19,19,19,19,19,19,19,19, 
  60.     19,19,19,19,19,19,19,19,19,19, 
  61.     8,8,8,8,8,8,8,8,8,8, 
  62.     8,8,8,8,8,8,8,8,8,8, 
  63.     10,10,10,10 
  64.     }; 
  65. */  
  66.     //标签值,角度合并了!  
  67.     float label[trainnum] =   
  68.     {  
  69.     1,1,1,1,1,1,1,1,1,1,  
  70.     2,2,2,2,2,2,2,2,2,2,  
  71.     3,3,3,3,3,3,3,3,3,3,  
  72.     1,1,1,1,1,1,1,1,1,1,  
  73.     5,5,5,5,5,5,5,5,5,5,  
  74.     5,5,5,5,5,5,5,5,5,5,  
  75.     5,5,5,5,5,5,5,5,5,5,  
  76.     8,8,8,8,8,8,8,8,8,8,  
  77.     9,9,9,9,9,9,9,9,9,9,  
  78.     10,10,10,10,10,10,10,10,10,10,  
  79.     1,1,1,1,1,1,1,1,1,1,  
  80.     3,3,3,3,3,3,3,3,3,3,  
  81.     5,5,5,5,5,5,5,5,5,5,  
  82.     14,14,14,14,14,14,14,14,14,14,  
  83.     15,15,15,15,15,15,15,15,15,15,  
  84.     2,2,2,2,2,2,2,2,2,2,  
  85.     17,17,17,17,17,17,17,17,17,17,  
  86.     18,18,18,18,18,18,18,18,18,18,  
  87.     1,1,1,1,1,1,1,1,1,1,  
  88.     9,9,9,9,9,9,9,9,9,9,  
  89.     1,1,1,1,1,1,1,1,1,1,  
  90.     5,5,5,5,5,5,5,5,5,5,  
  91.     8,8,8,8,8,8,8,8,8,8,  
  92.     3,3,3,3,3,3,3,3,3,3,  
  93.     5,5,5,5,5,5,5,5,5,5,  
  94.     5,5,5,5,5,5,5,5,5,5,  
  95.     1,1,1,1,1,1,1,1,1,1,  
  96.     2,2,2,2,2,2,2,2,2,2,  
  97.     3,3,3,3,3,3,3,3,3,3,  
  98.     3,3,3,3,3,3,3,3,3,3,  
  99.     3,3,3,3,3,3,3,3,3,3,  
  100.     3,3,3,3,3,3,3,3,3,3,  
  101.     17,17,17,17,17,17,17,17,17,17,  
  102.     17,17,17,17,17,17,17,17,17,17,  
  103.     17,17,17,17,17,17,17,17,17,17,  
  104.     8,8,8,8,8,8,8,8,8,8,  
  105.     8,8,8,8,8,8,8,8,8,8,  
  106.     10,10,10,10  
  107.     };  
  108. string labelname[500] = {"左1",//1,4,11  
  109.                          "右1",//2,16  
  110.                          "直右",//3,12  
  111.                          "左2",//4  
  112.                         "直1",//5,6,7,13  
  113.                         "直2",//6  
  114.                         "直3",//7  
  115.                         "虚线",//8  
  116.                         "斑马线1",//9  
  117.                         "斑马线2",//10  
  118.                         "倒左",//11  
  119.                         "倒直右",//12  
  120.                         "倒直",//13  
  121.                         "误检",//14  
  122.                         "误检2",//15  
  123.                         "倒右",//16  
  124.                         "倒直左",//17  
  125.                         "误检3",//18  
  126.                         "直左"//19,17  
  127.                             };  
  128. /*----------------------------  
  129.  * 功能 : 将 cv::Mat 数据写入到 .txt 文件  
  130.  *----------------------------  
  131.  * 函数 : WriteData  
  132.  * 访问 : public   
  133.  * 返回 : -1:打开文件失败;0:写入数据成功;1:矩阵为空  
  134.  *  
  135.  * 参数 : fileName    [in]    文件名  
  136.  * 参数 : matData [in]    矩阵数据  
  137.  */    
  138. int WriteData(string fileName, cv::Mat& matData)    
  139. {    
  140.     int retVal = 0;    
  141.     
  142.     // 打开文件    
  143.     ofstream outFile(fileName.c_str(), ios_base::out);  //按新建或覆盖方式写入    
  144.     if (!outFile.is_open())    
  145.     {    
  146.         cout << "打开文件失败" << endl;     
  147.         retVal = -1;    
  148.         return (retVal);    
  149.     }    
  150.     
  151.     // 检查矩阵是否为空    
  152.     if (matData.empty())    
  153.     {    
  154.         cout << "矩阵为空" << endl;     
  155.         retVal = 1;    
  156.         return (retVal);    
  157.     }    
  158.     
  159.     // 写入数据    
  160.     for (int r = 0; r < matData.rows; r++)    
  161.     {    
  162.         for (int c = 0; c < matData.cols; c++)    
  163.         {    
  164.             if(c != 0)  
  165.                 outFile << ",";  
  166.             float data = matData.at<float>(r,c);  //读取数据,at<type> - type 是矩阵元素的具体数据格式   
  167.             outFile << data  ;   //每列数据用 tab 隔开    
  168.         }    
  169.         outFile << endl;  //换行  
  170.     }    
  171.     
  172.     return (retVal);    
  173. }    
  174.   
  175. void floatscale(float* f,int num, int scale)  
  176. {  
  177.     for(int i = 0; i < num; i++)  
  178.     {  
  179.         f[i]=f[i]*scale;  
  180.     }  
  181. }  
  182.   
  183. int main()  
  184. {     
  185.     //读取sample中的样本  
  186.     char filename[200];  
  187.     Mat sample;  
  188.     Mat train;  
  189.     Mat Hu_train;  
  190.     //读取图片数据,提取特征也在这里!  
  191.     IplImage* ImageGray;  
  192.     float feavec[48];  
  193.     for(int i = 0; i < trainnum; i++)  
  194.     {  
  195.         sprintf(filename,"sample/%d.jpg",1+i);  
  196.         //*************把图片信息直接放入************  
  197.         sample = imread(filename,-1);  
  198.         resize(sample,sample,Size(10,70));  
  199.         sample = sample.reshape(0,1);  
  200.         sample.convertTo(sample, CV_32F);  
  201.         train.push_back(sample);  
  202.         //*******************************************  
  203.         //********zernike特征***********  
  204.         //ImageGray = cvLoadImage(filename,0);  
  205.         //MomentFeature MF;  
  206.         //MF.SetXStep(1);  
  207.         //MF.SetYStep(3);  
  208.         //MF.SetVecNum(3,9);  
  209.         //Hu[3] + Zernike[9]     
  210.         //int FeatureLength = MF.GetFeatureLength();  
  211.         //MF.GetMomentFeature(ImageGray,1,1,feavec);  
  212.         //cout << FeatureLength << endl;  
  213.         //Mat feature(1, 48, CV_32FC1, feavec);  
  214.         //normalize(feature,feature,1.0,0.0,NORM_MINMAX);  
  215.         //train.push_back(feature);  
  216.         //cout << feavec[47] << endl;  
  217.         //feavec为输出特征  
  218.         //*******************************  
  219.   
  220.         //********Hu*********  
  221.         //Canny(sample,sample,60,120);  
  222.         //Moments mo; //矩变量  
  223.         //double M[7];//Hu矩输出  
  224.         //计算Hu矩  
  225.         //mo=moments(sample);  
  226.         //HuMoments(mo, M);  
  227.         //cout << M[0] << " " << M[1] << " " << M[2] << " " << M[3] << " " << M[4] << " " << M[5] << " " << M[6] << " " << endl;   
  228.         //Mat Hu(1, 7, CV_64F, M);  
  229.         //train.push_back(Hu);  
  230.         //*******************  
  231.         //*******harris******  
  232.         //Mat cornerStrength;   
  233.         //cornerHarris(sample, cornerStrength, 2, 3, 0.01);  
  234.         //Mat harrisCorner;   
  235.         //threshold(cornerStrength, harrisCorner, 0.00001, 255, THRESH_BINARY);   
  236.         //imshow("角点检测后的二值效果图", harrisCorner);   
  237.         //waitKey();  
  238.         //*******************  
  239.         //*******SIFT********  
  240.         //SIFT sift(10);  
  241.         //vector<KeyPoint> key_points;    //特征点    
  242.         // descriptors为描述符,mascara为掩码矩阵    
  243.         //Mat descriptors, mascara;    
  244.         //Mat output_img;    //输出图像矩阵  
  245.         //sift(sample,mascara,key_points,descriptors);    //执行SIFT运算  
  246.         //cout << descriptors.cols << " " << descriptors.rows << " " <<(int)key_points.size() <<  endl;  
  247.         //*******************  
  248.   
  249.     }  
  250.     //Hu需要reshape以下  
  251.     //resize(train,train,Size(7,20));  
  252.   
  253.     //特征写入txt  
  254.     WriteData("feature_train.txt",train);  
  255.     //导入标签值  
  256.     Mat labels(trainnum, 1, CV_32FC1, label);  
  257.     cout << train.cols << " " << train.rows << endl;  
  258.     cout << labels.cols << " " << labels.rows << endl;  
  259.   
  260.     //*********************SVM训练部分***********************  
  261.     //准备开始训练  
  262.     CvSVM classifier;  
  263.     CvSVMParams SVM_params;  
  264.     SVM_params.kernel_type = CvSVM::LINEAR; //使用RBF分类非线性问题  
  265.     SVM_params.svm_type = CvSVM::C_SVC;  
  266.     SVM_params.degree = 0;  
  267.     SVM_params.gamma = 0.01;  
  268.     SVM_params.term_crit =  cvTermCriteria(CV_TERMCRIT_ITER, 1000, FLT_EPSILON);  
  269.     SVM_params.C = 1;  
  270.     SVM_params.coef0 = 0;  
  271.     SVM_params.nu = 0;  
  272.     SVM_params.p = 0.005;  
  273.     //classifier.train(train,labels ,Mat(),Mat(),SVM_params); //SVM训练  
  274.     //使用调参  
  275.     //对不用的参数step设为0    
  276.     //CvParamGrid nuGrid = CvParamGrid(1,1,0.0);    
  277.     //CvParamGrid coeffGrid = CvParamGrid(1,1,0.0);    
  278.     //CvParamGrid degreeGrid = CvParamGrid(1,1,0.0);   
  279.     classifier.train_auto(train,labels ,Mat(),Mat(),SVM_params,  
  280.                 10,  
  281.                 classifier.get_default_grid(CvSVM::C),  
  282.                 classifier.get_default_grid(CvSVM::GAMMA),  
  283.                 classifier.get_default_grid(CvSVM::P),  
  284.                 classifier.get_default_grid(CvSVM::NU),  
  285.                 classifier.get_default_grid(CvSVM::COEF),  
  286.                 classifier.get_default_grid(CvSVM::DEGREE)  
  287.                 );  
  288.     classifier.save("model180.txt");  
  289.     //******************************************************  
  290.     //这里载入分类器,方便直接训练  
  291.     //CvSVM classifier;  
  292.     //classifier.load("model180.txt");  
  293.     vector<Mat> testdata; //定义测试数据  
  294.     Mat testmmat;  
  295.     Mat test;  
  296.     int testnum = 11;  
  297.     for(int i = 0; i < testnum; i++)  
  298.     {  
  299.         sprintf(filename,"test/%d.jpg",i+1);  
  300.         //*************直接把图片信息放入***********  
  301.         test = imread(filename,-1);  
  302.         resize(test,test,Size(10,70));  
  303.         test = test.reshape(0,1);  
  304.         test.convertTo(test, CV_32FC1);  
  305.         testdata.push_back(test);  
  306.         testmmat.push_back(test);  
  307.         //******************************************  
  308.   
  309.         //********zernike特征***********************  
  310.         //ImageGray = cvLoadImage(filename,0);  
  311.         //MomentFeature MF;  
  312.         //MF.SetXStep(1);  
  313.         //MF.SetYStep(3);  
  314.         //MF.SetVecNum(3,9);  
  315.         //Hu[3] + Zernike[9]     
  316.         //int FeatureLength = MF.GetFeatureLength();  
  317.         //MF.GetMomentFeature(ImageGray,1,1,feavec);  
  318.         //cout << FeatureLength << endl;  
  319.         //Mat feature(1, 48, CV_32FC1, feavec);  
  320.         //normalize(feature,feature,1.0,0.0,NORM_MINMAX);  
  321.         //testdata.push_back(feature);  
  322.         //testmmat.push_back(feature);  
  323.         //feavec为输出特征  
  324.         //******************************************  
  325.         //********Hu*******************  
  326.         //Canny(test,test,60,120);  
  327.         //Moments mo; //矩变量  
  328.         //double M[7];//Hu矩输出  
  329.         //计算Hu矩  
  330.         //mo=moments(test);  
  331.         //HuMoments(mo, M);  
  332.         //cout << M[0] << " " << M[1] << " " << M[2] << " " << M[3] << " " << M[4] << " " << M[5] << " " << M[6] << " " << endl;   
  333.         //Mat Hu(1, 7, CV_32FC1, M);  
  334.         //testdata.push_back(Hu);  
  335.         //*****************************  
  336.     }  
  337.     WriteData("feature_test.txt",testmmat);  
  338.     char textInImage[200];  
  339.     //使用训练好的分类器进行预测  
  340.     for (int i = 0;i < testdata.size() ; ++i)  
  341.     {  
  342.         clock_t start = clock();  
  343.         int result = (int)classifier.predict(testdata[i]);  
  344.         std::cout<<"测试样本"<<i+1<<"的测试结果为:"  
  345.             <<result<< " " << labelname[result-1] << "\n";  
  346.         clock_t end = clock();  
  347.         double tt = static_cast<double>(end - start);  
  348.         sprintf(textInImage, "elapsed time: %5.2f ms", tt);  
  349.         cout << textInImage << endl;  
  350.     }  
  351.   
  352.     getchar();  
  353.       
  354. }  
(以上程序注释部分大部分为特征算子的尝试,本文不作介绍,所以注释掉,但是留在这里,有兴趣的可以留言进行研究!同时,将提取的特征写入文件,方便进行其他测试!) 
其中label与labelname要一一对应,这里设立了19个类别,主要还包含了相同样式不同角度的模板,如果能够找到一个旋转不变的特征算子,则这里的类别可以设置更少,分类效果应该也会更好!

 

SVM采用最简单的线性核函数,由于模板样式简单,分类明确,所以可以认为是线性可分问题,结果不错,当然,复杂情况下往往采用RBF径向基核函数,那么需要调参(SVM原理这里不做讨论,以后会再阐述)。

需要注意的事,训练完模型之后可以直接对模型进行保存:

Cpp代码   收藏代码
  1. classifier.save("model180.txt");  

 这样,会发现工程下多了一个model180.txt文件,不妨打开瞧一瞧,SVM的相关参数都保存在其中:
                                         

那么下次要用此训练好的模型则可以直接调用该txt模型:

Cpp代码   收藏代码
  1. CvSVM classifier;  
  2. classifier.load("model180.txt");  

最后我们可以得到结果:
                
 当然,这些labelname可以自行定义!

 

以上只是一个非常简单的demo,旨在留下一个OpenCV做SVM分类的简单框架,方便以后举一反三,如果有做的不好的地方,欢迎大家及时指正!
 本文试验的样本和测试集提供在附件,欢迎尝试!

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值