SVM示例程序
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
using namespace cv;
//OpenCV3需额外加入:
#include <opencv2/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
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 } };//Mat结构特征数据
cv::Mat trainingDataMat(4, 2, CV_32FC1, trainingData);//样本标签
cv::Mat labelsMat(4, 1, CV_32SC1, labels);//Mat结构标签
// initial SVM 设置SVM参数
cv::Ptr<cv::ml::SVM> 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<cv::Vec3b>(i, j) = green;
else if (respose == -1)
image.at<cv::Vec3b>(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.获取训练样本
SVM是一种有监督的学习分类方法,所以对于给出的训练样本,要明确每个样本的归类是0还是1,即每个样本都需要标注一个确切的类别标签,提供给SVM训练使用。
最简单的情况下,假定有两类训练样本,样本的维度是二维,每类包含3个样本,可以定义如下:
// training data获取训练样本
int labels[4] = { 1, -1, -1, -1 };//样本数据
float trainingData[4][2] = { { 501, 10 },{ 255, 10 },{ 501, 255 },{ 10, 501 } };//Mat结构特征数据
cv::Mat trainingDataMat(4, 2, CV_32FC1, trainingData);//样本标签
cv::Mat labelsMat(4, 1, CV_32SC1, labels);//Mat结构标签
2.初始化SVM参数
CVSVM::C_SVC类型是SVM中最常被用到的类型,它的重要特征是可以处理非完美分类的问题(即训练数据不可以被线性分割),包括线性可分割和不可分割。
核函数的目的是为了将训练样本映射到更有利于线性分割的样本集中。若设置核函数的类型是CvSVM::LINEAR表示不需要进行高维空间映射。
算法终止条件,SVM训练的过程是一个通过迭代方式解决约束条件下的二次优化问题,可以设定一个最大迭代次数和容许误差的组合,以允许算法在适当的条件下停止计算。
// initial SVM 设置SVM参数
cv::Ptr<cv::ml::SVM> 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));
3.训练支持向量
// train operation 训练支持向量机
svm->train(trainingDataMat, cv::ml::SampleTypes::ROW_SAMPLE, labelsMat);
4.不同样本区域分割
// 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<cv::Vec3b>(i, j) = green;
else if (respose == -1)
image.at<cv::Vec3b>(i, j) = blue;
}
}
5.绘制支持向量
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);
}
6.分类结果