OpenCV3.0或OpenCV3.1的SVM操作

[轉載] http://blog.csdn.net/wfh2015/article/details/51163890

OpenCV2.0 SVM代碼及其分析

OpenCV 在很久以前就集成了SVM的功能,現在OpenCV升級到了3.0和3.1了,很多人都不習慣了怎麼調用OpenCV中的SVM功能了。在之前OpenCV的SVM調用一直有個案例:首先,給定幾組訓練數據,並且給了label所對應的值。然後經過訓練之後,對圖像的各個位置進行預測是1還是-1。如果是1的話,用綠色來表示,如果是-1呢,用藍色表示。並且還畫出幾個支持向量。 

下面給了OpenCV2.0 的SVM代碼(勿噴,直接從OpenCV官方網址複製下來的)

#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(3, 1, CV_32FC1, labels);

    float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
    Mat trainingDataMat(3, 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(0,255,0), blue (255,0,0);
    // 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;
    circle( image, Point(501,  10), 5, Scalar(  0,   0,   0), thickness, lineType);
    circle( image, Point(255,  10), 5, Scalar(255, 255, 255), thickness, lineType);
    circle( image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType);
    circle( image, Point( 10, 501), 5, Scalar(255, 255, 255), thickness, lineType);

    // Show support vectors
    thickness = 2;
    lineType  = 8;
    int c     = SVM.get_support_vector_count();

    for (int i = 0; i < c; ++i)
    {
        const float* v = SVM.get_support_vector(i);
        circle( image,  Point( (int) v[0], (int) v[1]),   6,  Scalar(128, 128, 128), thickness, lineType);
    }

    imwrite("result.png", image);        // save the image 

    imshow("SVM Simple Example", image); // show it to the user
    waitKey(0);

}

OpenCV 3.0、OpenCV3.1 的SVM訓練代碼

下面給出了正確的,記住是正確的代碼:

#include "opencv2/opencv.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/ml.hpp"

//using namespace cv;
//using namespace cv::ml;

int main(int argc, char** argv)
{
    // visual representation
    int width = 512;
    int height = 512;
    cv::Mat image = cv::Mat::zeros(height, width, CV_8UC3);

    // training data
    int labels[4] = { 1, -1, -1, -1 };
    float trainingData[4][2] = { { 501, 10 }, { 255, 10 }, { 501, 255 }, { 10, 501 } };
    cv::Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
    cv::Mat labelsMat(4, 1, CV_32SC1, labels);

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

    // train operation
    svm->train(trainingDataMat, cv::ml::SampleTypes::ROW_SAMPLE, labelsMat);

    // prediction
    cv::Vec3b green(0, 255, 0);
    cv::Vec3b blue(255, 0, 0);
    for (int i = 0; i < image.rows; i++)
    {
        for (int j = 0; j < image.cols; j++)
        {
            cv::Mat sampleMat = (cv::Mat_<float>(1, 2) << j, i);
            float respose = svm->predict(sampleMat);
            if (respose == 1)
                image.at<cv::Vec3b>(i, j) = green;
            else if (respose == -1)
                image.at<cv::Vec3b>(i, j) = blue;
        }
    }

    int thickness = -1;
    int lineType = cv::LineTypes::LINE_8;

    cv::circle(image, cv::Point(501, 10), 5, cv::Scalar(0, 0, 0), thickness, lineType);
    cv::circle(image, cv::Point(255, 10), 5, cv::Scalar(255, 255, 255), thickness, lineType);
    cv::circle(image, cv::Point(501, 255), 5, cv::Scalar(255, 255, 255), thickness, lineType);
    cv::circle(image, cv::Point(10, 501), 5, cv::Scalar(255, 255, 255), thickness, lineType);

    thickness = 2;
    lineType = cv::LineTypes::LINE_8;

    cv::Mat sv = svm->getSupportVectors();
    for (int i = 0; i < sv.rows; i++)
    {
        const float* v = sv.ptr<float>(i);
        cv::circle(image, cv::Point((int)v[0], (int)v[1]), 6, cv::Scalar(128, 128, 128), thickness, lineType);
    }


    cv::imshow("SVM Simple Example", image);


    cv::waitKey(0);
    return 0;
}

運行的效果如下: 

OpenCV3.1 SVM運行效果圖

爲了保證代碼可讀性,代碼沒有用using namespace cvusing namespace cv::ml;之類的代碼,全部都寫完整的名稱,命名空間+類名。比如設置SVM的核類型爲線性,寫成svm->setKernel(cv::ml::SVM::KernelTypes::LINEAR);。當然,這個只是一處地方,其他的請自己閱讀。

代碼的注意事項

雖然,大家的目標很明確:導入訓練數據+label –> Mat, 訓練,預測,顯示這些目標。但是仍然避免不了代碼錯誤。下面就是我遇到的代碼的問題

籤以及變成Mat的數據類型

其實,在本代碼中出現了一種情況就是數據類型,下面均以標籤爲例: 
這個是OpenCV3.0、OpenCV3.1正確的代碼

int labels[4] = { 1, -1, -1, -1 };
cv::Mat labelsMat(4, 1, CV_32SC1, labels);

下面是OpenCV 2.X 正確代碼

float labels[4] = {1.0, -1.0, -1.0, -1.0};
Mat labelsMat(3, 1, CV_32FC1, labels);
如果將OpenCV 2.X的代碼換到OpenCV 3.1、OpenCV3.0代碼會有什麼樣子的結果呢? 

這裏寫圖片描述 
這個很熟悉吧所以,如果傻乎乎的換,這個是行不通的。 
下面對於labellabelMat按照不同的情況進行分析。 
首先,這個labelMattrainingMat到底能取哪幾種類型?

下面,請看OpenCV3.1 源碼中的一個部分:

void setData(InputArray _samples, int _layout, InputArray _responses,
                 InputArray _varIdx, InputArray _sampleIdx, InputArray _sampleWeights,
                 InputArray _varType, InputArray _missing)
    {
        samples = _samples.getMat(); 
        responses = _responses.getMat();
        CV_Assert( samples.type() == CV_32F || samples.type() == CV_32S );
        if( !responses.empty() )
        {
            CV_Assert( responses.type() == CV_32F || responses.type() == CV_32S );
        }
    }

爲了方便起見,將這個函數的代碼的其他部分刪除了。首先解釋一下:samples就是訓練的數據。response就是標籤。通過上面,我們知道再來用Mat的時候,只能用CV_32FCV_32S。所以說,如果這些矩陣不能寫什麼CV_8UC1之類的了,這個是錯誤的。

假設我們寫其他的情況,比如

label爲int,labelsMat爲CV_32SC1

這個是正確的。

label爲float,labelsMat爲CV_32FC1

這個會出現錯誤,這個我還沒有分析出來是什麼原因。錯誤的截圖如下:錯誤。 
下面的錯誤均表示爲截圖所示的錯誤。

label爲int,labelsMat爲CV_32FC1

同樣的,這個是錯誤。當我們用Imagewatch插件去觀察labelsMat的值的時候發現這個labelsMat的值爲這裏寫圖片描述。好吧,這個很明顯了。

label爲float,labelsMat爲CV_32SC1

這個是可以運行的,但是結果肯定是錯誤的。同樣的,值不對。

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