源碼位置:https://github.com/comhaqs/face_find.git 分支: develop_face_recognition
opencv的人臉識別模塊現在是放在另外一個庫opencv_contrib裏,需要編譯到opencv裏纔可以使用,故這裏將opencv和opencv_contrib的源碼都下下來,opencv源碼:https://github.com/opencv/opencv/releases ,opencv_contrib源碼:https://github.com/opencv/opencv_contrib/releases。兩個的版本要一致,例如都是4.2.0。下好後,我這邊是使用cmake的gui版本,cmake的源碼目錄指向opencv的源碼目錄,輸出文件目錄最好指向一個空文件夾,然後點擊Configure按鈕,變量區會有很多紅色定義,在變量區中找到OPENCV_EXTRA_MODULES_PATH,將其選擇爲opencv_contrib/modules目錄,並勾選BUILD_opencv_world,再點擊configure。直到變量區沒有紅色,然後點擊Generate按鈕生成VS項目文件。
生成項目文件後,使用對應的VS打開,然後選擇opencv_world工程編譯即可,因爲是人臉識別,所以還需要將opencv_contrib\modules\face\include下的opencv文件夾合併到程序的include目錄裏。編譯的時候可能會提示部分文件丟失,需要手動下載,官方的issues裏有人提到了這個問題(https://github.com/opencv/opencv_contrib/issues/1301),下面有人給出了對應的下載路徑。將url顯示的內容另存爲對應的文件,放到opencv_contrib\modules\xfeatures2d\src目錄下即可。其實上面輸出文件夾CMakeDownloadLog.txt裏已經記錄了對應的下載路徑。下載時會碰到無法打開url的問題,多刷新幾次就可以了。也可以直接從CSDN下載:https://download.csdn.net/download/comhaqs/12088222
Hi! The solution I used was to download by hand all the missing files, so if you encounter the same issue please use the folliwing command in a bash file:
#!/bin/bash
cd ./cache/xfeatures2d/
cd boostdesc
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_lbgm.i > 0ae0675534aa318d9668f2a179c2a052-boostdesc_lbgm.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_binboost_256.i > e6dcfa9f647779eb1ce446a8d759b6ea-boostdesc_binboost_256.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_binboost_128.i > 98ea99d399965c03d555cef3ea502a0b-boostdesc_binboost_128.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_binboost_064.i > 202e1b3e9fec871b04da31f7f016679f-boostdesc_binboost_064.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_bgm_hd.i > 324426a24fa56ad9c5b8e3e0b3e5303e-boostdesc_bgm_hd.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_bgm_bi.i > 232c966b13651bd0e46a1497b0852191-boostdesc_bgm_bi.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/34e4206aef44d50e6bbcd0ab06354b52e7466d26/boostdesc_bgm.i > 0ea90e7a8f3f7876d450e4149c97c74f-boostdesc_bgm.i
cd ../vgg
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/fccf7cd6a4b12079f73bbfb21745f9babcd4eb1d/vgg_generated_120.i > 151805e03568c9f490a5e3a872777b75-vgg_generated_120.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/fccf7cd6a4b12079f73bbfb21745f9babcd4eb1d/vgg_generated_64.i > 7126a5d9a8884ebca5aea5d63d677225-vgg_generated_64.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/fccf7cd6a4b12079f73bbfb21745f9babcd4eb1d/vgg_generated_48.i > e8d0dcd54d1bcfdc29203d011a797179-vgg_generated_48.i
curl https://raw.githubusercontent.com/opencv/opencv_3rdparty/fccf7cd6a4b12079f73bbfb21745f9babcd4eb1d/vgg_generated_80.i > 7cd47228edec52b6d82f46511af325c5-vgg_generated_80.i
And then run cmake again, and make. Hope this help
人臉識別類的代碼如下:
#include "face_recognition.h"
#include <opencv2/opencv.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
using namespace cv;
using namespace cv::face;
face_recognition::face_recognition()
{
}
void face_recognition::start(){
try{
Ptr<LBPHFaceRecognizer> p_model = LBPHFaceRecognizer::create();
// 獲取訓練圖片集合
std::vector<info_recognition_ptr> infos;
if(!find_face_info(infos, "./face")){
return;
}
std::vector<Mat> images;
std::vector<int> labels;
for(auto& p_info : infos){
for(auto& f : p_info->files){
// 這裏記得是IMREAD_GRAYSCALE,即灰度圖片,不然會報錯
images.push_back(imread(f, cv::IMREAD_GRAYSCALE));
labels.push_back(p_info->index);
}
m_index_to_name.insert(std::make_pair(p_info->index, p_info->name));
}
// 訓練模型
p_model->train(images, labels);
mp_model = p_model;
// 加載opencv提供的人臉檢測模型,識別率比較低
std::string face_file("./haarcascade_frontalface_alt2.xml");
if(boost::filesystem::exists(face_file)){
mp_cascade = std::make_shared<cv::CascadeClassifier>();
mp_cascade->load(face_file);
}else{
LOG_ERROR("找不到對應的文件檢測模型文件:"<<face_file);
}
}catch(const std::exception& e){
LOG_ERROR("發生錯誤:"<<e.what());
}
}
bool face_recognition::train(unsigned char *p_data, int width, int height){
try {
if(!mp_model || !mp_cascade){
return false;
}
// 原始圖片數據轉成灰度圖片
cv::Mat bgr(cv::Size(width, height), CV_8UC3);
bgr.data = p_data;
cv::Mat gray;
gray.create(bgr.size(), bgr.type());
cv::cvtColor(bgr, gray, cv::COLOR_BGR2GRAY);
// 先人臉檢測,再人臉識別
std::vector<cv::Rect> rect;
mp_cascade->detectMultiScale(gray, rect, 1.1, 3, 0);
for (auto& r : rect)
{
bool flag_find_face = false;
std::string name;
// 複製出檢測出來的人臉,然後轉換成灰度圖片,不轉換會報錯
cv::Mat desc;
bgr(r).copyTo(desc);
cv::Mat gray_desc;
gray_desc.create(desc.size(), desc.type());
cv::cvtColor(desc, gray_desc, cv::COLOR_BGR2GRAY);
int index = -1;
double confidence = 0.0;
// 實際總會返回一個檢測結果,置信度暫時不知道怎麼使用
mp_model->predict(gray_desc, index, confidence);
if(0 > index){
}else{
auto iter = m_index_to_name.find(index);
if(m_index_to_name.end() == iter){
LOG_WARN("找不到對應人臉信息;序號:"<<index);
}else{
LOG_INFO("找到人臉;名稱:"<<iter->second<<"; 可信度:"<<confidence<<"; 序號:"<<index);
name = iter->second;
flag_find_face = true;
}
}
if(flag_find_face){
cv::rectangle(bgr, r, CV_RGB(0, 255, 0), 4);
cv::putText(bgr, name, Point(r.x + 0.5 * r.width, r.y - 5), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, CV_RGB(0, 255, 0));
}else{
cv::rectangle(bgr, r, CV_RGB(255, 0, 0), 2);
}
}
} catch(const std::exception& e){
LOG_ERROR("發生錯誤:"<<e.what());
}
return true;
}
bool face_recognition::find_and_save_face(const std::string& folder_desc, const std::string& folder_src){
std::vector<std::string> files;
if(!find_file_from_folder(files, folder_src)){
return false;
}
cv::CascadeClassifier cascade;
cascade.load("./haarcascade_frontalface_alt2.xml");
for(auto& p : files){
cv::Mat src = cv::imread(p);
if(nullptr == src.data){
LOG_ERROR("文件無法正常讀取:"<<p);
continue;
}
cv::Mat gray;
gray.create(src.size(), src.type());
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
// 人臉檢測
std::vector<cv::Rect> rect;
cascade.detectMultiScale(gray, rect, 1.1, 3, 0);
if(rect.empty()){
LOG_WARN("沒有檢測到人臉:"<<p);
continue;
}
int index = 0;
for (auto& r : rect)
{
cv::Mat desc;
src(r).copyTo(desc);
imwrite((boost::format("%s/%d.png") % folder_desc % (index++)).str(), desc);
}
}
return true;
}
bool face_recognition::find_face_info(std::vector<info_recognition_ptr>& infos, const std::string& folder){
std::vector<std::string> all_folders;
if(!find_folder_from_folder(all_folders, boost::filesystem::system_complete(folder).string())){
return false;
}
int index = 1;
for(auto& d : all_folders){
std::vector<std::string> files;
if (!find_file_from_folder(files, d)) {
continue;
}
auto path = boost::filesystem::system_complete(d);
auto parent_folder = path.parent_path().string();
auto name = path.string().substr(parent_folder.size() + 1);
auto p_info = std::make_shared<info_recognition>();
p_info->index = index++;
p_info->name = name;
for(auto& f : files){
p_info->files.push_back(f);
}
infos.push_back(p_info);
}
return true;
}
bool face_recognition::find_file_from_folder(std::vector<std::string>& files, const std::string& folder, const std::string& extend){
boost::filesystem::path path_folder(folder);
if(!boost::filesystem::exists(path_folder)){
LOG_ERROR("文件夾路徑不存在:"<<folder);
return false;
}
boost::filesystem::directory_iterator end;
for(boost::filesystem::directory_iterator iter(path_folder); iter != end; ++iter){
auto path = iter->path();
if (boost::filesystem::is_directory(path)) {
continue;
}
if(!extend.empty() && extend != path.extension()){
continue;
}
files.push_back(path.string());
}
return true;
}
bool face_recognition::find_folder_from_folder(std::vector<std::string>& all_folders, const std::string& folder){
boost::filesystem::path path_folder(folder);
if(!boost::filesystem::exists(path_folder)){
LOG_ERROR("文件夾路徑不存在:"<<folder);
return false;
}
boost::filesystem::directory_iterator end;
for(boost::filesystem::directory_iterator iter(path_folder); iter != end; ++iter){
auto path = iter->path();
if (!boost::filesystem::is_directory(path)) {
continue;
}
if("." == path.string() || ".." == path.string()){
continue;
}
all_folders.push_back(path.string());
}
return true;
}