官方源代码:
#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**)
{
// Data for visual representation
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);
// Set up training data
int labels[4] = {1, -1, -1, -1};
Mat labelsMat(4, 1, CV_32SC1, 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
SVM::Params params;
params.svmType = SVM::C_SVC;
params.kernelType = SVM::LINEAR;
params.termCrit = TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6);
// Train the SVM
Ptr<SVM> svm = StatModel::train<SVM>(trainingDataMat, ROW_SAMPLE, labelsMat, 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) << 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;
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);
}
说明
1.设置训练数据
该练习的训练数据由属于两个不同类之一的一组标记的2D点形成; 其中一个课程包括一个点和另一个三点。
float labels[4] = {1.0, -1.0, -1.0, -1.0};
float trainingData[4][2] = {{501, 10}, {255, 10}, {501, 255}, {10, 501}};
之后使用的功能CvSVM :: train要求将训练数据作为浮点数的Mat对象存储。因此,我们从上面定义的数组创建这些对象:
Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
Mat labelsMat (4, 1, CV_32FC1, labels);
2.设置SVM的参数
在本教程中,我们在最简单的情况下介绍了SVM的理论,当训练示例被分为两类是可分离的。然而,SVM可以用于各种各样的问题(例如,非线性可分离数据的问题,使用内核函数的SVM来提高示例的维度等)。因此,我们必须在训练SVM之前定义一些参数。这些参数存储在类CvSVMParams的对象中。
ml::SVM::Params params;
params.svmType = ml::SVM::C_SVC;
params.kernelType = ml::SVM::LINEAR;
params.termCrit = TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6);
-
-
SVM类型。我们在这里选择可用于n类分类的类型ml :: SVM :: C_SVC(n 2)。该参数在属性ml :: SVM :: Params.svmType中定义。
注意
SVM类型的重要特征CvSVM :: C_SVC处理类的不完全分离(即训练数据是非线性可分离的)。这个特性在这里并不重要,因为数据是线性可分的,我们选择这个SVM类型是最常用的。
-
SVM内核的类型。我们没有谈到内核函数,因为它们对于我们正在处理的训练数据并不感兴趣。不过,让我们简要介绍一下内核函数的主要思路。它是对训练数据进行的映射,以改进其与线性可分离数据集的相似性。该映射包括增加数据的维度,并使用内核函数高效地进行。我们在这里选择ml :: SVM :: LINEAR,这意味着没有做任何映射。该参数在属性ml :: SVMParams.kernel_type中定义。
-
算法的终止标准。在SVM训练过程中所实施的解决受约束的二次优化问题迭代的方式。这里我们指定最大迭代次数和公差误差,因此即使最佳超平面尚未计算,我们允许算法在较少数量的步骤中完成。该参数在一个结构中定义cvTermCriteria。
-
3.训练SVM
我们称之为CvSVM :: train方法构建SVM模型。
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);
4.由SVM分类的区域
方法CvSVM :: 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) << 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;
}
5. 支持向量
我们在这里使用几种方法来获取有关支持向量的信息。方法CvSVM :: get_support_vector_count输出在问题中使用的支持向量的总数,并使用方法CvSVM :: get_support_vector,我们使用索引获取每个支持向量。我们在这里使用这种方法来找到支持向量的训练示例并突出显示。
int c = SVM.get_support_vector_count();
for (int i = 0; i < c; ++i)
{
const float* v = SVM.get_support_vector(i); // get and then highlight with grayscale
circle( image, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thickness, lineType);
}
结果
- 代码打开一个图像并显示两个类的训练示例。一个类的点用白色圆圈表示,黑色的点用于另一个类。
- SVM被训练并用于对图像的所有像素进行分类。这导致在蓝色区域和绿色区域中的图像的划分。两个区域之间的边界是最优分离超平面。
- 最后,训练样本周围使用灰色戒指显示支持向量。