OpenCV进阶---介绍SVM

1. 学习目标:

目标 OpenCV函数
训练 cv::ml::SVM::train
测试 cv::ml::SVM::test

2. OpenCV理论

       支持向量机(SVM)是由超平面定义的判别分类器。 换句话说,给定标记的训练数据(监督学习),算法输出最佳超平面,用来对新示例进行分类。

对于属于两个类别之一的线性可分的2D点集合,找到分离的直线。:

                                                                                          

note

        在这个例子中,我们处理笛卡尔平面中的线和点,而不是高维空间中的超平面和向量。重要的是要理解这只是因为我们的直觉更好地建立在容易想象的例子上。但是,相同的概念适用于要分类的示例位于维度大于2的空间中的任务。
       在上图中,您可以看到存在多行提供问题的解决方案。他们中的任何一个比其他人更好吗?我们可以直观地定义一个标准来估计线条的价值:如果线条过于接近点,则线条很差,因为它会对噪声敏感,泛化能力弱。因此,我们的目标应该是尽可能从所有点找到通过的线。

然后,SVM算法的操作基于找到给出训练样本的最大距离的超平面。这个距离在SVM理论中称为“margin”。因此,最佳超平面为训练的输出结果。

  •                                                                                  

如何计算最佳超平面 

                                                           f(x)= \beta ^{_{0}}+\beta ^{T}x

β为权重(斜率),β0为偏置(截距)。

通过缩放β和β0,可以以无数种不同的方式表示最佳超平面(平行线)。 在超平面的所有可能表示中,一般所选择的是

                                                        

其中x表示最接近超平面的训练样本。 通常,最接近超平面的训练样本称为支持向量。 这种表示称为规范超平面

下面计算点x与超平面(β,β0)的距离distance:

                                                         

特别地,对于规范超平面,分子等于1并且到支持向量的距离是

                                               

上文中引入的边距margin(此处表示为M)是距离支持向量距离的两倍: 

                                                             

最后,使M最大化的问题等同于在受到某些约束的情况下最小化函数L(β)的问题。 约束模拟超平面的要求,以正确地分类所有训练样本xi。从形式上看, 

                                        

yi为样本对应的labels。

这是拉格朗日优化的问题,其可以使用拉格朗日乘数来求解以获得权重向量β和最优超平面的偏差β0。

详细推导请看SVM数学理论基础

 

3. Code

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/ml.hpp>
using namespace cv;
using namespace cv::ml;
int main(int, char**)
{
    // Set up training data
    int labels[4] = {1, -1, -1, -1};
    float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
    Mat trainingDataMat(4, 2, CV_32F, trainingData);
    Mat labelsMat(4, 1, CV_32SC1, labels);
    // Train the SVM
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
    svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);
    // Data for visual representation
    int width = 512, height = 512;
    Mat image = Mat::zeros(height, width, CV_8UC3);
    // Show the decision regions given by the SVM
    Vec3b green(0,255,0), blue(255,0,0);
    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);
            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;
    circle( image, Point(501,  10), 5, Scalar(  0,   0,   0), thickness );
    circle( image, Point(255,  10), 5, Scalar(255, 255, 255), thickness );
    circle( image, Point(501, 255), 5, Scalar(255, 255, 255), thickness );
    circle( image, Point( 10, 501), 5, Scalar(255, 255, 255), thickness );
    // Show support vectors
    thickness = 2;
    Mat sv = svm->getUncompressedSupportVectors();
    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);
    }
    imwrite("result.png", image);        // save the image
    imshow("SVM Simple Example", image); // show it to the user
    waitKey();
    return 0;
}

4. 代码详解

training data

int labels[4] = {1, -1, -1, -1};
float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };

之后将使用的函数cv :: ml :: SVM :: train需要将训练数据存储为浮点数的cv :: Mat对象。 因此,我们从上面定义的数组创建这些对象:

    Mat trainingDataMat(4, 2, CV_32F, trainingData);
    Mat labelsMat(4, 1, CV_32SC1, labels);

设置SVM参数

    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::LINEAR);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
  • SVM的类型。我们在这里选择可用于n级分类的类型C_SVC(n≥2)。这种类型的重要特征是它处理非线性类效果好(即,当训练数据是非线性可分离的时)。此功能在这里并不重要,因为数据是线性可分的,我们选择此SVM类型只是最常用的。
  • SVM内核的类型。我们还没有谈到内核函数,因为它们对我们正在处理的训练数据不感兴趣。不过,我们现在简要解释一下内核函数背后的主要思想。它是对训练数据的映射,以改善其与线性可分离数据集的相似性。此映射包括增加数据的维度,并使用内核函数高效完成。我们在这里选择LINEAR类型,这意味着不需要映射。此参数使用cv :: ml :: SVM :: setKernel定义。
  • 算法的停止条件。实现SVM训练过程以迭代方式求解约束二次优化问题。在这里,我们指定了最大迭代次数和容差误差,因此我们允许算法以较少的步数完成,即使尚未计算最佳超平面。此参数在结构cv :: TermCriteria中定义。
  • 训练SVM我们调用方法cv :: ml :: SVM :: train来构建SVM模型。
    svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);

 SVM分类

方法cv :: ml :: SVM :: predict用于使用训练的SVM对输入样本进行分类。 在此示例中,我们使用此方法以根据SVM执行的预测为空间着色。 换句话说,遍历图像将其像素解释为笛卡尔平面的点。 每个点都根据SVM预测的类别着色; 如果它是带有标签1的类,则为绿色;如果是带有标签-1的类,则为蓝色。

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

 支持向量

       我们在这里使用几种方法来获取有关支持向量的信息。 方法cv :: ml :: SVM :: getSupportVectors获取所有支持向量。 我们在这里使用这些方法来查找支持向量的训练样本并突出显示它们。

thickness = 2;
    Mat sv = svm->getUncompressedSupportVectors();
    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);
    }

Result

  • 代码打开一个图像并显示两个类的训练示例。 一个类的点用白色圆圈表示,另一个类为黑色。
  • SVM经过训练并用于对图像的所有像素进行分类。 这导致图像在蓝色区域和绿色区域中分割。 两个区域之间的边界是最佳分离超平面。
  • 最后,使用训练示例周围的灰色环显示支持向量。
  •             

猜你喜欢

转载自blog.csdn.net/red_ear/article/details/88964177
今日推荐