利用C++和OpenCV3设计支持向量机SVM分类器

发布时间:2024-08-28 09:01

目录

    • 一、代码示例
    • 二、SVM训练过程
      • 2.1 数据准备
      • 2.2 初始化SVM参数
      • 2.3 训练SVM
      • 2.4 保存数据
      • 2.5 加载保存的数据
      • 2.6 测试数据
    • 三、显示支持向量机分类区域
      • 3.1 初始化图像显示区域
      • 3.2 显示SVM决策区域
      • 3.3 显示训练数据
      • 3.4 显示支持向量
    • 四、输出结果

一、代码示例

话不多说,直接上代码:

#include 
#include 
#include
using namespace std;
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[4] = { 1, -1, -1, -1 };
	float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
	Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
	Mat labelsMat(4, 1, CV_32SC1, labels);
	//训练SVM
	Ptr<SVM> svm = SVM::create();//创建一个svm对象
	svm->setType(SVM::C_SVC); //设置SVM公式类型
	svm->setKernel(SVM::LINEAR);//设置SVM核函数类型
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));//设置SVM训练时迭代终止条件
	svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);//训练数据
	//显示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;
		}
	//显示训练数据
	int thickness = -1;//-1表示实心
	int lineType = 8;
	circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);//半径为5
	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);
	//显示支持向量
	thickness = 2;
	lineType = 8;
	Mat sv = svm->getUncompressedSupportVectors();
	//cout << sv << endl;//输出结果:[501,10; 255,10; 501,255]为什么???
	for (int i = 0; i < sv.rows; ++i)
	{
		const float* v = sv.ptr<float>(i);//指向矩阵sv的第i行
		circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);//灰色,半径为6
	}
	imwrite(\"result.png\", image);        //保存图像
	imshow(\"SVM Simple Example\", image); //显示图像
	waitKey(0);
}

二、SVM训练过程

支持向量机SVM的训练过程主要包括以下四个方面:

2.1 数据准备

本案例中的训练数据如下:

//设置训练数据
	int labels[4] = { 1, -1, -1, -1 };
	float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
	Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
	Mat labelsMat(4, 1, CV_32SC1, labels);

这里主要设置了四个坐标点,其中第一个坐标点的标签为1,后三个坐标点的标签为-1。最后将其转化为SVM能识别的所需的类型。
【注】:
①样本数据必须是CV_32FC1类型。这是由opencv3版本决定的;
②样本标签必须是CV_32SC1,opencv3后从int数组转换为CV_32SC1类型,而opencv2是从float数据转换。

2.2 初始化SVM参数

初始化参数程序如下:

Ptr<SVM> svm = SVM::create();//创建一个svm对象
svm->setType(SVM::C_SVC); //设置SVM公式类型
svm->setKernel(SVM::LINEAR);//设置SVM核函数类型
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));//设置SVM训练时迭代终止条件

SVM::create() ——创建一个SVM对象

svm->setType(SVM::C_SVC); ——设置SVM公式类型,包括C_SVC、NU_SVC、ONE_CLASS、EPS_SVR、NU_SVR,用于指定分类、回归等,默认为C_SVC;

svm->setKernel(SVM::LINEAR); ——设置SVM核函数类型,包括CUSTOM、LINEAR、POLY、RBF、SIGMOID、CHI2、INTER,默认值为RBF;
非线性分类中常见的核函数包括:齐次多项式、非齐次多项式、双曲正切、高斯核(Gaussiankernel)、线性核、径向基函数(radialbasis function, RBF)核和、Sigmoid核。

svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); ——设置SVM训练时迭代终止条件,默认值是cv::TermCriteria(cv::TermCriteria::MAX_ITER + TermCriteria::EPS,1000, FLT_EPSILON);

2.3 训练SVM

程序如下:

svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);//训练数据

2.4 保存数据

当遇到较大数据量时,一般需要将数据保存为xml文件,以便在其他程序中得到调用。程序如下:

	//保存模型
	svm->save(\"svm.xml\");

2.5 加载保存的数据

当需要加载2.4中保存的xml文件时,代码为:

Ptr<SVM> svm = SVM::load(\"svm.xml\");//加载svm对象

2.6 测试数据

测试数据代码如下:

int response = (int)svm->predict(p);

其中,p为CV_32FC1类型数据,response输出SVM预测的数据类型。

三、显示支持向量机分类区域

本例中,由于数据量较小,因此我们可以显示SVM的决策区域,以便更好地了解SVM算法的实现过程。主要步骤如下:

3.1 初始化图像显示区域

//视觉表示的数据
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3);

将图像显示区域设置为512*512像素大小。

3.2 显示SVM决策区域

//显示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;
		}

遍历图像上所有点,如果该点预测为1,则赋值为绿色;否则如果预测为-1,则赋值为蓝色

3.3 显示训练数据

//显示训练数据
	int thickness = -1;//-1表示实心
	int lineType = 8;
	circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);//半径为5
	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);

将训练数据标签为1的坐标点显示为半径为5的黑色实心圆圈,标签为-1的坐标点显示为半径为5的白色实心圆圈。

3.4 显示支持向量

那什么是支持向量呢?看一幅图:
\"利用C++和OpenCV3设计支持向量机SVM分类器_第1张图片\"
样本中距离超平面最近的一些点,这些点叫做支持向量

代码如下:

	//显示支持向量
	thickness = 2;
	lineType = 8;
	Mat sv = svm->getUncompressedSupportVectors();
	//cout << sv << endl;//输出结果:[501,10; 255,10; 501,255]为什么???
	for (int i = 0; i < sv.rows; ++i)
	{
		const float* v = sv.ptr<float>(i);//指向矩阵sv的第i行
		circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);//灰色,半径为6
	}

将所有训练得到的支持向量绘制为半径为6的空心灰色圆圈。

四、输出结果

输出图像为:
\"利用C++和OpenCV3设计支持向量机SVM分类器_第2张图片\"
在上图中可以发现,被灰色圆圈包围的三个数据才决定了最终的决策边界,所以这三个数据被称之为支持向量

ok,以上便是全部内容了,如果对你有所帮助,记得点个赞呦~

参考:

  1. https://blog.csdn.net/fengbingchun/article/details/78353140
  2. https://blog.csdn.net/wyx100/article/details/75295215

如果还想进一步加深对支持向量机SVM的了解,可以参考博客:基于C++,OpenCV3以及SVM的手写数字识别系统的设计(从手写数字识别设计中认识SVM)

如果想了解SVM的推导过程,可以参考视频:
1.林轩田机器学习技法(Machine Learning Techniques)
2.机器学习-白板推导系列(六)-支持向量机SVM(Support Vector Machine)

3.机器学习实战教程(八):支持向量机原理篇之手撕线性SVM
4.机器学习实战教程(九):支持向量机实战篇之再撕非线性SVM

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号