剛把OpenCV更新到2.4.3版,來試驗一下它的SVM好不好用。首先當然要瞭解支持向量機器 (Support Vector Machine)和非線性支持向量機器 (Non-linear SVMs),先前的SVM實驗也可以參考。
支持向量機(Support Vector Machine, SVM)是一種監督式學習的方法,一般是應用於分類(Classification/supervised learning)等相關議題上。SVM 基本運作模式如下:在給定一群訓練樣本之下,每個樣本會分別對應至兩個不同的類別(Category),SVM 會嘗試從建構一個模型(Model),並利用此模型將每一個樣本分配到一個類別上。
典型的SVM是一種二元分類器(Two-class classifier),因此,先針對典型SVM來進行說明。

在二元分類中,SVM嘗試在訓練資料(training data)所構成的空間中,尋找一個超平面(hyperplane)能將不同類別的資料完美的分開,而且,希望此超平面與不同的類別的距離愈大愈好。如圖所示,藍色圓形為第一個類別(標記為+1),紅色方形為第二個類別(標記為-1),而SVM則想要找出的超平面即是wx+b=0,此超平面可以使得兩個類別(class)的距離最大。

- 環境設定:Visual Studio 2010 安裝 OpenCV 2.4
- 範例程式:C:\OpenCV2.4.3\samples\cpp\tutorial_code\ml\introduction_to_svm\introduction_to_svm.cpp
程式碼:
/**
Theme: Introduction to Support Vector Machines
compiler: Visual Studio 2010 with OpenCV 2.4.3
Date: 101/11/12
Author: HappyMan
Blog: https://cg2010studio.wordpress.com/
*/
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
using namespace cv;
int main()
{
// Data for visual representation
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);
// Set up training data
float labels[4] = {1.0, -1.0, -1.0, -1.0};
Mat labelsMat(4, 1, CV_32FC1, labels);
Point point1 = Point(501.0, 100.0);
Point point2 = Point(255.0, 100.0);
Point point3 = Point(501.0, 255.0);
Point point4 = Point(100.0, 501.0);
float trainingData[4][2] = { {point1.x, point1.y}, {point2.x, point2.y}, {point3.x, point3.y}, {point4.x, point4.y} };
Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
// Set up SVM's parameters
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
// Train the SVM
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
Vec3b green(100,255,100), blue(255,100,100);
// Show the decision regions given by the SVM
for (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 data
int thickness = -1;
int lineType = 8;
int radius = 3;
circle(image, point1, radius, labels[0]==1.0?Scalar(0,0,0):Scalar(255, 255, 255), thickness, lineType);
circle(image, point2, radius, labels[1]==1.0?Scalar(0,0,):Scalar(255, 255, 255), thickness, lineType);
circle(image, point3, radius, labels[2]==1.0?Scalar(0,0,0):Scalar(255, 255, 255), thickness, lineType);
circle(image, point4, radius, labels[3]==1.0?Scalar(0,0,0):Scalar(255, 255, 255), thickness, lineType);
// Show support vectors
thickness = 2;
lineType = 8;radius = 6;
int count = SVM.get_support_vector_count();
for (int i = 0; i < count; ++i)
{
const float* vector = SVM.get_support_vector(i);
circle( image, Point( (int) vector[0], (int) vector[1]), radius, Scalar(128, 128, 128), thickness, lineType);
}
// save the image
imwrite("result.png", image);
// show it to the user
imshow("SVM HappyMan Example", image);
waitKey(0);
}
執行結果:

此圖分類得相當完美!
改變標籤:
- float labels[4] = {-1.0, 1.0, 1.0, -1.0};

然而此圖無法分類,該怎麼辦?剛好範例顏色不是藍就是綠,該站哪一邊比較好呢:P?
參考:OpenCV v2.4.3 documentation – Introduction to Support Vector Machines、WiKi – 支持向量機、支援向量機(Support Vector Machine, SVM)之多重分類。
Comments on: "[OpenCV] SVM 實驗" (1)
[…] 既SVM實驗後,接著實驗高階的SVM。首先還是要瞭解支持向量機器 (Support Vector Machine)和非線性支持向量機器 (Non-linear SVMs),先前的SVM實驗也可以參考。 […]
讚讚