主要 内容 opencv3 调试2分类svm 和多分类svm。
参数调试参考地址
官方文档说明地址
实现内容:1 opencv +svm实现首先体二分类
2 opencv+svm实现人脸分类
3 opencv+pca+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/==
总共三部分:从digits中分割出所需训练集与测试集;训练;测试。
/
部分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();
}
以下是一开始没有好好阅读文档,以为svm是二分类,因此15类就傻瓜式的生成15个svm模型来作为多分类
<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;
}