目前需要提煉下ml部分的介面。目的是以後方便選擇用哪種分類器。還是一頭霧水啊。。。學到哪先記錄到哪。
一。以CvSVM為例。下面是CvSVM類的定義:
class CV_EXPORTS_W CvSVM : public CvStatModel{public: // SVM type enum { C_SVC=100, NU_SVC=101, ONE_CLASS=102, EPS_SVR=103, NU_SVR=104 }; // SVM kernel type enum { LINEAR=0, POLY=1, RBF=2, SIGMOID=3 }; // SVM params type enum { C=0, GAMMA=1, P=2, NU=3, COEF=4, DEGREE=5 }; CV_WRAP CvSVM(); virtual ~CvSVM(); CvSVM( const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat* sampleIdx=0, CvSVMParams params=CvSVMParams() ); virtual bool train( const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat* sampleIdx=0,//這兩個參數好像不太用 CvSVMParams params=CvSVMParams() ); virtual float predict( const CvMat* sample, bool returnDFVal=false ) const;// virtual float predict( const CvMat* samples, CV_OUT CvMat* results ) const; CV_WRAP virtual int get_support_vector_count() const; virtual const float* get_support_vector(int i) const; virtual CvSVMParams get_params() const { return params; }; CV_WRAP virtual void clear(); static CvParamGrid get_default_grid( int param_id ); virtual void write( CvFileStorage* storage, const char* name ) const; virtual void read( CvFileStorage* storage, CvFileNode* node ); CV_WRAP int get_var_count() const { return var_idx ? var_idx->cols : var_all; }protected: virtual bool set_params( const CvSVMParams& params ); virtual bool train1( int sample_count, int var_count, const float** samples, const void* responses, double Cp, double Cn, CvMemStorage* _storage, double* alpha, double& rho ); virtual bool do_train( int svm_type, int sample_count, int var_count, const float** samples, const CvMat* responses, CvMemStorage* _storage, double* alpha ); virtual void create_kernel(); virtual void create_solver(); virtual float predict( const float* row_sample, int row_len, bool returnDFVal=false ) const; virtual void write_params( CvFileStorage* fs ) const; virtual void read_params( CvFileStorage* fs, CvFileNode* node ); CvSVMParams params; CvMat* class_labels; int var_all; float** sv; int sv_total; CvMat* var_idx; CvMat* class_weights; CvSVMDecisionFunc* decision_func; CvMemStorage* storage; CvSVMSolver* solver; CvSVMKernel* kernel;};
SVM的介面基本上跟大部分分類器的差不多。train函數參數裡一個是train_data,一個是response,最後一個是SVM對應的參數結構體。predict的參數就是一個1 x N的樣本特徵向量。下面是OpenCV提供的一個調用例子。
#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/ml/ml.hpp>using namespace cv;int main(){// Data for visual representationint width = 512, height = 512;Mat image = Mat::zeros(height, width, CV_8UC3);// Set up training datafloat labels[4] = {1.0, -1.0, -1.0, -1.0};Mat labelsMat(4, 1, CV_32FC1, labels);//對應於介面的_responsefloat trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };Mat trainingDataMat(4, 2, CV_32FC1, trainingData);//對應於介面的_train_data// Set up SVM's parametersCvSVMParams params;params.svm_type = CvSVM::C_SVC;params.kernel_type = CvSVM::LINEAR;params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);// Train the SVMCvSVM SVM;SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);Vec3b green(0,255,0), blue (255,0,0);// Show the decision regions given by the SVMfor (int i = 0; i < image.rows; ++i)for (int j = 0; j < image.cols; ++j){Mat sampleMat = (Mat_<float>(1,2) << i,j);float response = SVM.predict(sampleMat);if (response == 1)image.at<Vec3b>(j, i) = green;else if (response == -1)image.at<Vec3b>(j, i) = blue;}// Show the training dataint thickness = -1;int lineType = 8;circle( image, Point(501, 10), 5, Scalar( 0, 0, 0), thickness, lineType);circle( image, Point(255, 10), 5, Scalar(255, 255, 255), thickness, lineType);circle( image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType);circle( image, Point( 10, 501), 5, Scalar(255, 255, 255), thickness, lineType);// Show support vectorsthickness = 2;lineType = 8;int c = SVM.get_support_vector_count();for (int i = 0; i < c; ++i){const float* v = SVM.get_support_vector(i);circle( image, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thickness, lineType);}imwrite("result.png", image); // save the imageimshow("SVM Simple Example", image); // show it to the userwaitKey(0);}
二。以cascadeclassifier為例說下它如何跟ml.hpp的關係。這個關係有點複雜,不像SVM那麼標準了。
在traincascade\boost.cpp中
bool CvCascadeBoost::train( const CvFeatureEvaluator* _featureEvaluator,//包含了sum,tilted,特徵的位置等資訊 int _numSamples, int _precalcValBufSize, int _precalcIdxBufSize, const CvCascadeBoostParams& _params )
這個是訓練一個強分類器的介面,裡面調用訓練一個弱分類器的介面是:ml\ml.hpp
boolCvBoostTree::train( CvDTreeTrainData* _train_data, const CvMat* _subsample_idx, CvBoost* _ensemble )
可是從ml.hpp檔案中可以看到大部分從cvStatModel裡面繼承來的分類器的訓練函數的結構應該是:
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;
用括弧括起來的是可選的參數,但是train_data的意思是一行是一個樣本的所有特徵(好像這麼一行都叫特徵向量。。。),行數是樣本的數目。responses是響應值的矩陣,應該是一個n x 1的矩陣。
而在我們的例子裡這兩個參數都跑到CvDTreeTrainData* _train_data這裡面去了。
1.
featureEvaluator->init( (CvFeatureParams*)featureParams, numPos + numNeg, cascadeParams.winSize );
在CvCascadeBoost初始化_featureEvaluator的時候就已經根據選擇的特徵類型,正樣本的大小把所有樣本的積分圖空間申請了。還有就是在這個初始化的時候也把對應的responses申請了。
2.
bool CvCascadeClassifier::updateTrainingSet( double& acceptanceRatio)//featureEvaluator->setImage( img, isPositive ? 1 : 0, i );
在這把積分圖和response都計算出來。
3. TrainCascade\boost.cpp
data = new CvCascadeBoostTrainData( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params );
在這裡計算所有的樣本的特徵值。這樣上面1.2.步驟中的featureEvaluator的資訊也都在data中了,所以data直接送到CvBoostTree::train的介面中去了:
CvCascadeBoostTree* tree = new CvCascadeBoostTree; if( !tree->train( data, subsample_mask, this ) )//應該是訓練一個弱分類器tree { delete tree; break; } cvSeqPush( weak, &tree );//把弱分類器添加到強分類器裡面
所以我們要封裝train的時候需要把從父類vfr_machine_learning_package的介面的_train_data和response給處理下成CvDTreeTrainData的data,然後才能調用if( !tree->train( data, subsample_mask, this ) )