不知不觉,学习脑与科学已经将近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;
}
附件里上传楼主的分类器,图片太大就不传了,需要的话说一下。
有时间打算把其它几种算法都实现一遍,一起讨论吧。