在opencv的ml模块中有个统计模型类,而其他的比如朴素贝叶斯分类器、knn、svm等等其他模型都是基于该模型上派生出来的。所以先介绍下该模型。
该类的定义在文件“opencv\sources\modules\ml\include\opencv2\ml\ml.hpp”中:
class CV_EXPORTS_W CvStatModel{public: CvStatModel(); virtual ~CvStatModel(); virtual void clear(); //save函数用来将整个模型状态保持到指定的XML 或者YAML文件中,并按照具体的类看是使用默认的名字还是指定的名字. //使用了CxCore中的数据持久性功能。 CV_WRAP virtual void save(const char* filename, const char* name = 0) const; // load函数从指定的XML或者YAML中装载模型中指定的或者默认名字部分 //而之前的被装载的模型已经通过CvStatModel::clear()初始化了. CV_WRAP virtual void load(const char* filename, const char* name = 0); // write函数会以将完整的模型以指定或者默认名字存储到文件中。该函数会被 CvStatModel::save()调用. virtual void write(CvFileStorage* storage, const char* name) const; // read函数会从文件中指定的节点上读取整个模型。使用函数GetFileNodeByName()来定位节点。 //之前的模型也是需要被函数 CvStatModel::clear()初始化的. virtual void read(CvFileStorage* storage, CvFileNode* node); //下面这几个函数被注释掉是因为它们不存在该模型中,只是对于其他的ml模型 //来说是有的,所以这里就完全不设定该函数,只是虽然各自派生的ml模型有各 //自的这几个函数,可是行为还是很类似的,所以就统一在该基类中介绍了。 // virtual bool train( const Mat& train_data, [int tflag,] ..., // const Mat& responses, ..., [const Mat& var_idx,] ..., // [const Mat& sample_idx,] ... [const Mat& var_type,] ..., // [const Mat& missing_mask,]... // )=0; // virtual float predict( const Mat& sample ... ) const=0; protected: const char* default_model_name;};
实现部分:
在文件“\opencv\sources\modules\ml\src\inner_functions.cpp”中#include "precomp.hpp"//构造函数,对该类唯一的类成员进行赋值CvStatModel::CvStatModel(){ default_model_name = "my_stat_model";}//虚析构函数,调用虚函数clear()来执行不同的类的清理工作CvStatModel::~CvStatModel(){ clear();}void CvStatModel::clear(){}//写函数,将数据写入到指定的文件中void CvStatModel::save( const char* filename, const char* name ) const{//存储文件的指针初始化 CvFileStorage* fs = 0;// CV_FUNCNAME( "CvStatModel::save" ); __BEGIN__;//打开传入该函数的文件 CV_CALL( fs = cvOpenFileStorage( filename, 0, CV_STORAGE_WRITE )); if( !fs ) CV_ERROR( CV_StsError, "Could not open the file storage. Check the path and permissions" );//如果未传入模型的名字,则写入默认的模型名字 write( fs, name ? name : default_model_name ); __END__;//释放指向文件的指针 cvReleaseFileStorage( &fs );}//装载指定文件中的数据void CvStatModel::load( const char* filename, const char* name ){ CvFileStorage* fs = 0; CV_FUNCNAME( "CvStatModel::load" ); __BEGIN__; CvFileNode* model_node = 0; CV_CALL( fs = cvOpenFileStorage( filename, 0, CV_STORAGE_READ )); if( !fs ) EXIT; if( name ) model_node = cvGetFileNodeByName( fs, 0, name ); else { CvFileNode* root = cvGetRootFileNode( fs ); if( root->data.seq->total > 0 ) model_node = (CvFileNode*)cvGetSeqElem( root->data.seq, 0 ); } read( fs, model_node ); __END__;//释放指向文件的指针 cvReleaseFileStorage( &fs );}//写函数void CvStatModel::write( CvFileStorage*, const char* ) const{ OPENCV_ERROR( CV_StsNotImplemented, "CvStatModel::write", "" );}void CvStatModel::read( CvFileStorage*, CvFileNode* ){ OPENCV_ERROR( CV_StsNotImplemented, "CvStatModel::read", "" );}
该文件中还有大量的数学计算函数,比如矩阵分解,从GMM上采样等等。
备注:好了这里就主要介绍下几个不存在于该类的函数。
1、模型训练函数bool CvStatModel::train(const Mat& train_data, [int tflag,] ..., const Mat& responses, ..., [constMat& var_idx,] ..., [const Mat& sample_idx,] ... [const Mat& var_type,] ...,[const Mat& missing_mask,]... ) = 0
该函数是通过输入特征向量和对应的输出目标值(responses)来训练模型的。输入和输出的向量/值都是以矩阵的形式传递的。默认情况下输入特征向量是以train_data rows,也就是一个训练样本中所有的成分(特征)是连续存储的。不过一些算法是需要处理它们的转置形式的,只要当基于所有的输入集合中的特征都是连续存储的(个人:其实也就是存储成一个矩阵而不是链表).如果两种布局都支持的话,那么该方法将会有个tflag参数用来指定具体的情况。
• tflag=CV_ROW_SAMPLE 特征向量以行形式存储; • tflag=CV_COL_SAMPLE 特征向量以列形式存储。train_data 必须是 CV_32FC1 (32位浮点类型,单通道) 格式的. 而Responses通常存储成1D向量(一行或者一列)形式 ,格式有: CV_32SC1(只在分类的时候用); CV_32FC1, 其中一个输入向量对应着一个目标值.
对于分类问题来说, responses 是离散的标签; 对于回归问题来说,responses 是模型函数需要逼近的值。 一些算法只能用来分类;一些算法只能用来回归;还有一些能用在两方面。对于回归来说,输出变量的类型既可以是通过独立的参数传递也可以是var_type向量的最后一个元素: • CV_VAR_CATEGORICAL 输出变量是离散的类别标签; • CV_VAR_ORDERED(=CV_VAR_NUMERICAL) 输出的值是有序的,也就是说两个不同的值可以进行数值对比,当然这是一个回归问题。 输入变量的类型可以通过var_type指定。大多数算法只能处理有序的输入变量。 许多ML模型会在一个特征子集或者是训练集中样本子集上进行训练,为了能够容易的在不同状态间选择,该函数还包含了var_idx 和 sample_idx 这两个参数。前者是用来指定感兴趣的变量(特征);后者是用来指定感兴趣的样本。这两个向量都是整型向量 (CV_32SC1) ,(当然是基于0开始索引的)或者是基于激活的变量/样本的8位 (CV_8UC1)标记。当传递一个NULL指针给这两个参数时,也就意味着所有的变量/样本都会用来训练。 另外,许多算法可以处理缺失的值的情况,也就是说当某个具体的训练样本的特征的值是未知的(比如,忘记测量一个病人在礼拜一时候的体温)参数 missing_mask,是一个与train_data 具有相同size的8位的矩阵,它用来标记缺失的值(即该标记矩阵中非0值) 通常来说,在进入到训练阶段之前的模型的状态是需要调用CvStatModel::clear()来重置的 ;不过某些算法会让你选择是否使用新的训练数据来更新模型的状态而不是重置他们。float CvStatModel::predict(const Mat& sample, ...) const
该函数用来对一个新的样本进行预测其response。在分类问题中,该方法返回一个类别标签;在回归问题上,盖方法返回一个函数值 。输入的样本必须与传递给train函数中train_data一样大的特征维度。如果var_idx 参数传递给了train,那么记得,也同时只提取必须的特征给该函数。后缀的const表示该预测函数不会影响到模型的内部状态,所以该方法可以安全的在不同的线程中被调用。