opencv3 svm实现手写体与多分类

主要 内容 opencv3 调试2分类svm 和多分类svm。

1.opencv3调试二分类SVM

第5小点有完整程序参考。

<1>数据准备 区分手写字体0,1

首先在项目文件夹下面建立两个子文件夹,分别为train,test。train,test文件夹下面分别再建立子文件夹0,1。具体如图所示:

在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

<2> 给训练集数据加上标签

Mat& trainingImages, vector& trainingLabels:这里采用了引用&,所以不清空前会一直累加变化

void getFiles(string path, vector<string>& files)//打开文件夹访问里面所有内容
{
	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)
{
	char * filePath = "train\\1";
	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)
{
	char * filePath = "train\\0";
	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);
	}
}

<2> 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, 1000,0.01));
	// train operation
	svm->train(trainingData,SampleTypes::ROW_SAMPLE, trainingLabels);
	//保存模型
	svm->save("svm.xml");

<3>测试 载入训练模型 测试0

	string modelpath = "svm.xml";
	Ptr<ml::SVM> svm = Algorithm::load<ml::SVM>(modelpath);
	char * filePath = "test\\0";
	vector<string> files;
	getFiles(filePath, files);
	int number = files.size();
	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 == 0)//因为现在测试为0所以标签为0 预测值出来为零
		{
			std::cout<<"预测为0 预测正确"<<endl;
		}
	}

<5>程序

digits文件如下,在opencv库中有!路径为==/opencv/sources/samples/data/==
在这里插入图片描述

/////////////////////////////////////////////////////////////////////////
////部分1  将一张巨大的图片中的元素切割出来  digits.png
///////////////////////////////////////////////////////////////////////////
//#include <opencv2/opencv.hpp>
//#include <iostream>
//#include <ml.hpp>
//
//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, CV_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, "E:\\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;
//}
//
//
/////////////////////////////////////////////////////////////////////////
////部分2  训练
///////////////////////////////////////////////////////////////////////////
//#include <stdio.h>  
//#include <time.h>  
//#include <opencv2/opencv.hpp>  
//#include <opencv/cv.h>  
//#include <iostream> 
//#include <opencv2/core/core.hpp>  
//#include <opencv2/highgui/highgui.hpp>  
//#include <opencv2/ml/ml.hpp>  
//#include <io.h>
//#include "opencv2/ml.hpp"
//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);
//	get_0(trainingImages, trainingLabels);
//	Mat(trainingImages).copyTo(trainingData);
//	trainingData.convertTo(trainingData, CV_32FC1);
//	Mat(trainingLabels).copyTo(classes);
//	//配置SVM训练器参数
//	// initial 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, 1000,0.01));
//
//
//	// train operation
//	svm->train(trainingData,SampleTypes::ROW_SAMPLE, trainingLabels);
//
//
//
//	//保存模型
//	svm->save("svm.xml");
//	cout << "训练好了!!!" << endl;
//	getchar();
//	return 0;
//}
//void getFiles(string path, vector<string>& files)
//{
//	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)
//{
//	char * filePath = "testdata\\train\\1";
//	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)
//{
//	char * filePath = "testdata\\train\\0";
//	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);
//	}
//}

/////////////////////////////////////////////////////////////////////////
////部分3  测试
///////////////////////////////////////////////////////////////////////////

#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <io.h>

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

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

int main()
{
	int result = 0;
	char * filePath = "testdata\\test\\0";
	vector<string> files;
	getFiles(filePath, files);
	int number = files.size();
	cout << number << endl;
	//cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::create();
	//svm->clear();
	string modelpath = "svm.xml";
	Ptr<ml::SVM> svm = Algorithm::load<ml::SVM>(modelpath);
	//FileStorage svm_fs(modelpath, FileStorage::READ);
	//if (svm_fs.isOpened())
	//{
	////	svm->load(modelpath.c_str());
	//	Ptr<ml::SVM> svm = Algorithm::load<ml::SVM>("svm.xml");
	//}
	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 == 0)
		{
			result++;
		}
	}
	cout << result << endl;
	getchar();
	return  0;
}
void getFiles(string path, vector<string>& files)
{
	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);
	}
}

2 多分类svm 人脸识别

因为一个svm模型只能分为两类,当有N个人作区分时,产生N个模型即可。
opencv参考地址https://docs.opencv.org/3.3.0/d1/d2d/classcv_1_1ml_1_1SVM.html#aad7f1aaccced3c33bb256640910a0e56 我发现svm中(C_SVM)支持多分类 所以只需要类别标签分为贴为1~15,predict出来就是1-15。
在这里插入图片描述

修改的程序如下,只是在标签贴的时候不同!!!!
检测标准, cout << response;输出为1,就是第一个人;输出为2,就是第二个人!

#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <io.h> //查找文件相关函数
#include <vector>
#include <string>
#include <fstream>
using namespace std;
using namespace cv;
using namespace cv::ml;
//声明
void getFiles(string path, vector<string>& files);
void getBubble(Mat& trainingImages, vector<int>& trainingLabels, int num, int count);//,int num
void train2modle(int classesNum);
void Test_result(int classesNum);


void getFiles(string path, vector<string>& files)
{
	intptr_t   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 getBubble(Mat& trainingImages, vector<int>& trainingLabels, int num, int count)//count第几个训练器  num第几个文件夹
{

	string tem;
	std::stringstream StrStm;
	StrStm.clear();								//std::stringstream变量在使用(转化)前都应先调用.cleat()函数
	tem.clear();
	StrStm << num;
	StrStm >> tem;
	string filePath = "faces\\train\\" + tem; //正样本路径
	//char *  filePath = "faces\\train\\1";
	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);
	//	if (count == num)
			trainingLabels.push_back(num);//该样本为数字
	//	else
	//		trainingLabels.push_back(0);//该样本为数字
	}

}
int main()
{
	train2modle(15);
	Test_result(15);
	return 0;
}

void train2modle(int classesNum)
{
	int count = 1;
	//获取训练数据
	Mat classes;
	Mat trainingData;
	Mat trainingImages;
	vector<int> trainingLabels;
//	for (int count = 1; count <= classesNum; count++)//测试文件夹 有15个人脸文件夹
	{
		trainingLabels.clear();
		trainingImages.release();
		for (int num = 1; num <= classesNum; num++)
			getBubble(trainingImages, trainingLabels, num, count);
		//getNoBubble(trainingImages, trainingLabels);
		//在主函数中,将getBubble()与getNoBubble()写好的包含特征的矩阵拷贝给trainingData,将包含标签的vector容器进行类
		//型转换后拷贝到trainingLabels里,至此,数据准备工作完成,trainingData与trainingLabels就是我们要训练的数据。
		Mat(trainingImages).copyTo(trainingData);
		trainingData.convertTo(trainingData, CV_32FC1);
		Mat(trainingLabels).copyTo(classes);
		classes.convertTo(classes, CV_32SC1);
		// 创建分类器并设置参数
		Ptr<SVM> SVM_params = SVM::create();
		SVM_params->clear();
		SVM_params->setType(SVM::C_SVC);
		SVM_params->setKernel(SVM::LINEAR);  //核函数
		SVM_params->setDegree(0);
		SVM_params->setGamma(1);
		SVM_params->setCoef0(0);
		SVM_params->setC(1);
		SVM_params->setNu(0);
		SVM_params->setP(0);
		SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01));
		Ptr<TrainData> tData = TrainData::create(trainingData, ROW_SAMPLE, classes);
		// 训练分类器
		SVM_params->train(tData);
		string tem;
		std::stringstream StrStm;
		StrStm.clear();								//std::stringstream变量在使用(转化)前都应先调用.cleat()函数
		tem.clear();
		StrStm << count;
		StrStm >> tem;
		string savexml = "svm.xml";
		//保存模型
		SVM_params->save(savexml);
		std::cout << "训练好了!!!" << endl;
	}
}
void Test_result(int classesNum)
{
	std::stringstream StrStm;
	int result = 0;	int count = 1; 	string tem; int sum = 0;
	vector <int> responseSave;
	for (int j = 1; j <= classesNum; j++) //数据集测试图片加载
	{
		StrStm.clear();
		tem.clear();
		StrStm << j;
		StrStm >> tem;
		string filePath = "faces//test//" + tem;
		vector<string> files;
		getFiles(filePath, files);
		int number = files.size();
		for (int i = 0; i < number; i++)//测试文件夹下子文件夹内的图片
		{
		//	for (int count = 1; count <= classesNum; count++)//生成的模型加载
			{
				StrStm.clear();
				tem.clear();
				StrStm << count;
				StrStm >> tem;
				string modelpath = "svm.xml";
				sum = number + sum;
				Ptr<ml::SVM> svm = Algorithm::load<ml::SVM>(modelpath);
				Mat inMat = imread(files[i].c_str());
				Mat p = inMat.reshape(1, 1);
				p.convertTo(p, CV_32FC1);
				int response = (int)svm->predict(p);
				cout << response;
				////responseSave.push_back(response);
				if (response== j )
				{
					result = result + 1;
					////	std::cout << "识别为S" << count << "实际为S" << j << endl;
					//	//break;
				}

			}
			cout << endl;
		}

	}


	std::cout << result << endl;
	std::cout << "识别率" << endl;
	std::cout << result / sum * 100 << "%" << endl;
	getchar();
}

以下结尾生成多个模型的多分类

<1>数据准备

同样在项目工程下建立两个文件夹 train、test但是测试的有十五个人所以 train 、test文件夹下各15个文件夹具体如下:
在这里插入图片描述在这里插入图片描述

<2>添加类别标签

对于第一个分类器而言,只有第一个人为正样本 剩余全为负样本
对于第二个分类器而言,只有第二个人为正样本 剩余全为负样本
.
.
.

void getFiles(string path, vector<string>& files)
{
	intptr_t   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 getBubble(Mat& trainingImages, vector<int>& trainingLabels, int num, int count)//count第几个训练器  num第几个文件夹
{
	string tem;
	std::stringstream StrStm;
	StrStm.clear();								//std::stringstream变量在使用(转化)前都应先调用.cleat()函数
	tem.clear();
	StrStm << num;
	StrStm >> tem;
	string filePath = "faces\\train\\" + tem; //正样本路径
	//char *  filePath = "faces\\train\\1";
	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);
		if (count == num)
			trainingLabels.push_back(1);//该样本为数字
		else
			trainingLabels.push_back(0);//该样本为数字
	}

}

<2>训练

每次训练新的模型前,都需要把trainingLabels,trainingImages释放!!不然==引用&会一直补充在后!导致维度一直在增会出错。
== trainingLabels.clear();
trainingImages.release();

void train2modle(int classesNum)
{
	int count = 1;
	//获取训练数据
	Mat classes;
	Mat trainingData;
	Mat trainingImages;
	vector<int> trainingLabels;
	for (int count = 1; count <= classesNum; count++)//测试文件夹 有15个人脸文件夹
	{
		trainingLabels.clear();
		trainingImages.release();
		for (int num = 1; num <= classesNum; num++)
			getBubble(trainingImages, trainingLabels, num,count);
		//getNoBubble(trainingImages, trainingLabels);
		//在主函数中,将getBubble()与getNoBubble()写好的包含特征的矩阵拷贝给trainingData,将包含标签的vector容器进行类
		//型转换后拷贝到trainingLabels里,至此,数据准备工作完成,trainingData与trainingLabels就是我们要训练的数据。
		Mat(trainingImages).copyTo(trainingData);
		trainingData.convertTo(trainingData, CV_32FC1);
		Mat(trainingLabels).copyTo(classes);
		classes.convertTo(classes, CV_32SC1);
		// 创建分类器并设置参数
		Ptr<SVM> SVM_params = SVM::create();
		SVM_params->clear();
		SVM_params->setType(SVM::C_SVC);
		SVM_params->setKernel(SVM::LINEAR);  //核函数
		SVM_params->setDegree(0);
		SVM_params->setGamma(1);
		SVM_params->setCoef0(0);
		SVM_params->setC(1);
		SVM_params->setNu(0);
		SVM_params->setP(0);
		SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01));
		Ptr<TrainData> tData = TrainData::create(trainingData, ROW_SAMPLE, classes);
		// 训练分类器
		SVM_params->train(tData);
		string tem;
		std::stringstream StrStm;
		StrStm.clear();								//std::stringstream变量在使用(转化)前都应先调用.cleat()函数
		tem.clear();
		StrStm << count;
		StrStm >> tem;
		string savexml = "svm" + tem + ".xml";
		//保存模型
		SVM_params->save(savexml);
		std::cout<< "训练好了!!!" << endl;
	}
}

<3>测试

输出的是标签。cout << response ;对于每一张测试样本来说会输出15个数(每一个模型进去就会出来一个预测标签)其中第几个1就判为第几个人;比如100000000000000 =该样本预测为第一个人
010000000000000 =该样本预测为第二个人

void Test_result(int classesNum)
{
	std::stringstream StrStm;
	int result = 0;	int count = 1; 	string tem; int sum = 0;
	vector <int> responseSave;
	for (int j = 1; j <= classesNum; j++) //数据集测试图片加载
	{ 
		StrStm.clear();
		tem.clear();
		StrStm << j;
		StrStm >> tem;
		string filePath = "faces//test//"+tem;
		vector<string> files;
		getFiles(filePath, files);
		int number = files.size();
		for (int i = 0; i < number; i++)//测试文件夹下子文件夹内的图片
		{
			for (int count = 1; count <= classesNum; count++)//生成的模型加载
			{
				StrStm.clear();
				tem.clear();
				StrStm << count;
				StrStm >> tem;
				string modelpath = "svm" + tem + ".xml";
				sum = number + sum;
				Ptr<ml::SVM> svm = Algorithm::load<ml::SVM>(modelpath);
				Mat inMat = imread(files[i].c_str());
				Mat p = inMat.reshape(1, 1);
				p.convertTo(p, CV_32FC1);
				int response = (int)svm->predict(p);
				cout << response ;
				////responseSave.push_back(response);
				if (response == 1 &&(j ==count))
				{
					result = result + 1;
				////	std::cout << "识别为S" << count << "实际为S" << j << endl;
				//	//break;
				}
			
			}
			cout << endl;
		}
		
	}


	std::cout << result << endl;
	std::cout << "识别率" << endl;
	std::cout << result / sum * 100 << "%" << endl;
	getchar();
}

<5>主函数

int main()
{
	train2modle(15);
	Test_result(15);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35705332/article/details/84647957