opencv C++ SVM模型训练与分类实现

Posted lixiaoweia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv C++ SVM模型训练与分类实现相关的知识,希望对你有一定的参考价值。

最近想学习一下分类算法的内容,恰好opencv有SVM的函数,故先从这个下手。找了许多资料,发现要么是opencv2、3的,要么就没有具体实现代码,学习还是把代码与原理一起结合来看比较好。

其中,我主要参考的是这一篇文章:

学习SVM(一) SVM模型训练与分类的OpenCV实现https://blog.csdn.net/chaipp0607/article/details/68067098写得非常好!但是是2017年发布的文章,其中许多内容都做了更新,我用的是opencv 4.5.1版本,win10系统,vs2019作开发工具。具体opencv配置不说了,我对上面那篇文章的代码进行了更新。

步骤一样.

一、数据准备

首先找到opencv库自带的digits图片,我的电脑上路径在:D:\\app\\opencv4.5.1\\opencv\\opencv\\sources\\samples\\data\\digits.png

然后在D盘建立如下文件夹:

只需新建命名就好了,不用往里面放东西。接下来建立vs2019项目工程,新建源文件

复制如下代码:

#include <windows.h>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/utils/logger.hpp>
#include <thread>
#include <time.h>
//#include <stdio.h>
#include <string.h>


using namespace std;
using namespace cv;

int main()

	char ad[128] =  0 ;
	int  filename = 0, filenum = 0;
	Mat img = imread("digits.png");
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	int b = 20;
	int m = gray.rows / b;   //原图为1000*2000
	int n = gray.cols / b;   //裁剪为5000个20*20的小图块

	for (int i = 0; i < m; i++)
	
		int offsetRow = i * b;  //行上的偏移量
		if (i % 5 == 0 && i != 0)
		
			filename++;
			filenum = 0;
		
		for (int j = 0; j < n; j++)
		
			int offsetCol = j * b; //列上的偏移量
			sprintf_s(ad, "D:\\\\data\\\\%d\\\\%d.jpg", filename, filenum++);
			//截取20*20的小块
			Mat tmp;
			gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
			imwrite(ad, tmp);
		
	
	return 0;




 运行结束后,在刚刚新建的文件夹中,0、1文件夹内各有500张分割好的图片。

最后在test_image、train_image分别新建0、1文件夹。

把data\\0中的0-399复制到data\\test_image\\0,399-499复制到data\\train_image\\0;

把data\\1中的0-399复制到data\\test_image\\1,399-499复制到data\\train_image\\1。第一步完成。

--D:
  --data
    --0
    --1
    --train_image
      --0(400张)
      --1(400张)
    --test_image
      --0(100张)
      --1(100张)

 二、模型训练

 再新建一个源文件:SVM模型训练.cpp,将第一步的SVM数据准备文件从项目中移除。

复制上如下代码,其中最主要的就是opencv4中的SVM函数改变很大,配置参数上与原文完全不同

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

#include <iostream> 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/ml/ml.hpp>  
#include <io.h>

using namespace std;
using namespace cv;
using namespace cv::ml;

void getFiles(string path, vector<string>& files);
void get_1(Mat& trainingImages, vector<int>& trainingLabels);
void get_0(Mat& trainingImages, vector<int>& trainingLabels);

int main()

	//获取训练数据
	Mat classes;
	Mat trainingData;
	Mat trainingImages;
	vector<int> trainingLabels;
	get_1(trainingImages, trainingLabels);
	//waitKey(2000);
	get_0(trainingImages, trainingLabels);
	Mat(trainingImages).copyTo(trainingData);
	trainingData.convertTo(trainingData, CV_32FC1);
	Mat(trainingLabels).copyTo(classes);
	//配置SVM训练器参数
	Ptr<SVM> svm = SVM::create();
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::LINEAR);
	svm->setDegree(0);
	svm->setGamma(1);
	svm->setCoef0(0);
	svm->setC(1);
	svm->setNu(0);
	svm->setP(0);
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, 0.01));
	//训练
	svm->train(trainingData, ROW_SAMPLE, classes );
	//保存模型
	svm->save("svm.xml");
	
	cout << "训练好了!!!" << endl;

	getchar();
	return 0;

void getFiles(string path, vector<string>& files)

	long long  hFile = 0;
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\\\*").c_str(), &fileinfo)) != -1)
	
		do
		
			if ((fileinfo.attrib & _A_SUBDIR))
			
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
					getFiles(p.assign(path).append("\\\\").append(fileinfo.name), files);
			
			else
			
				files.push_back(p.assign(path).append("\\\\").append(fileinfo.name));
			
		 while (_findnext(hFile, &fileinfo) == 0);

		_findclose(hFile);
	


void get_1(Mat& trainingImages, vector<int>& trainingLabels)

	string filePath = "D:\\\\data\\\\train_image\\\\1";
	cout << "获取D:\\\\data\\\\1" << endl;
	vector<string> files;
	getFiles(filePath, files);
	int number = files.size();
	for (int i = 0; i < number; i++)
	
		Mat  SrcImage = imread(files[i].c_str());
		SrcImage = SrcImage.reshape(1, 1);
		trainingImages.push_back(SrcImage);
		trainingLabels.push_back(1);
	

void get_0(Mat& trainingImages, vector<int>& trainingLabels)

	string filePath = "D:\\\\data\\\\train_image\\\\0";
	cout << "获取D:\\\\data\\\\0" << endl;
	vector<string> files;
	getFiles(filePath, files);
	int number = files.size();
	for (int i = 0; i < number; i++)
	
		Mat  SrcImage = imread(files[i].c_str());
		SrcImage = SrcImage.reshape(1, 1);
		trainingImages.push_back(SrcImage);
		
		trainingLabels.push_back(0);
	

 训练完毕后,在这个解决方案文件夹下就生成了一个.xml文件,即是我们训练出来的模型。

训练时还可以选择自动训练,会自己寻找最优参数,效果也很好。

//训练
	svm->trainAuto(trainingData, ROW_SAMPLE, classes );

三、加载模型实现分类

同样的,新建一个源文件:

复制如下代码:

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

#include <iostream> 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/ml/ml.hpp>  
#include <io.h>

using namespace std;
using namespace cv;
using namespace cv::ml;

void getFiles(string path, vector<string>& files);
int main()

	int result = 0;
	string filePath = "D:\\\\data\\\\test_image\\\\1";
	vector<string> files;
	getFiles(filePath, files);
	int number = files.size();
	cout << number << endl;
	string modelpath = "svm.xml";
	cv::Ptr<cv::ml::SVM> svm;
	svm = cv::Algorithm::load<cv::ml::SVM>(modelpath);


	/*CvSVM svm;
	svm.clear();
	string modelpath = "svm.xml";
	FileStorage svm_fs(modelpath, FileStorage::READ);
	if (svm_fs.isOpened())
	
		svm.load(modelpath.c_str());
	*/
	for (int i = 0; i < number; i++)
	
		Mat inMat = imread(files[i].c_str());
		Mat p = inMat.reshape(1, 1);
		p.convertTo(p, CV_32FC1);
		int response = (int)svm->predict(p);
		if (response == 1)//要预测1,如果用0来做测试集就改成response == 0
		
			result++;
		
		else
		
			cout << files[i] << endl;
		
	
	cout << result << endl;
	getchar();
	return  0;

void getFiles(string path, vector<string>& files)

	long long   hFile = 0;
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\\\*").c_str(), &fileinfo)) != -1)
	
		do
		
			if ((fileinfo.attrib & _A_SUBDIR))
			
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
					getFiles(p.assign(path).append("\\\\").append(fileinfo.name), files);
			
			else
			
				files.push_back(p.assign(path).append("\\\\").append(fileinfo.name));
			
		 while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	

 如果想要检测0的分类准确率就让第46行的response == 0。

 可以看到,100张1有99张被识别出来,有一张452没有识别成功。100张0都识别出来了。

以上是关于opencv C++ SVM模型训练与分类实现的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中使用 OpenCV 3.0 中的 HOG 功能训练 SVM 分类器?

在 OpencV 中训练 SVM 分类器?

SVM 训练 C++ OpenCV

如何使用具有面部特征的 openCV 训练支持向量机(svm)分类器?

opencv进阶-HOG+SVM行人检测(已训练好的分类器)

将经过训练的 SVM 从 scikit-learn 导入到 OpenCV