QT+Opencv實現人臉檢測與性別識別(1)
seetaface開源人臉檢測框架實現人臉檢測,opencv+dnn模組實現性別分類,qt做顯示介面,完成一個課程設計。
依賴庫:opencv3.1+ 包含dnn模組,QT5
1.性別分類網路訓練
1.1.訓練資料準備
下載lfw人臉資料庫,由於原始資料集未提供性別標籤,參考了GitHub專案LFWgender,訪問一個網路api介面,根據姓名對資料新增上標籤。最終獲得了約1600張圖片作為訓練資料,600張圖片作為驗證集,男女樣本各一半。
1.2.人臉檢測與臉部區域擷取
性別識別的第一步是人臉檢測,而我們送到卷積網路訓練和預測的資料,實際上就是檢測出來的人臉區域,因此,我們首先要對lfw資料集做人臉檢測與臉部擷取,擷取前與擷取後的圖如下
我們用seetaface開源人臉檢測做人臉檢測,檢測出的人臉區域為紅色框部分。為了讓輸入到卷積網路的圖片包含充分的資訊,我們把人臉區域按照一定方法,擴充套件到一個更合理的區域,即綠色框區域。實際的訓練集與驗證集都應該是最右邊的圖片,即擷取之後的圖片。
實現步驟
1.用Python指令碼獲取圖片檔案訪問路徑
形如
E:\female\Adelina_Avila_0001.jpg;0
E:\female\Adelina_Avila_0001.jpg;1
這樣的txt檔案,前面是檔案絕對路徑,後面是標籤,0表示女性。
#encoding:utf-8
import os
'''
獲取male樣本和female樣本中的圖片的絕對路徑,
並儲存在txt檔案中,male樣本路徑後加上標籤1,female樣本路徑後加上標籤2,
如
male0.jpg;1
male1.jpg;1
female0.jpg;2
female1.jpg;2
'''
#root = os.getcwd()
#print root
male_path = os.path.abspath('female') #male相對路徑
file_list = os.listdir(male_path)#female路徑下的圖片相對路徑 male0.jpg
#print file_list
f = open('label.txt','w')
abs_path_list=[]#圖片絕對路徑
print 'male......'
for file in file_list:
file_name = male_path+'\\'+file
print file_name
abs_path_list.append(file_name+';0\n' )
female_path = os.path.abspath('valfemale')
file_list = os.listdir(female_path)
print 'female......'
for file in file_list:
file_name = female_path+'\\'+file
abs_path_list.append(file_name+';1\n')
f.writelines(abs_path_list)
2.用seetaface人臉檢測提取綠色框部分並儲存
遍歷之前Python指令碼獲取的圖片訪問路徑,逐個檢測人臉,人臉區域擴充套件,依據label 0 1儲存到femaleroi和maleroi資料夾
#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
//#include<opencv2/opencv.hpp>
#include "face_detection.h"
using namespace std;
using namespace cv;
void read_csv(string& fileName, vector<string>& images, vector<int>& labels, char separator = ';')//參考opencv官網的一段例子
{
ifstream file(fileName.c_str(), ifstream::in);
string line, path, label;
while (getline(file, line))
{
stringstream lines(line);
getline(lines, path, separator);
getline(lines, label);
if (!path.empty() && !label.empty())
{
images.push_back(path);
labels.push_back(atoi(label.c_str()));
}
}
}
void SplitString(const std::string& s, std::vector<std::string>& v, const std::string& c)
//為了從E:/female/Adelina_Avila_0001.jpg擷取Adelina_Avila_0001.jpg字元段寫的字串函式
{
std::string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while (std::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));
}
int main()
{
seeta::FaceDetection detector("seeta_fd_frontal_v1.0.bin");
detector.SetMinFaceSize(40);
detector.SetScoreThresh(2.f);
detector.SetImagePyramidScaleFactor(0.8f);
detector.SetWindowStep(4, 4);
vector<string> images;
vector<int> labels;
string csv_path = "E:/label.txt";
read_csv(csv_path, images, labels);
for (int i = 0; i < labels.size();++i)
{
string img_path = images[i];
int label = labels[i];
cv::Mat img = cv::imread(img_path, cv::IMREAD_UNCHANGED);
cv::Mat img_gray;
if (img.channels() != 1)
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
else
img_gray = img;
seeta::ImageData img_data;
img_data.data = img_gray.data;
img_data.width = img_gray.cols;
img_data.height = img_gray.rows;
img_data.num_channels = 1;
std::vector<seeta::FaceInfo> faces = detector.Detect(img_data);
#ifdef USE_OPENMP
cout << "OpenMP is used." << endl;
#else
cout << "OpenMP is not used. " << endl;
#endif
#ifdef USE_SSE
cout << "SSE is used." << endl;
#else
cout << "SSE is not used." << endl;
#endif
cv::Rect face_rect;
int32_t num_face = static_cast<int32_t>(faces.size());
cout << "人臉個數" << num_face << endl;
for (int32_t i = 0; i < num_face; i++) {
face_rect.x = faces[i].bbox.x;
face_rect.y = faces[i].bbox.y;
face_rect.width = faces[i].bbox.width;
face_rect.height = faces[i].bbox.height;
//擴充臉部至更大範圍的頭部,250是原圖片大小
float extend = MIN(MIN(face_rect.x, face_rect.y), MIN(250 - 1 - face_rect.x - face_rect.width, 250 - 1 - face_rect.y - face_rect.height));
extend = MIN(extend, face_rect.height / 4);
face_rect.x -= extend;
face_rect.y -= extend;
face_rect.width = face_rect.width + 2 * extend;
face_rect.height = face_rect.height + 2 * extend;
cout << "人臉大小" << face_rect.size() << endl;
if ((face_rect.x + face_rect.width)>img.rows || (face_rect.y + face_rect.height) > img.cols ||(face_rect.x<0) ||(face_rect.y<0))
continue;
cv::Mat roi;
img(face_rect).copyTo(roi);
string maleroot = "E:/valmale/";
string femaleroot = "E:/valfemale/";
vector<string> vs;
string c = "/";
SplitString(img_path, vs, c);//擷取圖片檔名,以便儲存roi圖片
if (label == 0)
cv::imwrite(maleroot + vs[vs.size() - 1], roi);
//vs vector(E:,female,Adelina_Avila_0001.jpg),所以vs.size()-1指向最後的元素
else
cv::imwrite(femaleroot + vs[vs.size() - 1], roi);
//cv::rectangle(img, face_rect, CV_RGB(0, 0, 255), 4, 8, 0);
}
}
return 0;
}
上述Python與cpp程式碼完成了將檢測到的人臉擴充套件到一定範圍,並將此範圍擷取儲存到對應資料夾的操作。訓練集和驗證集可以自己切分,最後會有MaleTrainRoi, FemaleTrainRoi, MaleValRoi, FemaleValRoi四個資料夾,為後續使用caffe訓練自己的資料做準備。