`
java-大神
  • 浏览: 34452 次
  • 性别: Icon_minigender_1
  • 来自: 大山东
社区版块
存档分类
最新评论

机器学习:支持向量机实现手写识别

阅读更多

    不知不觉,学习脑与科学已经将近3个月了,虽然对上课老师讲的纯理论提不起一点兴趣,但是不得不说,这玩意儿确实有它吸引人的地方,比如计算机视觉,比如今天的机器学习。

    机器学习可以说是人工智能的核心,是它使计算机拥有了智能,简单说,它使计算机有了学习的能力。MS李

给了五种机器学习算法实现手写识别(否则挂科):

    自适应增强;

    支持向量机;

    贝叶斯分类器;

    人工神经网络;

    决策树;

    

    支持向量机是啥?跑去百科转了一圈,不懂。又借了本导论,很快就看完了,伤感是没怎么看懂,高兴的是字我都认识。乱起八糟看了一堆,用几个关键词来形容吧,大量样本-->hog特征处理-->训练产生分类器-->预测。

   

    处理样本数据。

    现在有0~9的样本图片60000张(如下),可以用一个for循环来一次读入每张图片,这里介绍一另一种
              
方法,读入文件(本质也是循环),楼主用bat批处理器把所用图片路径存到一个"hb.txt"中,突然发现不知道怎么说批处理了,这里暂时略过,容楼主再去仔细看下汇编(尴尬)~~

    HOG特征处理

    其实就是提取图片、灰度化(三维化)、划分成一个个cells、统计。

 

            cvResize(src,trainImg); //读取图片,归一化处理    
            HOGDescriptor *hog=new HOGDescriptor(cvSize(28,28),cvSize(14,14),cvSize(7,7),cvSize(7,7),9); //实例化一个hog对象     
            vector<float>descriptors;//存放结果,数组     
            hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //Hog特征计算

   

 

    训练形成分类器

    这就简单啦,新建一个SVM,直接调用train(),

	svm.train( data_mat, res_mat, NULL, NULL, param ); 
//data_mat样本矩阵存储上面提取到的HOG特征,存储 m 个样本的 n 个特征, res_mat 是标签矩阵
//训练数据,利用训练数据和确定的学习参数,进行SVM学习 
//F:\OpenCV-2.4.5\opencv\modules\ml\src\svm.cpp-------P1584-P1647   

只能说opencv太强大,好吧,还是源代码最直接,其实在我们用着系统早已封装好的方法时,我们似乎渐渐忽视了源代码重要性,若不是之前就已经知道算法原理,光是一个train()根本不能就绝问题,下面贴出源码仅供有兴趣的同学参考:

 

bool CvSVM::train( const CvMat* _train_data, const CvMat* _responses,
    const CvMat* _var_idx, const CvMat* _sample_idx, CvSVMParams _params )
{
    bool ok = false;
    CvMat* responses = 0;
    CvMemStorage* temp_storage = 0;
    const float** samples = 0;

    CV_FUNCNAME( "CvSVM::train" );

    __BEGIN__;

    int svm_type, sample_count, var_count, sample_size;
    int block_size = 1 << 16;
    double* alpha;

    clear();
    CV_CALL( set_params( _params ));

    svm_type = _params.svm_type;

    /* Prepare training data and related parameters */
    CV_CALL( cvPrepareTrainData( "CvSVM::train", _train_data, CV_ROW_SAMPLE,
                                 svm_type != CvSVM::ONE_CLASS ? _responses : 0,
                                 svm_type == CvSVM::C_SVC ||
                                 svm_type == CvSVM::NU_SVC ? CV_VAR_CATEGORICAL :
                                 CV_VAR_ORDERED, _var_idx, _sample_idx,
                                 false, &samples, &sample_count, &var_count, &var_all,
                                 &responses, &class_labels, &var_idx ));


    sample_size = var_count*sizeof(samples[0][0]);

    // make the storage block size large enough to fit all
    // the temporary vectors and output support vectors.
    block_size = MAX( block_size, sample_count*(int)sizeof(CvSVMKernelRow));
    block_size = MAX( block_size, sample_count*2*(int)sizeof(double) + 1024 );
    block_size = MAX( block_size, sample_size*2 + 1024 );

    CV_CALL( storage = cvCreateMemStorage(block_size + sizeof(CvMemBlock) + sizeof(CvSeqBlock)));
    CV_CALL( temp_storage = cvCreateChildMemStorage(storage));
    CV_CALL( alpha = (double*)cvMemStorageAlloc(temp_storage, sample_count*sizeof(double)));

    create_kernel();
    create_solver();

    if( !do_train( svm_type, sample_count, var_count, samples, responses, temp_storage, alpha ))
        EXIT;

    ok = true; // model has been trained succesfully

    __END__;

    delete solver;
    solver = 0;
    cvReleaseMemStorage( &temp_storage );
    cvReleaseMat( &responses );
    cvFree( &samples );

    if( cvGetErrStatus() < 0 || !ok )
        clear();

    return ok;
}

    生成分类器

svm.save( "HOG_SVM_DATA.xml" );  //保存训练好的分类器

   

#include "stdafx.h"
#include "opencv2/opencv.hpp"
#include "windows.h"
#include "fstream"
using namespace std;
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
    vector<string> img_path;//输入文件名变量   
    vector<int> img_catg;  //标签  
    int nLine = 0;    
    string buf;    
    ifstream svm_data( "D:\\nums\\hb.txt" );//训练样本图片的路径都写在这个txt文件中,bat批处理
    unsigned long n;     
    while( svm_data )//将训练样本文件依次读取进来    
    {    
        if( getline( svm_data, buf ) )    //将输入流svm_data中读到的字符存入buf中
        {    
            nLine ++;    
            if( nLine % 2 == 0 )//注:奇数行是图片全路径,偶数行是标签 
            {    
                 img_catg.push_back( atoi( buf.c_str() ) );
				 //atoi将字符串转换成整型,标志(0,1,2,...,9),注意这里至少要有两个类别,否则会出错  
				 // 函数声明:const char *c_str(); c_str()函数返回一个指向正规字符串的指针, 内容与本字符串相同
            }    
            else    
            {    
                img_path.push_back( buf );//图像路径    
            }    
        }    
    }    
    svm_data.close();//关闭文件    
    CvMat *data_mat, *res_mat;  //定义样本矩阵,类型矩阵  
    int nImgNum = nLine / 2; //nImgNum:横坐标是样本数量,只有文本行数的一半,另一半是标签  
       
    data_mat = cvCreateMat( nImgNum, 324, CV_32FC1 );  //样本矩阵
	//第二个参数,即矩阵的列是由下面的descriptors的大小决定的,可以由descriptors.size()得到,
	//且对于不同大小的输入训练图片,这个值是不同的  
    cvSetZero( data_mat );     
    //类型矩阵,存储每个样本的类型标志    
    res_mat = cvCreateMat( nImgNum, 1, CV_32FC1 );    
    cvSetZero( res_mat );   
     
    IplImage* src;    
    IplImage* trainImg=cvCreateImage(cvSize(28,28),8,3);
	//需要分析的图片,这里默认设定图片是28*28大小,所以上面定义了324,如果要更改图片大小,
	//可以先用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<<" 处理: "<<img_path[i].c_str()<<endl;    
                   
            cvResize(src,trainImg); //读取图片,归一化处理    
            HOGDescriptor *hog=new HOGDescriptor(cvSize(28,28),cvSize(14,14),cvSize(7,7),cvSize(7,7),9);      
            vector<float>descriptors;//存放结果,数组     
            hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //Hog特征计算      
            cout<<"HOG dims: "<<descriptors.size()<<endl;        
            n=0;    
            for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++) //迭代器   
            {    
                cvmSet(data_mat,i,n,*iter);//存储HOG特征到data_mat矩阵中 
                n++;    
            }       
            cvmSet( res_mat, i, 0, img_catg[i] );    
            cout<<" 处理完毕: "<<img_path[i].c_str()<<" "<<img_catg[i]<<endl;    
    }    
        
                 
    CvSVM svm = CvSVM();//新建一个SVM      
    CvSVMParams param;//SVM训练相关参数  
    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型的数组里。     */  
                                                                 
	//data_mat样本矩阵存储上面提取到的HOG特征,存储 m 个样本的 n 个特征, res_mat 是标签矩阵
	svm.train( data_mat, res_mat, NULL, NULL, param );  //训练数据,利用训练数据和确定的学习参数,进行SVM学习 
	//F:\OpenCV-2.4.5\opencv\modules\ml\src\svm.cpp-------P1584-P1647   
	svm.save( "HOG_SVM_DATA.xml" );  //保存训练好的分类器 
	   
   //检测样本    
	IplImage *test;  
    char result[512]; 
	vector<string> img_tst_path;
	ifstream img_tst( "D:\\SVM_TEST.txt" );  //加载需要预测的图片集合,这个文本里存放的是图片全路径,不要标签
	while( img_tst )  
	{  
		if( getline( img_tst, buf ) )  
		{  
			img_tst_path.push_back( buf );  
		}  
	}  
	img_tst.close(); 

    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;    
		}
		//读入需要计算特征的图像
		IplImage* trainTempImg=cvCreateImage(cvSize(28,28),8,3);
        cvZero(trainTempImg);    
        cvResize(test,trainTempImg);    
        HOGDescriptor *hog=new HOGDescriptor(cvSize(28,28),cvSize(14,14),cvSize(7,7),cvSize(7,7),9);       
        vector<float>descriptors;//结果数组  
         //计算hog特征    
        hog->compute(trainTempImg, descriptors,Size(1,1), Size(0,0)); 
            
        cout<<"HOG dims: "<<descriptors.size()<<endl;    
        CvMat* SVMtrainMat=cvCreateMat(1,descriptors.size(),CV_32FC1);    
         int n=0;    
        for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)    
        {    
               cvmSet(SVMtrainMat,0,n,*iter);    
               n++;    
        }    
        int ret = svm.predict(SVMtrainMat);//检测结果
        sprintf( result, "%s  %d\r\n",img_tst_path[j].c_str(),ret );
        predict_txt<<result;  //输出检测结果到文本
	}
		predict_txt.close();    
		cvReleaseMat( &data_mat );    
		cvReleaseMat( &res_mat );  
		cvReleaseImage(&test);
		cvReleaseImage(&trainImg);
	  return 0;
}

 

 

    不管是手写识别,还是以后要研究的人脸识别,车牌识别,分类器都是必不可少的,又了它,接下来的工作就容易了。

 

    用画图板保存一张jpg,读入图片并加载已经训练好的分类器"HOG_SVM_DATA.xml",显示结果,OK。

 

 

// SVMmain.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "opencv2/opencv.hpp"
#include "windows.h"
#include "fstream"
using namespace std;
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
    CvSVM svm = CvSVM();
    svm.load("HOG_SVM_DATA.xml");//加载训练好的分类器xml文件
	//检测样本    
    IplImage *test;  
    char result[300]; //存放预测结果 
    test = cvLoadImage("5.jpg", 1); //待预测图片,用系统自带的画图工具随便手写,最好用粗体
    if (!test)
     {
	  MessageBox(NULL,TEXT("待预测图像不存在!"),TEXT("提示"),MB_ICONWARNING);
	  return -1;
     }
	IplImage* trainTempImg=cvCreateImage(cvSize(128,128),8,3);
     cvZero(trainTempImg);    
     cvResize(test,trainTempImg);     
     HOGDescriptor *hog=new HOGDescriptor(cvSize(128,128),cvSize(64,64),cvSize(32,32),cvSize(32,32),9);      
     vector<float>descriptors;//存放结果       
     hog->compute(trainTempImg, descriptors,Size(1,1), Size(0,0)); //Hog特征计算      
     cout<<"HOG dims: "<<descriptors.size()<<endl;  //打印Hog特征维数  ,这里是324
     CvMat* SVMtrainMat=cvCreateMat(1,descriptors.size(),CV_32FC1);   
     int n=0;    
     for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)    
     {    
           cvmSet(SVMtrainMat,0,n,*iter);   
           n++;    
      }   
    
      int ret = svm.predict(SVMtrainMat);//检测结果
	  //cout<<ret;
      sprintf(result, "%d\r\n",ret );
      cvNamedWindow("dst",1);
      cvShowImage("dst",test);
      //MessageBox(NULL,TEXT("5"),TEXT("预测结果"),MB_OK);	//(LPCWSTR)result
	  MessageBoxA(NULL,result,"预测结果",MB_OK);
      cvReleaseImage(&test);
      cvReleaseImage(&trainTempImg);
      return 0;
}

    附件里上传楼主的分类器,图片太大就不传了,需要的话说一下。

    有时间打算把其它几种算法都实现一遍,一起讨论吧。

 

 

    

 

 


    

 

 
 

 

 

 

 

 

 

    

  • 大小: 792 Bytes
  • 大小: 917 Bytes
  • 大小: 593 Bytes
  • 大小: 794 Bytes
  • 大小: 792 Bytes
  • 大小: 442 Bytes
  • 大小: 31.6 KB
分享到:
评论
1 楼 HeyGirl 2014-12-28  
  

相关推荐

Global site tag (gtag.js) - Google Analytics