OpenCV笔记(3)实现支持向量机(SVM)

      参考教程:点击打开链接

      参考教程使用的是OpenCV 2.0 版本,这里将其修改为3.0版本。


1.SVM(支持向量机)

      SVM是一种训练机器学习的算法,可以用于解决分类和回归问题。

      正式定义:是一个能将不同类样本在样本空间分割的超平面,给定一些标记好的训练样本,SVM算法输出一个最优化的分隔超平面。

      

      判定是否为最优平面的依据:

      

      如上图所示,给定一些分属两类的特征点,这些点可以通过直线分割,我们要找到一个最优的直线。满足的直线很多,该怎么定义最优?有个评价标准是直线到所有点的距离最远。距离样本点太近的话,直线对噪声敏感度高,泛化性比较差。

      SVM算法就是找到能将某个值最大化的超平面,这个值是超平面离所有样本点的最小距离。这个距离叫间隔。

      

      相比较其他机器学习算法,它的优点有样本小 、结构风险小、非线性等。

      SVM离不开核函数。简单来说,核函数就是将低维空间的线性不可分类问题,转化为高维空间的线性可分问题,在高维空间找到最优边界。


2.OpenCV实现

      首先,建立训练样本:

//建立训练样本
int labels[10] = {1, 1, -1, 1, -1, -1, -1, 1, -1, -1};
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 labelsMat(10, 1, CV_32SC1, labels);
Mat trainingDataMat(10, 2, CV_32FC1, trainingData);

      labels是训练数据的分类标记,有两类:1和-1。

      trainingData是训练数据。


      设置SVM的参数:

Ptr<SVM> svm = SVM::create();//创建分类器
svm->setType(SVM::C_SVC);//SVM类型
svm->setKernel(SVM::LINEAR);//核函数的类型
svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));//算法终止条件

      这里opencv 2.x版本和3.x版本设置方式不一样,下面是2.x版本:

CvSVMParams params;//创建分类器
params.svm_type = CvSVM::C_SVC;//SVM类型
params.kernel_type = CvSVM::LINEAR;//核函数的类型
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);//算法终止条件

      其它的参数我们需要时,可以继续设置。

      

      开始训练:

//设置训练数据并训练分类器
Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);
svm->train(tData);

      下面是2.x版本的训练:

CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params);

      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); 
		//float response = SVM.predict(sampleMat); 这里有不同

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

      给空间上色,着色取决于SVM的分类,绿色是标记为1的点,蓝色是标记为-1的点。      

 

      显示结果:

//显示训练数据
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<float>(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", image);
waitKey(0);


      最终结果:

      程序创建一张图像,在其中显示训练样本,1为黑点,-1为白点。

      训练得到SVM,并将图像的每一个像素分类。分类的结果将图像分为蓝绿两部分,中间线是最优分割平面。

       


      完整代码:

#include <stdio.h>
#include "opencv2/opencv.hpp"

using namespace cv;
using namespace ml;

int main()
{
	int width = 512;
	int height = 512;

	Mat image = Mat::zeros(height, width, CV_8UC3);  

	//建立训练样本
	int labels[10] = { 1, -1, 1, 1, -1, 1, -1, 1, -1, -1 };
	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 labelsMat(10, 1, CV_32SC1, labels);
	Mat trainingDataMat(10, 2, CV_32FC1, trainingData);

	Ptr<SVM> svm = SVM::create();//创建分类器
	svm->setType(SVM::C_SVC);//SVM类型
	svm->setKernel(SVM::LINEAR);//核函数的类型
	svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));//算法终止条件

	//设置训练数据并训练分类器
	Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);
	svm->train(tData);

	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); 
			//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;
	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<float>(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", image);
	waitKey(0);
}

     

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
10-13 17:47
门头沟学院 Java
wulala.god:图一那个善我面过,老板网上找的题库面的
点赞 评论 收藏
分享
死在JAVA的王小美:哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,我也是,让我免了一轮,但是硬气拒绝了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务