版本:opencv3.4.1
平臺:win10
#<io.h>是windows專有庫,在linux中不能使用
本文參考github訓練的ssd模型(在本文后悔給出鏈接),然后結合LBP實作人臉檢測,之所以選擇ssd+lbp其原因在于快,相比于其他浮點計算人臉檢測方法,如EigenFaceRecognizer,FisherFaceRecognizer,在速度上,因為lbp采用整數進行計算,因此,在速度上有很大的提示,這對于在一些羸弱的計算機如樹莓派也能良好的作業,
下面是演示代碼:
使用方法:
在第一次使用的時候,將Flag 設為1,他會采集人臉,并進行訓練
在后面的執行中,就可以將flag設為0,他會自動去加載上一下訓練的模型
該代碼使用了兩個自實作函式:getfiles和SplitString,主要用來獲取目錄下的所有檔案和用來進行字串分割,在使用時,可不比糾結于這兩個演算法的實作,知道其使用方法即可:
void getFiles(string path, vector<string>& files)
第一個引數為檔案路徑,第二個是一個vector向量,用于存放該檔案夾下所有檔案的路徑
void SplitString(const string& s, vector<string>& v, const string& c)
第一個引數為要進行分割的字串,第二個是一個vector向量,用于存放分割的字串,第三個引數為要使用什么字串進行風格,如“,”、"_"等,
#include<opencv2/opencv.hpp>
#include <iostream>
#include<opencv2/face.hpp>
#include<opencv2/dnn.hpp>
#include <string>
#include<io.h>
#include <fstream>
using namespace std;
using namespace cv;
using namespace cv::face;
using namespace cv::dnn;
int Flag = 1; //1表示需需要進行添加新人臉,進行訓練,2表示加載模型直接運行
String image_base = "F:\\VS\\opencv\\data\\orl_faces";
String modelFile = "E:\\opencv_install\\model\\caffe_ssd_face_detactor\\res10_300x300_ssd_iter_140000.caffemodel"; //caffe模型
String model_text_file = "E:\\opencv_install\\model\\caffe_ssd_face_detactor\\deploy.prototxt";
String train_data_dir = "F:\\VS\\opencv\\邊角檢測\\邊角檢測\\data";
void getFiles(string path, vector<string>& files);
void SplitString(const string& s, vector<string>& v, const string& c);
map<int, string> ID_NAME;
int main(int argc, char** argv)
{
//namedWindow("image", WINDOW_AUTOSIZE);
// import Caffe SSD model
Net net = readNetFromCaffe(model_text_file, modelFile);
if (net.empty()) {
printf("read caffe model data failure...\n");
return -1;
}
VideoCapture capture(0);
if (!capture.isOpened()) {
printf("could not open camera...\n");
return -1;
}
//采集人臉資料進行訓練
if (Flag == 1) //添加人臉,進行訓練
{
/*getFiles(train_data_dir, face_image_list);*/
//使用攝像頭進行采集10張需要添加的人臉
int cnt = 10;
int ID = 1;
cout << "請輸入姓名" << endl;
string name = "";
cin >> name;
//回圈遍歷檔案夾,為了避免ID重復,需要先獲取當前已有ID;
vector<string> face_image_list;
getFiles(train_data_dir, face_image_list);
if (face_image_list.size() == 0)
ID = 1;
else
{
//回圈遍歷,獲取最大ID值
for (int i = 0; i < face_image_list.size(); i++)
{
//存盤格式為"F:\\VS\\opencv\\邊角檢測\\邊角檢測\\data\\name_ID_1.jpg"
//獲取ID方式,使用_進行分割
vector<string> ID_list;
SplitString(face_image_list[i], ID_list, "_");
if (atoi(ID_list[1].c_str()) > ID)
ID = atoi(ID_list[1].c_str());
}
}
//新用戶ID+1
ID++;
namedWindow("image", WINDOW_AUTOSIZE);
while (true)
{
Mat tem_frame;
capture.read(tem_frame);
Mat inputBlob = blobFromImage(tem_frame, 1.0, Size(300, 300), Scalar(104, 117, 123), false, false);
//crop一定要設定成false,讓他進行縮放,而不能從中心進行裁剪
net.setInput(inputBlob, "data");
Mat prob = net.forward("detection_out");
Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());//size[2]存放寬度,3存放高度
//設定置信區間
float confidence_threshold = 0.8;
float confidence = detectionMat.at<float>(0, 2); //第二列存放的是置信度
float tl_x = -1;
float tl_y = -1;
float br_x = -1;
float br_y = -1;
if (confidence > confidence_threshold)
{
size_t objIndex = (size_t)(detectionMat.at<float>(0, 1)); //第1列存放的是標簽索引
tl_x = detectionMat.at<float>(0, 3) * tem_frame.cols; //后面依次為x,y的坐標
tl_y = detectionMat.at<float>(0, 4) * tem_frame.rows;
br_x = detectionMat.at<float>(0, 5) * tem_frame.cols;
br_y = detectionMat.at<float>(0, 6) * tem_frame.rows;
Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y)); //繪制矩形,這里坐標需要轉換為int
rectangle(tem_frame, object_box, Scalar(0, 0, 255), 2, 8, 0); //繪制
}
imshow("image", tem_frame);
char c = cv::waitKey(10);
if (c == 115) //調整好姿勢,按s進行采集資料
{
if (tl_x != -1)
{
Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y)); //繪制矩形,這里坐標需要轉換為int
Mat test_roi = tem_frame(object_box);
imwrite(format((train_data_dir + "\\" + name + "_ % d_%d.jpg").c_str(), ID, cnt--), test_roi);
cout << "已采集" << 10 - cnt << "張" << endl;
cv::waitKey(500);//放在誤觸,設定1s采集一張
}
else
{
cout << "調整角度進行人臉采集" << endl;
}
}
if (cnt == 0)
break;
}
destroyWindow("image");
//獲取訓練集,
vector<string> train_image_list;
getFiles(train_data_dir, train_image_list);
vector<int> train_label;
vector<Mat> tran_data;
for (int j = 0; j < train_image_list.size(); j++)
{
//存盤格式為"F:\\VS\\opencv\\邊角檢測\\邊角檢測\\data\\name_ID_1.jpg"
tran_data.push_back(imread(train_image_list[j], 0));//以灰度模式進行讀取
vector<string> label_tem;
SplitString(train_image_list[j], label_tem, "_");
train_label.push_back(atoi(label_tem[1].c_str()));
vector<string> name_tem;
SplitString(label_tem[0], name_tem, "\\");
ID_NAME[atoi(label_tem[1].c_str())] = name_tem[name_tem.size() - 1];
}
//train it
int height = 300;
int width = 300;
//確保每張圖片寬高一樣
for (int i = 0; i < tran_data.size(); i++)
{
resize(tran_data[i], tran_data[i], Size(width, height));
}
Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
cout << "train it....." << endl;
model->train(tran_data, train_label);
//模型保存
model->save("F:\\VS\\opencv\\邊角檢測\\邊角檢測\\model\\face.xml");
cout << "save it....." << endl;
}
// 創建并加載模型
Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
cout << "load it....." << endl;
model->read("F:\\VS\\opencv\\邊角檢測\\邊角檢測\\model\\face.xml");
//獲取ID_NMAE
if (ID_NAME.size() == 0)
{
vector<string> train_image_list;
getFiles(train_data_dir, train_image_list);
for (int j = 0; j < train_image_list.size(); j++)
{
//存盤格式為"F:\\VS\\opencv\\邊角檢測\\邊角檢測\\data\\name_ID_1.jpg"
vector<string> label_tem;
SplitString(train_image_list[j], label_tem, "_");
vector<string> name_tem;
SplitString(label_tem[0], name_tem, "\\");
ID_NAME[atoi(label_tem[1].c_str())] = name_tem[name_tem.size() - 1];
}
}
Mat frame;
while (true)
{
capture.read(frame);
if (frame.empty())
{
continue;
}
flip(frame, frame, 1);
Mat inputBlob = blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 117, 123), false, false);
//crop一定要設定成false,讓他進行縮放,而不能從中心進行裁剪
net.setInput(inputBlob, "data");
Mat prob = net.forward("detection_out");
Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());//size[2]存放寬度,3存放高度
//設定置信區間
float confidence_threshold = 0.8;
for (int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2); //第二列存放的是置信度
if (confidence > confidence_threshold)
{
size_t objIndex = (size_t)(detectionMat.at<float>(i, 1)); //第1列存放的是標簽索引
float tl_x = detectionMat.at<float>(i, 3) * frame.cols; //后面依次為x,y的坐標
float tl_y = detectionMat.at<float>(i, 4) * frame.rows;
float br_x = detectionMat.at<float>(i, 5) * frame.cols;
float br_y = detectionMat.at<float>(i, 6) * frame.rows;
Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y)); //繪制矩形,這里坐標需要轉換為int
rectangle(frame, object_box, Scalar(0, 0, 255), 2, 8, 0); //繪制
//預測是誰
Mat testSample_roi;
try {
testSample_roi = frame(object_box);
}
catch (Exception e)
{
testSample_roi = frame;
}
Mat gray;
cvtColor(testSample_roi, gray, CV_BGR2GRAY);//因為訓練的時候采用的是灰度
resize(gray, gray, Size(300, 300));
//int predictedLabel = model->predict(gray);
int predictedLabel;
double conf;
model->predict(gray, predictedLabel,conf);
cout << predictedLabel <<" " << conf << endl;
if(conf<40)
putText(frame, ID_NAME[predictedLabel], Point(tl_x, br_y), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255, 0, 0), 2);
else
putText(frame, "Unknow", Point(tl_x, br_y), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 255, 0), 2);
}
}
imshow("image", frame);
cv::waitKey(1);
}
return 0;
}
void SplitString(const string& s, vector<string>& v, const string& c)
{
string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while (string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2 - pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if (pos1 != s.length())
v.push_back(s.substr(pos1));
}
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函式結束查找
_findclose(hFile);
}
}

不訓練該人臉:

經測驗發現,LBPHFaceRecognizer的置信度閾值為60,當然該閾值的實際引數還是需要根據自己的資料及進行適當調整才行,
模型github鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293060.html
標籤:其他
上一篇:OpenCv特征匹配
