利用SVM支持向量機對彩色圖像進行分割並使用OpenCV進行實現

 機器學習中的一個比較常用的算法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),還在學習中。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章