由Opencv 机器学习部分在线文档
http://docs.opencv.org/modules/ml/doc/ml.html
得知,这些机器学习方法K-Nearest Neighbors等都继承于基类CvStatModel,此基类中有save,load等方法,http://wiki.opencv.org.cn/index.php/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B8%AD%E6%96%87%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8C#CvStatModel 中文解释说save,load方法会调用write和read方法。
在早期opencv中,基类的load和save都是纯虚函数,即子类要加以实现。
class CvStatModel
{
public:
/* CvStatModel(); */
/* CvStatModel( const CvMat* train_data ... ); */
virtual ~CvStatModel();
virtual void clear()=0;
/* virtual bool train( const CvMat* train_data, [int tflag,] ..., const CvMat* responses, ...,
[const CvMat* var_idx,] ..., [const CvMat* sample_idx,] ...
[const CvMat* var_type,] ..., [const CvMat* missing_mask,] ... )=0;
*/
/* virtual float predict( const CvMat* sample ... ) const=0; */
virtual void save( const char* filename, const char* name=0 )=0;
virtual void load( const char* filename, const char* name=0 )=0;
virtual void write( CvFileStorage* storage, const char* name )=0;
virtual void read( CvFileStorage* storage, CvFileNode* node )=0;
};
而后来opencv对此做了修改,修改成为了虚函数。如下:
class CV_EXPORTS_W CvStatModel
{
public:
CvStatModel();
virtual ~CvStatModel();
virtual void clear();
CV_WRAP virtual void save( const char* filename, const char* name=0 ) const;
CV_WRAP virtual void load( const char* filename, const char* name=0 );
virtual void write( CvFileStorage* storage, const char* name ) const;
virtual void read( CvFileStorage* storage, CvFileNode* node );
protected:
const char* default_model_name;
};
这个虚函数声明说明,任何继承自CvStatModel的类都可以调用save,load等。
但是在使用KNN时调用save会出错,我们查看ml.hpp 文件发现opencv中K-Nearest Neighbors没有实现load,save,write,read这些函数。如下:
// k Nearest Neighbors
class CV_EXPORTS_W CvKNearest : public CvStatModel
{
public:
CV_WRAP CvKNearest();
virtual ~CvKNearest();
CvKNearest( const CvMat* trainData, const CvMat* responses,
const CvMat* sampleIdx=0, bool isRegression=false, int max_k=32 );
virtual bool train( const CvMat* trainData, const CvMat* responses,
const CvMat* sampleIdx=0, bool is_regression=false,
int maxK=32, bool updateBase=false );
virtual float find_nearest( const CvMat* samples, int k, CV_OUT CvMat* results=0,
const float** neighbors=0, CV_OUT CvMat* neighborResponses=0, CV_OUT CvMat* dist=0 ) const;
CV_WRAP CvKNearest( const cv::Mat& trainData, const cv::Mat& responses,
const cv::Mat& sampleIdx=cv::Mat(), bool isRegression=false, int max_k=32 );
CV_WRAP virtual bool train( const cv::Mat& trainData, const cv::Mat& responses,
const cv::Mat& sampleIdx=cv::Mat(), bool isRegression=false,
int maxK=32, bool updateBase=false );
virtual float find_nearest( const cv::Mat& samples, int k, cv::Mat* results=0,
const float** neighbors=0, cv::Mat* neighborResponses=0,
cv::Mat* dist=0 ) const;
CV_WRAP virtual float find_nearest( const cv::Mat& samples, int k, CV_OUT cv::Mat& results,
CV_OUT cv::Mat& neighborResponses, CV_OUT cv::Mat& dists) const;
virtual void clear();
int get_max_k() const;
int get_var_count() const;
int get_sample_count() const;
bool is_regression() const;
virtual float write_results( int k, int k1, int start, int end,
const float* neighbor_responses, const float* dist, CvMat* _results,
CvMat* _neighbor_responses, CvMat* _dist, Cv32suf* sort_buf ) const;
virtual void find_neighbors_direct( const CvMat* _samples, int k, int start, int end,
float* neighbor_responses, const float** neighbors, float* dist ) const;
protected:
int max_k, var_count;
int total;
bool regression;
CvVectors* samples;
};
此时如果用KNN.save时会调用基类的虚函数,而我们无法知道基类的save是如何实现的。
而在可以保存的svm等方法中发现了,read和write的声明
// SVM model
class CV_EXPORTS_W CvSVM : public CvStatModel
{
public:
// SVM type
virtual void write( CvFileStorage* storage, const char* name ) const;
virtual void read( CvFileStorage* storage, CvFileNode* node );
我们这里省略了大部分,可猜测不同的方法保存是有差别的。
我们没有在KNN代码中找到任何实现write和read的函数,通过运行出错,我们理解为opencv并没有实现对knn的write,这是源于knn的特点的,knn所谓的train也是只是保存样本数据,没有生成分类的模型,这个train函数也不是严格意义的训练。
如果我们想保存这些样本数据,可以通过保存样本特征在文件中,一次读入,可以参考http://www.aishack.in/2010/10/k-nearest-neighbors-in-opencv/