支持向量机之SVM引导
视频讲解如下:
当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
不同编程语言对应的OpenCv版本以及开发环境信息如下:
语言 | OpenCv版本 | IDE |
C# | OpenCvSharp4.4.8.0.20230708 | Visual Studio 2022 |
C++ | OpenCv-4.5.5-vc14_vc15 | Visual Studio 2022 |
Python | OpenCv-Python (4.6.0.66) | PyCharm Community Edition 2022.1.3 |
运行效果如下,三个版本运行效果都差不多的:

C#版本
C#版本需要安装“OpenCvSharp4”、“OpenCvSharp4.runtime.win”两个库才行。不然会报错。
如果需要使用“ BitmapConverter.ToBitmap”操作,则需要追加安装“OpenCvSharp4.Extensions”库。
C#版本由于使用了unsafe,所以需要做如下配置修改:

OpenCvSharp 中SVM、Ptr的使用参考资料
https://www.csharpcodi.com/csharp-examples/OpenCvSharp.ML.SVM.Create()
https://www.csharpcodi.com/csharp-examples/OpenCvSharp.Mat.Ptr(int,%20int)
代码如下:
using OpenCvSharp;
using OpenCvSharp.ML;
namespace demo
{
internal class Program
{
static void Main(string[] args)
{
int width = 512, height = 512;
Mat image = new Mat(height, width, MatType.CV_8UC3); //创建窗口可视化
//// 设置训练数据
int[] labels = { +1, -1, +1, +1 };
Mat LabelsMat = new Mat(4, 1, MatType.CV_32SC1, labels);
float[,] trainingData = { { 501, 10 }, { 255, 10 }, { 501, 255 }, { 10, 501 } };
Mat trainingDataMat = new Mat(4, 2, MatType.CV_32FC1, trainingData);
// 创建分类器并设置参数
SVM model = SVM.Create();
model.Type = SVM.Types.CSvc;
model.KernelType = SVM.KernelTypes.Linear;
// opencv3:CriteriaType.MaxIter
// opencv4:CriteriaTypes.MaxIter
model.TermCriteria = new TermCriteria(CriteriaTypes.MaxIter, 100, 1e-6);
model.Train(trainingDataMat, SampleTypes.RowSample, LabelsMat); // 训练分类器
Vec3b green = new Vec3b(0, 255, 0);
Vec3b blue = new Vec3b(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)
{
float[] testFeatureData = { j, i }; //生成测试数据
Mat sampleMat = new Mat(1, 2, MatType.CV_32F, testFeatureData);
float response = (int)model.Predict(sampleMat);//进行预测,返回1或-1
if (response == 1)
image.Set(i, j, green);
else
image.Set(i, j, blue);
}
}
// 显示训练数据
Cv2.Circle(image, 501, 10, 10, new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Circle(image, 255, 10, 10, new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Circle(image, 501, 255, 10, new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Circle(image, 10, 501, 10, new Scalar(0, 0, 255), 2, LineTypes.Link8);
//绘图时,先宽后高,对应先列后行
for (int i = 0; i < LabelsMat.Rows; i++)
{
unsafe // 允许使用不安全代码,可能导致内存泄露
{
float* v = (float*)trainingDataMat.Ptr(i).ToPointer(); //取出每行的头指针
Point pt = new Point((int)v[0], (int)v[1]);
if (labels[i] == 1)
Cv2.Circle(image, pt.X, pt.Y, 5, new Scalar(0, 0, 0), -1, LineTypes.Link8);
else
Cv2.Circle(image, pt.X, pt.Y, 5, new Scalar(255, 255, 255), -1, LineTypes.Link8);
}
}
//pictureBox1.Image = BitmapConverter.ToBitmap(image);
Cv2.ImShow("out", image);
Cv2.WaitKey();
}
}
}
C++版本
C++版本中,svm分类算法在opencv4中有了很大的变动,取消了CvSVMParams这个类,因此在参数设定上会有些改变,毛星云的版本基本上是不能跑了。
参考文章 :https://www.cnblogs.com/denny402/p/5019233.html
代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
using namespace cv;
using namespace cv::ml;
int main()
{
int width = 512, height = 512;
Mat image = Mat::zeros(height, width, CV_8UC3); //创建窗口可视化
// 设置训练数据
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);
// 创建分类器并设置参数
Ptr<SVM> model = SVM::create();
model->setType(SVM::C_SVC);
model->setKernel(SVM::LINEAR); //核函数
//设置训练数据
Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);
// 训练分类器
model->train(tData);
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 = model->predict(sampleMat); //进行预测,返回1或-1
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的显示成白点
circle(image, Point(501, 10), 10, Scalar(0, 0, 255), 2, lineType);
circle(image, Point(255, 10), 10, Scalar(0, 0, 255), 2, lineType);
circle(image, Point(501, 255), 10, Scalar(0, 0, 255), 2, lineType);
circle(image, Point(10, 501), 10, Scalar(0, 0, 255), 2, lineType);
//绘图时,先宽后高,对应先列后行
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 Simple Example", image);
waitKey(0);
}
Python版本
Ptr操作在Python中的操作方法好像没有,所以目前只能用其他方法了。
import cv2
import numpy as np
# 创建窗口可视化
image = np.ones((512, 512, 4), np.uint8)
# 设置训练数据
labels = (1, -1, 1, 1)
LabelsMat = np.array(labels)
# LabelsMat转换成4行1列的矩阵
LabelsMat = LabelsMat.reshape(4, 1)
# 数据集一定要设置成浮点型
trainingDataMat = np.array([[501, 10], [255, 10], [501, 255], [10, 501]], dtype='float32');
trainingDataMat = trainingDataMat.reshape(4, 2)
# 创建分类器
svm = cv2.ml.SVM_create()
# 设置svm类型
svm.setType(cv2.ml.SVM_C_SVC)
# 核函数
svm.setKernel(cv2.ml.SVM_LINEAR)
# 训练
svm.train(trainingDataMat, cv2.ml.ROW_SAMPLE, LabelsMat)
imgH = image.shape[0]
imgW = image.shape[1]
for i in range(imgH):
for j in range(imgW):
arrayTest = np.empty(shape=[0, 2], dtype='float32')
arrayTest = np.append(arrayTest, [[i, j]], axis=0)
pt = np.array(arrayTest, dtype='float32')
response = svm.predict(pt)[1] # 进行预测,返回1或-1
if response[0][0] == 1:
image[i, j] = [0, 255, 0, 0]
else:
image[i, j] = [255, 0, 0, 0]
# 显示训练数据
image = cv2.circle(image, (501, 10), 10, (0, 0, 255), 2)
image = cv2.circle(image, (255, 10), 10, (0, 0, 255), 2)
image = cv2.circle(image, (501, 255), 10, (0, 0, 255), 2)
image = cv2.circle(image, (10, 501), 10, (0, 0, 255), 2)
# 绘图时,先宽后高,对应先列后行
imgH = LabelsMat.shape[0]
for i in range(imgH):
v = trainingDataMat[i]
x = v[0]
y = v[1]
if labels[i] == 1:
image = cv2.circle(image, (int(x), int(y)), 5, (0, 0, 0), -1)
else:
image = cv2.circle(image, (int(x), int(y)), 5, (255, 255, 255), -1)
cv2.imshow("image", image)
cv2.waitKey(0)