主要 內容 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;
}