OpenCV SVM Training Data

2019-04-14 23:09发布

I would like to learn svm implementation by using opencv 3.00 library in C++ and Visual Studio 2013. My code:

#include<stdio.h>
#include<math.h>
#include<opencv\cv.h>
#include<opencv\highgui.h>
#include<opencv2\objdetect\objdetect.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<vector>
#include <windows.h>
#include <atlstr.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv\cvaux.hpp>

using namespace cv;
using namespace std;

#include <opencv2\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);

    float trainingData[4][2] = { { 501, 10 }, { 255, 10 }, { 501, 255 }, { 10, 501 } };
    Mat trainingDataMat(4, 2, CV_32FC1, trainingData);

    // Set up SVM's parameters

    Ptr<ml::SVM> svm = ml::SVM::create();
    // edit: the params struct got removed,
    // we use setter/getter now:
    svm->setType(ml::SVM::C_SVC);
    svm->setKernel(ml::SVM::LINEAR);
    svm->setGamma(3);

    svm->train(trainingDataMat, ml::ROW_SAMPLE, labelsMat);

    Mat res;   // output


    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) << j, i);
        float response = svm->predict(sampleMat, res);

        if (response == 1)
            image.at<Vec3b>(i, j) = green;
        else if (response == -1)
            image.at<Vec3b>(i, j) = 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;
    Mat sv = svm->getSupportVectors();

    for (int i = 0; i < sv.rows; ++i)
    {
        const float* v = sv.ptr<float>(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);

} 

After running this code, I got that error:

OpenCV Error: Bad argument < in the case of classification problem the responses must be categorical; 
either specify varType when creating TrainData, or pass integer responses > in cv::ml::SVMImpl::train, 
file C:\builds\master_PackSlave-win64-vc12-shared\opencv\modules\ml\src\svm.cpp, line 1610

I debugged that code. The debugger stops at this line: svm->train(trainingDataMat, ml::ROW_SAMPLE, labelsMat);

It says:

 First-chance exception at 0x000007FEFDA5AAAD in train.exe: Microsoft C++ exception: cv::Exception at memory location 0x00000000001CEE50.
    Unhandled exception at 0x000007FEFDA5AAAD in train.exe: Microsoft C++ exception: cv::Exception at memory location 0x00000000001CEE50.

Besides, it says that:

(Win32): Loaded 'C:\OpenCV3.0.0\opencv\build\x64\vc12\bin\opencv_world300d.dll'. Cannot find or open the PDB file.

Actually, what I understand is that the problem is related to the memory.

1条回答
Melony?
2楼-- · 2019-04-14 23:47

The type of responses cannot be float or double.

Change

float labels[4] = { 1.0, -1.0, -1.0, -1.0 };
Mat labelsMat(4, 1, CV_32FC1, labels);

to

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

BTW, if you are using Linear kernel, the only parameter is C, so you do not need to setGamma.


Another problem is the way to get the predicted response. Since each time there is only one sample to predict, if you want to use the return value as the response, you should not pass res to predict.

You can change

float response = svm->predict(sampleMat, res);

to

float response = svm->predict(sampleMat);

Otherwise, if you want to use res, then the return value is no longer the response value. But you can get the response from res instead.

You can change

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

to

if (res.at<float>(0) == 1)
    image.at<Vec3b>(i, j) = green;
else if (res.at<float>(0) == -1)
    image.at<Vec3b>(i, j) = blue;
}
查看更多
登录 后发表回答