機器學習中的一個比較常用的算法SVM,Support vector machine,支持向量機,具體說明可以看維基百科http://zh.wikipedia.org/wiki/SVM。
本文主要對SVM在OpenCV中的應用進行一些說明。
1、首先是SVM的構造
原文這樣說http://docs.opencv.org/modules/ml/doc/support_vector_machines.html#cvsvmparams
CvSVM::CvSVM
Default and training constructors.
- C++: CvSVM::CvSVM()
- C++: CvSVM::CvSVM(const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat&sampleIdx=Mat(), CvSVMParams params=CvSVMParams() )¶
- C++: CvSVM::CvSVM(const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat*sampleIdx=0, CvSVMParams params=CvSVMParams() )
- Python: cv2.SVM([trainData, responses[, varIdx[, sampleIdx[, params]]]]) → <SVM object>
The constructors follow conventions of CvStatModel::CvStatModel(). See CvStatModel::train() for parameters descriptions.
加紅的C++是常用到的一種構造。
使用一般是
// 對SVM進行訓練
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
CvSVM::train
Trains an SVM.
- C++: bool CvSVM::train(const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat&sampleIdx=Mat(), CvSVMParams params=CvSVMParams() )
- C++: bool CvSVM::train(const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat*sampleIdx=0, CvSVMParams params=CvSVMParams() )
- Python: cv2.SVM.train(trainData, responses[, varIdx[, sampleIdx[, params]]]) → retval
The method trains the SVM model. It follows the conventions of the generic CvStatModel::train() approach with the following limitations:
- Only the CV_ROW_SAMPLE data layout is supported.//一般是按照一行爲一個樣本,例如彩色圖像,一般是一行表示(B G R )的一個像素點,對於M*N的圖像,可以reshape成MN*3的長條形Mat,這樣一行就是一個像素點了
- Input variables are all ordered.
- Output variables can be either categorical (params.svm_type=CvSVM::C_SVC or params.svm_type=CvSVM::NU_SVC), or ordered (params.svm_type=CvSVM::EPS_SVR or params.svm_type=CvSVM::NU_SVR), or not required at all (params.svm_type=CvSVM::ONE_CLASS).//幾個參數,還未細究
- Missing measurements are not supported.
All the other parameters are gathered in the CvSVMParams structure.
2、其次是設置SVM的參數這裏在網上看到有不同的設置形式,具體可以看參數的說明吧http://www.cnblogs.com/justany/archive/2012/11/23/2784125.html
// 設置SVM參數
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
3、再然後就是對測試的樣本進行predict了
CvSVM::predict
Predicts the response for input sample(s).
- C++: float CvSVM::predict(const Mat& sample, bool returnDFVal=false ) const¶
- C++: float CvSVM::predict(const CvMat* sample, bool returnDFVal=false ) const
- C++: float CvSVM::predict(const CvMat* samples, CvMat* results) const
- Python: cv2.SVM.predict(sample[, returnDFVal]) → retval
- Python: cv2.SVM.predict_all(samples[, results]) → results
-
Parameters: - sample – Input sample for prediction.
- samples – Input samples for prediction.
- returnDFVal – Specifies a type of the return value. If true and the problem is 2-class classification then the method returns the decision function value that is signed distance to the margin, else the function returns a class label (classification) or estimated function value (regression).
- results – Output prediction responses for corresponding samples.
float response = SVM.predict(sampleMat);
這裏需要注意,sampleMat 是與上面CV_ROW_SAMPLE 相對應的,是一行sample,例如是(B G R),response 是lables標記的兩類的標記 1 或者-1 (或其他中表示都可以)
以上的部分在大部分帖子或是博客裏都能看到,接下來講一下本文的重點,就是利用SVM進行彩色圖像的分割,當然,本文所用方法略顯笨拙,由於不是計算機及相關專業出身,所用許多C/c++的潛在功能使用不暢,還需努力啊。言歸正傳
首先是對
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
中的trainingDataMat 和labelsMat 進行人爲的獲取和訓練,並且對我們得到的目標點和背景點(姑且這麼說)進行人爲標記,可以將目標點標記爲1,將背景點標記爲-1。
例如以上這個圖像,當然比較簡單一些,只有藍色圖像和背景灰色圖像。
目標顏色圖像ForeImage, 大小爲M*N, 背景圖像BackImage,大小爲m*n
那麼trainingDataMat可以這樣獲取
trainingDataMat=ForeImage.reshape(1, ForeImage.rows*ForeImage.cols);//轉換成1維,行數爲M*N的長條矩陣,,每一行爲一 // 個像素點
trainingDataMat.convertTo(trainingDataMat,CV_32FC1);//這裏轉換成浮點數,試過uchar的話在接下來的train時會報錯
這樣就將目標點轉換成了向量了(可以這麼理解),然後還有就是背景點,這裏是將BackImage中的像素點push_back到trainingDataMat的後面了,只是需要記住,0~(M*N-1)是目標藍色像素點,而M*N~(M*N+m*n-1)是背景像素點就好,因爲我們要在之後的labelsMat中對trainingDataMat的每一個行向量進行標記,將0~(M*N-1)標記爲1,將M*N~(M*N+m*n-1)標記爲-1。
labelsMat=Mat(ForeImage.cols*ForeImage.rows + BackImage.cols*BackImage.rows,1, CV_32FC1, Scalar::all(1));
for (int h=0;h<BackImage.rows;h++)
{
uchar* p_BackImage = BackImage.ptr<uchar>(h);
for (int w=0;w<BackImage.cols;w++)
{
Mat m_pTemp(1,3,CV_32FC1);
m_pTemp.at<float>(0,0) = p_BackImage[3*w+0];
m_pTemp.at<float>(0,1) = p_BackImage[3*w+1];
m_pTemp.at<float>(0,2) = p_BackImage[3*w+2];
trainingDataMat.push_back(m_pTemp);
labelsMat.at<float>(ForeImage.cols*ForeImage.rows + h*BackImage.cols + w, 0) = -1;
}
}
實際上跟其他博客中講的差不多,把http://www.cnblogs.com/justany/archive/2012/11/23/2784125.html中的小例子貼過來了
對本文中的完整工程項目VS2010+opencv2.4.2中實現,點擊這兒可以下載。
最終實現的效果圖還湊合。
對於簡單的兩類圖像的場景,該文中的方法還是比較容易實現的,對於複雜的可以嘗試高斯混合模型(GMM),還在學習中。。。