OpenCV筆記(3)實現支援向量機(SVM)

為取經而來發表於2018-04-11

      參考教程:點選開啟連結

      參考教程使用的是OpenCV 2.0 版本,這裡將其修改為3.0版本。


1.SVM(支援向量機)

      SVM是一種訓練機器學習的演算法,可以用於解決分類和迴歸問題。

      正式定義:是一個能將不同類樣本在樣本空間分割的超平面,給定一些標記好的訓練樣本,SVM演算法輸出一個最優化的分隔超平面。

      

      判定是否為最優平面的依據:

      

      如上圖所示,給定一些分屬兩類的特徵點,這些點可以通過直線分割,我們要找到一個最優的直線。滿足的直線很多,該怎麼定義最優?有個評價標準是直線到所有點的距離最遠。距離樣本點太近的話,直線對噪聲敏感度高,泛化性比較差。

      SVM演算法就是找到能將某個值最大化的超平面,這個值是超平面離所有樣本點的最小距離。這個距離叫間隔。

      

      相比較其他機器學習演算法,它的優點有樣本小 、結構風險小、非線性等。

      SVM離不開核函式。簡單來說,核函式就是將低維空間的線性不可分類問題,轉化為高維空間的線性可分問題,在高維空間找到最優邊界。


2.OpenCV實現

      首先,建立訓練樣本:

//建立訓練樣本
int labels[10] = {1, 1, -1, 1, -1, -1, -1, 1, -1, -1};
float trainingData[10][2] = {{501,150},{255,10},{501,255},{10,501},{25,80},{150,300},{77,200},{300,300},{45,250},{200,200}};
Mat labelsMat(10, 1, CV_32SC1, labels);
Mat trainingDataMat(10, 2, CV_32FC1, trainingData);

      labels是訓練資料的分類標記,有兩類:1和-1。

      trainingData是訓練資料。


      設定SVM的引數:

Ptr<SVM> svm = SVM::create();//建立分類器
svm->setType(SVM::C_SVC);//SVM型別
svm->setKernel(SVM::LINEAR);//核函式的型別
svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));//演算法終止條件

      這裡opencv 2.x版本和3.x版本設定方式不一樣,下面是2.x版本:

CvSVMParams params;//建立分類器
params.svm_type = CvSVM::C_SVC;//SVM型別
params.kernel_type = CvSVM::LINEAR;//核函式的型別
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);//演算法終止條件

      其它的引數我們需要時,可以繼續設定。

      

      開始訓練:

//設定訓練資料並訓練分類器
Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);
svm->train(tData);

      下面是2.x版本的訓練:

CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);

      SVM區域分割:

Vec3b green(0, 255, 0), blue(255, 0, 0);
for (int i = 0; i < image.rows; ++i)
{
	for (int j = 0; j < image.cols; ++j)
	{
		Mat sampleMat = (Mat_<float>(1, 2) << j, i);  
		float response = svm->predict(sampleMat); 
		//float response = SVM.predict(sampleMat); 這裡有不同

		if (response == 1)
		{
			image.at<Vec3b>(i, j) = green;
		}	
		else if (response == -1)
		{
			image.at<Vec3b>(i, j) = blue;
		}	
	}
}	

      給空間上色,著色取決於SVM的分類,綠色是標記為1的點,藍色是標記為-1的點。      

 

      顯示結果:

//顯示訓練資料
int thickness = -1;
int lineType = 8;
Scalar c1 = Scalar::all(0);   //標記為1的顯示成黑點
Scalar c2 = Scalar::all(255); //標記成-1的顯示成白點
for (int i = 0; i < labelsMat.rows; i++)
{
	const float* v = trainingDataMat.ptr<float>(i); 
	Point pt = Point((int)v[0], (int)v[1]);
	if (labels[i] == 1)
	{
		circle(image, pt, 5, c1, thickness, lineType);
	}	
	else
	{
		circle(image, pt, 5, c2, thickness, lineType);
	}
}
imshow("SVM", image);
waitKey(0);


      最終結果:

      程式建立一張影像,在其中顯示訓練樣本,1為黑點,-1為白點。

      訓練得到SVM,並將影像的每一個畫素分類。分類的結果將影像分為藍綠兩部分,中間線是最優分割平面。

       


      完整程式碼:

#include <stdio.h>
#include "opencv2/opencv.hpp"

using namespace cv;
using namespace ml;

int main()
{
	int width = 512;
	int height = 512;

	Mat image = Mat::zeros(height, width, CV_8UC3);  

	//建立訓練樣本
	int labels[10] = { 1, -1, 1, 1, -1, 1, -1, 1, -1, -1 };
	float trainingData[10][2] = {{501,150},{255,10},{501,255},{10,501},{25,80},{150,300},{77,200},{300,300},{45,250},{200,200}};
	Mat labelsMat(10, 1, CV_32SC1, labels);
	Mat trainingDataMat(10, 2, CV_32FC1, trainingData);

	Ptr<SVM> svm = SVM::create();//建立分類器
	svm->setType(SVM::C_SVC);//SVM型別
	svm->setKernel(SVM::LINEAR);//核函式的型別
	svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));//演算法終止條件

	//設定訓練資料並訓練分類器
	Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);
	svm->train(tData);

	Vec3b green(0, 255, 0), blue(255, 0, 0);
	for (int i = 0; i < image.rows; ++i)
	{
		for (int j = 0; j < image.cols; ++j)
		{
			Mat sampleMat = (Mat_<float>(1, 2) << j, i);  
			float response = svm->predict(sampleMat); 
			//float response = SVM.predict(sampleMat); 這裡有不同

			if (response == 1)
			{
				image.at<Vec3b>(i, j) = green;
			}	
			else if (response == -1)
			{
				image.at<Vec3b>(i, j) = blue;
			}	
		}
	}	

	//顯示訓練資料
	int thickness = -1;
	int lineType = 8;
	Scalar c1 = Scalar::all(0);   //標記為1的顯示成黑點
	Scalar c2 = Scalar::all(255); //標記成-1的顯示成白點
	for (int i = 0; i < labelsMat.rows; i++)
	{
		const float* v = trainingDataMat.ptr<float>(i); 
		Point pt = Point((int)v[0], (int)v[1]);
		if (labels[i] == 1)
		{
			circle(image, pt, 5, c1, thickness, lineType);
		}	
		else
		{
			circle(image, pt, 5, c2, thickness, lineType);
		}
	}
	imshow("SVM", image);
	waitKey(0);
}

     

相關文章