由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,] <misc_training_alg_params> ... )=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/