OpenCV3.0或OpenCV3.1的SVM操作

OpenCV2.0 SVM代码及其分析

OpenCV 在很久以前就集成了SVM的功能,现在OpenCV升级到了3.0和3.1了,很多人都不习惯了怎么调用OpenCV中的SVM功能了。在之前OpenCV的SVM调用一直有个案例:首先,给定几组训练数据,并且给了label所对应的值。然后经过训练之后,对图像的各个位置进行预测是1还是-1。如果是1的话,用绿色来表示,如果是-1呢,用蓝色表示。并且还画出几个支持向量。 
下面给了OpenCV2.0 的SVM代码(勿喷,直接从OpenCV官方网址复制下来的)

#include 
#include 
#include 

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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

OpenCV 3.0、OpenCV3.1 的SVM训练代码

下面给出了正确的,记住是正确的代码:

例子1:


#include "stdafx.h"
#include "opencv2/opencv.hpp"
using namespace cv;
using namespace cv::ml;

int main(int, char**)
{
	int width = 512, height = 512;
	Mat image = Mat::zeros(height, width, CV_8UC3);  //创建窗口可视化

													 // 设置训练数据
	int labels[10] = { 1, -1, 1, 1,-1,1,-1,1,-1,-1 };
	Mat labelsMat(10, 1, CV_32SC1, labels);

	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 trainingDataMat(10, 2, CV_32FC1, trainingData);

	// 创建分类器并设置参数
	Ptr model = SVM::create();
	model->setType(SVM::C_SVC);
	model->setKernel(SVM::LINEAR);  //核函数

									//设置训练数据 
	Ptr tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);

	// 训练分类器
	model->train(tData);

	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_(1, 2) << j, i);  //生成测试数据
			float response = model->predict(sampleMat);  //进行预测,返回1或-1

			if (response == 1)
				image.at(i, j) = green;
			else if (response == -1)
				image.at(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(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 Simple Example", image);
	waitKey(0);

}


例子2:


#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 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(i, j) = green;
            else if (respose == -1)
                image.at(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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

运行的效果如下: 
OpenCV3.0或OpenCV3.1的SVM操作_第1张图片

为了保证代码可读性,代码没有用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);
  • 1
  • 2

下面是OpenCV 2.X 正确代码

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

如果将OpenCV 2.X的代码换到OpenCV 3.1、OpenCV3.0代码会有什么样子的结果呢? 
OpenCV3.0或OpenCV3.1的SVM操作_第2张图片 
这个很熟悉吧所以,如果傻乎乎的换,这个是行不通的。 
下面对于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 );
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

为了方便起见,将这个函数的代码的其他部分删除了。首先解释一下:samples就是训练的数据。response就是标签。通过上面,我们知道再来用Mat的时候,只能用CV_32FCV_32S。所以说,如果这些矩阵不能写什么CV_8UC1之类的了,这个是错误的。

假设我们写其他的情况,比如

label为int,labelsMat为CV_32SC1

这个是正确的。

label为float,labelsMat为CV_32FC1

这个会出现错误,这个我还没有分析出来是什么原因。错误的截图如下:OpenCV3.0或OpenCV3.1的SVM操作_第3张图片。 
下面的错误均表示为截图所示的错误。

label为int,labelsMat为CV_32FC1

同样的,这个是错误。当我们用Imagewatch插件去观察labelsMat的值的时候发现这个labelsMat的值为OpenCV3.0或OpenCV3.1的SVM操作_第4张图片。好吧,这个很明显了。

label为float,labelsMat为CV_32SC1

这个是可以运行的,但是结果肯定是错误的。同样的,值不对。

你可能感兴趣的:(OpenCV)