OpenCV 3.1.0支持向量機SVM

什麼是支持向量機SVM


支持向量機是20世紀90年代中期發展起來的基於統計學習理論的一種機器學習方法,通過尋求結構化風險最小來提高學習機泛化能力,實現經驗風險和置信範圍的最小化,從而達到在統計樣本量較少的情況下,也能獲得良好統計規律的目的。它是一種二類分類模型,其基本模型定義爲特徵空間上的間隔最大的線性分類器,即支持向量機的學習策略便是間隔最大化,最終可轉化爲一個凸二次規劃問題的求解。
通俗來說,支持向量機是一個能夠將不同類樣本在樣本空間分隔的超平面。 換句話說,給定一些標記(label)好的訓練樣本 (監督式學習), SVM算法輸出一個最優化的分隔超平面。

  • 如何評判最優的超平面?
    給定分別屬於兩類(圓形和正方形)的二維點,這些點可以通過直線分割。
    這裏寫圖片描述
    可以看出,能將兩類樣本分開的直線有無數條。如何定義一條直線好壞的標準?
    距離樣本太近的直線不是最優的,因爲這樣的直線對噪聲敏感度高,泛化性較差。 因此我們的目標是找到一條直線,離所有點的距離最遠。
    由此, SVM算法的實質是找出一個能夠將某個值最大化的超平面,這個值就是超平面離所有訓練樣本的最小距離。這個最小距離用SVM術語來說叫做間隔(margin) 。 概括一下,最優分割超平面最大化訓練數據的間隔。
    這裏寫圖片描述
  • SVM的核心——核函數
    很多情況下需要分類的數據都是線性不可分數據。一種解決方法是選擇一個核函數(Kernel)K,將原始數據映射到高維空間,使原始數據線性可分問題變爲在高維空間線性可分的問題。如圖所示。
    二維線性不可分三維線性可分

    常用核函數:
    ①線性核:K(x,y)=x·y
    ②多項式核:K(x,y)=[(x·y)+1]^d
    ③高斯核:K(x,y)=exp(-|x-y|^2/d^2)
    ④Sigmoid內積核:K(x,y)=tanh(a(x·y)+b)

OpenCV在很久以前就集成了SVM功能,然而升級到3.0和3.1版本後發生了很大的變化。這裏只對3.1版本進行考慮。

  • 建立訓練樣本

    一開始訓練數據用的是int類型,即
    int colors[8][3];
    Mat trainingDataMat(8, 3, CV_32SC1, colors);

    結果報錯:
    OpenCV Error: Assertion failed (_samples.type() == CV_32F) in cv::ml::SVMImpl::do_train, file C:\buildslave64\win64_amdocl\master_PackSlave-win64-vc14-shared\opencv\modules\ml\src\svm.cpp, line 1370
    查詢官方API發現:訓練數據必須爲CV_32FC1格式。
    這裏寫圖片描述

  • 訓練支持向量機

//初始化SVM
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::Types::C_SVC);
    svm->setKernel(SVM::KernelTypes::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));

    //訓練
    svm->train(trainingDataMat,SampleTypes::ROW_SAMPLE , labelsMat);
  • 預測
Mat sample = (Mat_<float>(1, 3) << color[0] / pixelAmount, color[1] / pixelAmount, color[2] / pixelAmount);
float response = svm->predict(sample);

源代碼:
簡單的通過顏色來對蘋果和香蕉進行分類,讀取樣本圖片並計算其像素顏色平均值作爲訓練數據進行訓練。

#include "opencv2/opencv.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/ml.hpp"
#include <iostream>
using namespace std;

using namespace cv;
using namespace cv::ml;

int main(int argc, char** argv)
{
    //訓練數據的分類 1.0蘋果-1.0香蕉
    int labels[8] = { 1,1,1,1,-1,-1,-1,-1 };
    Mat labelsMat(8, 1, CV_32SC1, labels);
    //訓練數據矩陣
    float colors[8][3];

    //蘋果樣本數據 
    for (int i = 0; i < 4; i++){

        string temp = "apple";
        char n[1];
        itoa(i, n, 10);
        temp = temp.append(n);
        string imgName = temp.append(".png");

        //cout << imgName;
        Mat testImg = imread(imgName);
        //imshow(imgName, testImg);

        int color[3] = { 0, 0, 0 };
        int pixelAmount = testImg.rows*testImg.cols;
        for (int j = 0; j < testImg.rows; j++){
            for (int k = 0; k < testImg.cols; k++){
                color[0] += testImg.at<Vec3b>(j, k)[0];
                color[1] += testImg.at<Vec3b>(j, k)[1];
                color[2] += testImg.at<Vec3b>(j, k)[2];
            }
        }
        cout << "蘋果" << i << " : " << color[0] / pixelAmount << " " << color[1] / pixelAmount << " " << color[2] / pixelAmount << endl;
        colors[i][0] = color[0] / pixelAmount;
        colors[i][1] = color[1] / pixelAmount;
        colors[i][2] = color[2] / pixelAmount;
    }
    //香蕉樣本數據
    for (int i = 0; i < 4; i++){

        string temp = "banana";
        char n[1];
        itoa(i, n, 10);
        temp = temp.append(n);
        string imgName = temp.append(".png");

        //cout << imgName;
        Mat testImg = imread(imgName);
        //imshow(imgName, testImg);

        int color[3] = { 0, 0, 0 };
        int pixelAmount = testImg.rows*testImg.cols;
        for (int j = 0; j < testImg.rows; j++){
            for (int k = 0; k < testImg.cols; k++){
                color[0] += testImg.at<Vec3b>(j, k)[0];
                color[1] += testImg.at<Vec3b>(j, k)[1];
                color[2] += testImg.at<Vec3b>(j, k)[2];
            }
        }
        cout << "香蕉" << i << " : " << color[0] / pixelAmount << " " << color[1] / pixelAmount << " " << color[2] / pixelAmount << endl;
        colors[i + 4][0] = color[0] / pixelAmount;
        colors[i + 4][1] = color[1] / pixelAmount;
        colors[i + 4][2] = color[2] / pixelAmount;
    }
    Mat trainingDataMat(8, 3, CV_32FC1, colors);

    //初始化SVM
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::Types::C_SVC);
    svm->setKernel(SVM::KernelTypes::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));

    //訓練
    svm->train(trainingDataMat,SampleTypes::ROW_SAMPLE , labelsMat);

    //測試圖片
    for (int i = 0; i < 4; i++){

        string temp = "test";
        char n[1];
        itoa(i, n, 10);
        temp = temp.append(n);
        string imgName = temp.append(".png");

        //cout << imgName;
        Mat testImg = imread(imgName);
        //imshow(imgName, testImg);

        int color[3] = { 0, 0, 0 };
        int pixelAmount = testImg.rows*testImg.cols;
        for (int j = 0; j < testImg.rows; j++){
            for (int k = 0; k < testImg.cols; k++){
                color[0] += testImg.at<Vec3b>(j, k)[0];
                color[1] += testImg.at<Vec3b>(j, k)[1];
                color[2] += testImg.at<Vec3b>(j, k)[2];
            }
        }
        cout << "測試" << i << " : " << color[0] / pixelAmount << " " << color[1] / pixelAmount << " " << color[2] / pixelAmount << endl;
        Mat sample = (Mat_<float>(1, 3) << color[0] / pixelAmount, color[1] / pixelAmount, color[2] / pixelAmount);
        float response = svm->predict(sample);
        if (response == 1.0){
            cout << "蘋果" << endl;
        }
        else if (response == -1.0){
            cout << "香蕉" << endl;
        }
    }

    waitKey();
    int a;
    cin >> a;
    return 0;
}

樣本圖片:
這裏寫圖片描述
這裏寫圖片描述
測試圖片:
這裏寫圖片描述
測試結果:
這裏寫圖片描述

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