machine learning(4) --SVM 在opencv 中的使用及SVM与HOG联合

邢雨华
2023-12-01

原文来自http://blog.csdn.net/yangtrees/article/details/7447206

 

一、SVM

OpenCV开发SVM算法是基于LibSVM软件包开发的,LibSVM是台湾大学林智仁(Lin Chih-Jen)等开发设计的一个简单、易于使用和快速有效的SVM模式识别与回归的软件包。用OpenCV使用SVM算法的大概流程是

1)设置训练样本集

需要两组数据,一组是数据的类别,一组是数据的向量信息。

2)设置SVM参数

利用CvSVMParams类实现类内的成员变量svm_type表示SVM类型:

CvSVM::C_SVC  C-SVC

CvSVM::NU_SVC v-SVC

CvSVM::ONE_CLASS 一类SVM

CvSVM::EPS_SVR e-SVR

CvSVM::NU_SVR v-SVR

成员变量kernel_type表示核函数的类型:

CvSVM::LINEAR 线性:u‘v

CvSVM::POLY 多项式:(r*u'v + coef0)^degree

CvSVM::RBF RBF函数:exp(-r|u-v|^2)

CvSVM::SIGMOID sigmoid函数:tanh(r*u'v + coef0)

成员变量degree针对多项式核函数degree的设置,gamma针对多项式/rbf/sigmoid核函数的设置,coef0针对多项式/sigmoid核函数的设置,Cvalue为损失函数,在C-SVC、e-SVR、v-SVR中有效,nu设置v-SVC、一类SVM和v-SVR参数,p为设置e-SVR中损失函数的值,class_weightsC_SVC的权重,term_crit为SVM训练过程的终止条件。其中默认值degree = 0,gamma = 1,coef0 = 0,Cvalue = 1,nu = 0,p = 0,class_weights = 0

3)训练SVM

调用CvSVM::train函数建立SVM模型,第一个参数为训练数据,第二个参数为分类结果,最后一个参数即CvSVMParams

4)用这个SVM进行分类

调用函数CvSVM::predict实现分类

5)获得支持向量

除了分类,也可以得到SVM的支持向量,调用函数CvSVM::get_support_vector_count获得支持向量的个数,CvSVM::get_support_vector获得对应的索引编号的支持向量。

实现代码如下:运行步骤

 // step 1:
 float labels[4] = {1.0, -1.0, -1.0, -1.0};
 Mat labelsMat(3, 1, CV_32FC1, labels);
 
 float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
 Mat trainingDataMat(3, 2, CV_32FC1, trainingData);

 // step 2:
 CvSVMParams params;
 params.svm_type = CvSVM::C_SVC;
 params.kernel_type = CvSVM::LINEAR;
 params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
 
 // step 3:
 CvSVM SVM;
 SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
 
 // step 4:
 Vec3b green(0, 255, 0), blue(255, 0, 0);
 for (int i=0; i<image.rows; i++)
 {
  for (int j=0; j<image.cols; j++)
  {
   Mat sampleMat = (Mat_<float>(1,2) << i,j);
   float response = SVM.predict(sampleMat);

   if (fabs(response-1.0) < 0.0001)
   {
    image.at<Vec3b>(j, i) = green;
   }
   else if (fabs(response+1.0) < 0.001)
   {
    image.at<Vec3b>(j, i) = blue;
   }
  }
 }

 // step 5:
 int c = SVM.get_support_vector_count();

 for (int i=0; i<c; i++)
 {
  const float* v = SVM.get_support_vector(i);
 }

 

实验代码1:颜色分类

//利用SVM解决2维空间向量的3级分类问题   
#include "stdafx.h"   
#include "cv.h"   
#include "highgui.h"       
#include <ML.H>   
#include <TIME.H>   
  
#include <CTYPE.H>   
 
#include <IOSTREAM>   
using namespace std;  
int main(int argc, char **argv)  
{  
  int size = 400;         //图像的长度和宽度   
     const int s = 1000;          //试验点个数(可更改!!)   
     int i, j, sv_num;  
     IplImage *img;  
     CvSVM svm = CvSVM();    //★★★   
     CvSVMParams param;  
     CvTermCriteria criteria;//停止迭代的标准   
     CvRNG rng = cvRNG(time(NULL));  
     CvPoint pts[s];         //定义1000个点   
     float data[s*2];        //点的坐标   
     int res[s];             //点的所属类   
     CvMat data_mat, res_mat;  
     CvScalar rcolor;  
     const float *support;  
  // (1)图像区域的确保和初始化   
     img= cvCreateImage(cvSize(size, size), IPL_DEPTH_8U, 3);  
     cvZero(img);  
     //确保画像区域,并清0(用黑色作初始化处理)。   
   
     // (2)学习数据的生成   
     for (i= 0; i< s; i++) {  
         pts[i].x= cvRandInt(&rng) % size;   //用随机整数赋值   
         pts[i].y= cvRandInt(&rng) % size;  
         if (pts[i].y> 50 * cos(pts[i].x* CV_PI/ 100) + 200) {  
             cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), CV_RGB(255, 0, 0));  
    cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), CV_RGB(255, 0, 0));  
             res[i] = 1;  
         }  
         else {  
             if (pts[i].x> 200) {  
                 cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), CV_RGB(0, 255, 0));  
                 cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), CV_RGB(0, 255, 0));  
                 res[i] = 2;  
             }  
             else {  
                 cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), CV_RGB(0, 0, 255));  
                 cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), CV_RGB(0, 0, 255));  
                 res[i] = 3;  
             }  
         }  
     }  
     //生成2维随机训练数据,并将其值放在CvPoint数据类型的数组pts[ ]中。   
   
     // (3)学习数据的显示   
     cvNamedWindow("SVM", CV_WINDOW_AUTOSIZE);  
     cvShowImage("SVM", img);  
     cvWaitKey(0);  
   
     // (4)学习参数的生成   
     for (i= 0; i< s; i++) {  
         data[i* 2] = float (pts[i].x) / size;  
         data[i* 2 + 1] = float (pts[i].y) / size;  
     }  
     cvInitMatHeader(&data_mat, s, 2, CV_32FC1, data);  
     cvInitMatHeader(&res_mat, s, 1, CV_32SC1, res);  
     criteria= cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON);  
     param= CvSVMParams (CvSVM::C_SVC, CvSVM::RBF, 10.0, 8.0, 1.0, 10.0, 0.5, 0.1, 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, NULL, NULL, param);//☆   
     //☆☆利用训练数据和确定的学习参数,进行SVM学习☆☆☆☆       
   
     // (6)学习结果的绘图   
     for (i= 0; i< size; i++) {  
         for (j= 0; j< size; j++) {  
             CvMat m;  
             float ret = 0.0;  
             float a[] = { float (j) / size, float (i) / size };  
             cvInitMatHeader(&m, 1, 2, CV_32FC1, a);  
             ret= svm.predict(&m);  
             switch ((int) ret) {  
                 case 1:  
                     rcolor= CV_RGB(100, 0, 0);  
                     break;  
                 case 2:  
                     rcolor= CV_RGB(0, 100, 0);  
                     break;  
                 case 3:  
                     rcolor= CV_RGB(0, 0, 100);  
                     break;  
             }  
             cvSet2D(img, i, j, rcolor);  
         }  
     }  
     //为了显示学习结果,通过输入图像区域的所有像素(特征向量)并进行分类。然后对输入像素用所属等级的颜色绘图。   
   
     // (7)训练数据的再绘制   
     for (i= 0; i< s; i++) {  
         CvScalar rcolor;  
         switch (res[i]) {  
             case 1:  
                 rcolor= CV_RGB(255, 0, 0);  
                 break;  
             case 2:  
                 rcolor= CV_RGB(0, 255, 0);  
                 break;  
             case 3:  
                 rcolor= CV_RGB(0, 0, 255);  
                 break;  
         }  
         cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), rcolor);  
         cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), rcolor);  
     }  
     //将训练数据在结果图像上重复的绘制出来。   
   
     // (8)支持向量的绘制   
     sv_num= svm.get_support_vector_count();  
     for (i= 0; i< sv_num; i++) {  
         support = svm.get_support_vector(i);  
         cvCircle(img, cvPoint((int) (support[0] * size), (int) (support[1] * size)), 5, CV_RGB(200, 200, 200));  
     }  
     //用白色的圆圈对支持向量作标记。   
   
     // (9)图像的显示    
     cvNamedWindow("SVM", CV_WINDOW_AUTOSIZE);  
     cvShowImage("SVM", img);  
     cvWaitKey(0);  
     cvDestroyWindow("SVM");  
     cvReleaseImage(&img);  
     return 0;  
     //显示实际处理结果的图像,直到某个键被按下为止。   
 } 

实验代码2:用MIT人脸库检测,效果实在不好,检测结果全是人脸或者全都不是人脸。原因应该是图像检测没有做好应该用HoG等特征首先检测,在进行分类训练,不特征不明显,肯定分类效果并不好。

//
// File Name: pjSVM.cpp
// Author:   easyfov(easyfov@gmail.com)
// Company: Lida Optical and Electronic Co.,Ltd.
//http://apps.hi.baidu.com/share/detail/32719017
//

#include <cv.h>
#include <highgui.h>
#include <ml.h>

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

#define WIDTH 20
#define HEIGHT 20

int main( /*int argc, char** argv*/ )
{
 vector<string> img_path;
 vector<int> img_catg;
 int nLine = 0;
 string buf;
 ifstream svm_data( "E:/SVM_DATA.txt" );

 while( svm_data )
 {
  if( getline( svm_data, buf ) )
  {
   nLine ++;
   if( nLine % 2 == 0 )
   {
     img_catg.push_back( atoi( buf.c_str() ) );//atoi将字符串转换成整型,标志(0,1)
   }
   else
   {
    img_path.push_back( buf );//图像路径
   }
  }
 }
 svm_data.close();//关闭文件

 CvMat *data_mat, *res_mat;
 int nImgNum = nLine / 2;   //读入样本数量
 样本矩阵,nImgNum:横坐标是样本数量, WIDTH * HEIGHT:样本特征向量,即图像大小
 data_mat = cvCreateMat( nImgNum, WIDTH * HEIGHT, CV_32FC1 );
 cvSetZero( data_mat );
 //类型矩阵,存储每个样本的类型标志
 res_mat = cvCreateMat( nImgNum, 1, CV_32FC1 );
 cvSetZero( res_mat );

 IplImage *srcImg, *sampleImg;
 float b;
 DWORD n;

 for( string::size_type i = 0; i != img_path.size(); i++ )
 {
    srcImg = cvLoadImage( img_path[i].c_str(), CV_LOAD_IMAGE_GRAYSCALE );
    if( srcImg == NULL )
    {
   cout<<" can not load the image: "<<img_path[i].c_str()<<endl;
   continue;
    }

    cout<<" processing "<<img_path[i].c_str()<<endl;

    sampleImg = cvCreateImage( cvSize( WIDTH, HEIGHT ), IPL_DEPTH_8U, 1 );//样本大小(WIDTH, HEIGHT)
    cvResize( srcImg, sampleImg );//改变图像大小

    cvSmooth( sampleImg, sampleImg ); //降噪
    //生成训练数据
    n = 0;
  for( int ii = 0; ii < sampleImg->height; ii++ )
  {
   for( int jj = 0; jj < sampleImg->width; jj++, n++ )
   {
     b = (float)((int)((uchar)( sampleImg->imageData + sampleImg->widthStep * ii + jj )) / 255.0 );
     cvmSet( data_mat, (int)i, n, b );
   }
     }
  cvmSet( res_mat, i, 0, img_catg[i] );
     cout<<" end processing "<<img_path[i].c_str()<<" "<<img_catg[i]<<endl;
 }


 CvSVM svm = CvSVM();
 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, NULL, NULL, param );
 //☆☆利用训练数据和确定的学习参数,进行SVM学习☆☆☆☆
 svm.save( "SVM_DATA.xml" );


 //检测样本
 IplImage *tst, *tst_tmp;
 vector<string> img_tst_path;
 ifstream img_tst( "E:/SVM_TEST.txt" );
 while( img_tst )
 {
  if( getline( img_tst, buf ) )
  {
   img_tst_path.push_back( buf );
     }
 }
 img_tst.close();

 CvMat *tst_mat = cvCreateMat( 1, WIDTH*HEIGHT, CV_32FC1 );
 char line[512];
 ofstream predict_txt( "SVM_PREDICT.txt" );
 for( string::size_type j = 0; j != img_tst_path.size(); j++ )
 {
  tst = cvLoadImage( img_tst_path[j].c_str(), CV_LOAD_IMAGE_GRAYSCALE );
  if( tst == NULL )
  {
    cout<<" can not load the image: "<<img_tst_path[j].c_str()<<endl;
      continue;
   }
   tst_tmp = cvCreateImage( cvSize( WIDTH, HEIGHT ), IPL_DEPTH_8U, 1 );
   cvResize( tst, tst_tmp );
   cvSmooth( tst_tmp, tst_tmp );
   n = 0;
   for(int ii = 0; ii < tst_tmp->height; ii++ )
   {
  for(int jj = 0; jj < tst_tmp->width; jj++, n++ )
  {
   b = (float)(((int)((uchar)tst_tmp->imageData+tst_tmp->widthStep*ii+jj))/255.0);
   cvmSet( tst_mat, 0, n, (double)b );
  }
   }

   int ret = svm.predict( tst_mat );
   sprintf( line, "%s %d\r\n", img_tst_path[j].c_str(), ret );
   predict_txt<<line;
}
predict_txt.close();

cvReleaseImage( &srcImg );
cvReleaseImage( &sampleImg );
cvReleaseImage( &tst );
cvReleaseImage( &tst_tmp );
cvReleaseMat( &data_mat );
cvReleaseMat( &res_mat );

return 0;
}

 

二、SVM与HOG用于物体分类

[cpp] view plaincopyprint
#include "cv.h" 
#include "highgui.h" 
#include "stdafx.h" 
#include <ml.h> 
#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 
using namespace cv; 
using namespace std; 
 
 
int main(int argc, char** argv)   
{   
    vector<string> img_path;//输入文件名变量
    vector<int> img_catg; 
    int nLine = 0; 
    string buf; 
    ifstream svm_data( "E:/SVM_DATA.txt" );//首先,这里搞一个文件列表,把训练样本图片的路径都写在这个txt文件中,使用bat批处理文件可以得到这个txt文件  
    unsigned long n; 
 
    while( svm_data )//将训练样本文件依次读取进来 
    { 
        if( getline( svm_data, buf ) ) 
        { 
            nLine ++; 
            if( nLine % 2 == 0 )//这里的分类比较有意思,看得出来上面的SVM_DATA.txt文本中应该是一行是文件路径,接着下一行就是该图片的类别,可以设置为0或者1,当然多个也无所谓
            { 
                 img_catg.push_back( atoi( buf.c_str() ) );//atoi将字符串转换成整型,标志(0,1),注意这里至少要有两个类别,否则会出错 
            } 
            else 
            { 
                img_path.push_back( buf );//图像路径 
            } 
        } 
    } 
    svm_data.close();//关闭文件 
 
    CvMat *data_mat, *res_mat; 
    int nImgNum = nLine / 2; //读入样本数量 ,因为是每隔一行才是图片路径,所以要除以2
    样本矩阵,nImgNum:横坐标是样本数量, WIDTH * HEIGHT:样本特征向量,即图像大小 
    data_mat = cvCreateMat( nImgNum, 1764, CV_32FC1 );  //这里第二个参数,即矩阵的列是由下面的descriptors的大小决定的,可以由descriptors.size()得到,且对于不同大小的输入训练图片,这个值是不同的
    cvSetZero( data_mat ); 
    //类型矩阵,存储每个样本的类型标志 
    res_mat = cvCreateMat( nImgNum, 1, CV_32FC1 ); 
    cvSetZero( res_mat ); 
 
    IplImage* src; 
    IplImage* trainImg=cvCreateImage(cvSize(64,64),8,3);//需要分析的图片,这里默认设定图片是64*64大小,所以上面定义了1764,如果要更改图片大小,可以先用debug查看一下descriptors是多少,然后设定好再运行 
 
 //开始搞HOG特征
    for( string::size_type i = 0; i != img_path.size(); i++ ) 
    { 
            src=cvLoadImage(img_path[i].c_str(),1); 
            if( src == NULL ) 
            { 
                cout<<" can not load the image: "<<img_path[i].c_str()<<endl; 
                continue; 
            } 
 
            cout<<" processing "<<img_path[i].c_str()<<endl; 
                
            cvResize(src,trainImg);   //读取图片    
            HOGDescriptor *hog=new HOGDescriptor(cvSize(64,64),cvSize(16,16),cvSize(8,8),cvSize(8,8),9);  //具体意思见参考文章1,2    
            vector<float>descriptors;//结果数组    
            hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //调用计算函数开始计算    
            cout<<"HOG dims: "<<descriptors.size()<<endl; 
            //CvMat* SVMtrainMat=cvCreateMat(descriptors.size(),1,CV_32FC1); 
            n=0; 
            for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++) 
            { 
                cvmSet(data_mat,i,n,*iter);//把HOG存储下来 
                n++; 
            } 
                //cout<<SVMtrainMat->rows<<endl; 
            cvmSet( res_mat, i, 0, img_catg[i] ); 
            cout<<" end processing "<<img_path[i].c_str()<<" "<<img_catg[i]<<endl; 
    } 
     
              
    CvSVM svm = 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, NULL, NULL, param );//训练啦   
    //☆☆利用训练数据和确定的学习参数,进行SVM学习☆☆☆☆    
    svm.save( "SVM_DATA.xml" );
 
    //检测样本 
    IplImage *test; 
    vector<string> img_tst_path; 
    ifstream img_tst( "E:/SVM_TEST.txt" );//同输入训练样本,这里也是一样的,只不过不需要标注图片属于哪一类了
    while( img_tst ) 
    { 
        if( getline( img_tst, buf ) ) 
        { 
            img_tst_path.push_back( buf ); 
        } 
    } 
    img_tst.close(); 
 
 
 
    CvMat *test_hog = cvCreateMat( 1, 1764, CV_32FC1 );//注意这里的1764,同上面一样 
    char line[512]; 
    ofstream predict_txt( "SVM_PREDICT.txt" );//把预测结果存储在这个文本中 
    for( string::size_type j = 0; j != img_tst_path.size(); j++ )//依次遍历所有的待检测图片 
    { 
        test = cvLoadImage( img_tst_path[j].c_str(), 1); 
        if( test == NULL ) 
        { 
             cout<<" can not load the image: "<<img_tst_path[j].c_str()<<endl; 
               continue; 
         } 
         
        cvZero(trainImg); 
        cvResize(test,trainImg);   //读取图片    
        HOGDescriptor *hog=new HOGDescriptor(cvSize(64,64),cvSize(16,16),cvSize(8,8),cvSize(8,8),9);  //具体意思见参考文章1,2    
        vector<float>descriptors;//结果数组    
        hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //调用计算函数开始计算    
        cout<<"HOG dims: "<<descriptors.size()<<endl; 
        CvMat* SVMtrainMat=cvCreateMat(1,descriptors.size(),CV_32FC1); 
        n=0; 
        for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++) 
            { 
                cvmSet(SVMtrainMat,0,n,*iter); 
                n++; 
            } 
 
        int ret = svm.predict(SVMtrainMat);//获取最终检测结果,这个predict的用法见 OpenCV的文档
  std::sprintf( line, "%s %d\r\n", img_tst_path[j].c_str(), ret ); 
        predict_txt<<line; 
    } 
    predict_txt.close(); 
 
//cvReleaseImage( &src); 
//cvReleaseImage( &sampleImg ); 
//cvReleaseImage( &tst ); 
//cvReleaseImage( &tst_tmp ); 
cvReleaseMat( &data_mat ); 
cvReleaseMat( &res_mat ); 
 
return 0; 

关于HOG函数HOGDescriptor:

见博客http://blog.csdn.net/raocong2010/article/details/6239431

 

 类似资料: