功能
人脸定位,人脸识别,年龄预测,性别预测,口罩有无,眼睛睁闭识别集一体demo,活体检测效果不佳(可能是我摄像头太渣的原因),没加上。
环境
Windows10,vs2015/2017,seetaface6,opencv411
代码
#include <iostream>
#include <opencv2/opencv.hpp>
#include "seeta/FaceDetector.h"
#include "seeta/FaceLandmarker.h"
#include "seeta/FaceRecognizer.h"
#include "seeta/GenderPredictor.h"
#include "seeta/AgePredictor.h"
#include "seeta/EyeStateDetector.h"
#include "seeta/MaskDetector.h"
using namespace std;
using namespace cv;
#define IS_KEY_Q(k) (k == 113 || k == 81)
#define IS_KEY_R(k) (k == 114 || k == 82)
#define IS_KEY_SPACE(k) (k == 32)
const string TITLE = "人脸识别";
const string FACEDB = "faceDB/";
seeta::FaceDetector *FD = NULL;
seeta::FaceLandmarker *FL = NULL;
seeta::FaceRecognizer *FR = NULL;
seeta::GenderPredictor *GP = NULL;
seeta::AgePredictor *AP = NULL;
seeta::EyeStateDetector *EBD = NULL;
seeta::MaskDetector *MD = NULL;
VideoCapture *CAP = NULL;
Mat FRAME;
Mat REG_FRAME;
vector<std::shared_ptr<float>> FEATURES;
vector<Rect> FACE_RECTS;
vector<std::shared_ptr<float>> FACEDB_FEATURES;
vector<string> FACEDB_NAMES;
void register_face();
void recognize_face();
bool init();
void deinit();
void extract_features();
void extract_feature(Mat img);
int main(int argc, char **argv) {
bool r = init();
while (r) {
CAP->read(FRAME);
if (FRAME.empty()) continue;
FRAME.copyTo(REG_FRAME);
recognize_face();
imshow(TITLE, FRAME);
int key = waitKey(10);
if (IS_KEY_Q(key)) {
break;
}
if (IS_KEY_R(key)) {
register_face();
}
}
deinit();
return 0;
}
bool init() {
cout << "======基于seetaface的人脸识别======" << endl;
cout << "|Q/q ------------------------ 退出|" << endl;
cout << "|R/r ------------------------ 注册|" << endl;
cout << "===================================" << endl;
cout << "加载模型···" << endl;
seeta::ModelSetting fd_setting;
fd_setting.set_device(SEETA_DEVICE_CPU);
fd_setting.append("models/face_detector.csta");
FD = new seeta::FaceDetector(fd_setting);
seeta::ModelSetting fl_setting;
fl_setting.set_device(SEETA_DEVICE_CPU);
fl_setting.append("models/face_landmarker_pts5.csta");
FL = new seeta::FaceLandmarker(fl_setting);
seeta::ModelSetting fr_setting;
fr_setting.set_id(0);
fr_setting.append("models/face_recognizer.csta");
fr_setting.set_device(SEETA_DEVICE_CPU);
FR = new seeta::FaceRecognizer(fr_setting);
seeta::ModelSetting gp_setting("models/gender_predictor.csta");
GP = new seeta::GenderPredictor(gp_setting);
seeta::ModelSetting ap_setting("models/age_predictor.csta");
AP = new seeta::AgePredictor(ap_setting);
seeta::ModelSetting edb_setting;
edb_setting.set_device(SEETA_DEVICE_CPU);
edb_setting.set_id(0);
edb_setting.append("models/eye_state.csta");
EBD = new seeta::EyeStateDetector(edb_setting);
seeta::ModelSetting md_setting;
md_setting.set_device(SEETA_DEVICE_CPU);
md_setting.append("models/mask_detector.csta");
MD = new seeta::MaskDetector(md_setting);
if (!FD || !FL || !FR || !GP || !AP || !EBD || !MD) {
cout << "加载模型失败" << endl;
return false;
}
cout << "加载摄像头/本地视频···" << endl;
namedWindow(TITLE, 1);
//CAP = new VideoCapture(0);
CAP = new VideoCapture("qyn.mp4");
if (!CAP->isOpened()) {
cout << "加载摄像头/本地视频失败" << endl;
return false;
}
cout << "加载人脸数据库···" << endl;
vector<cv::String> files;
glob(FACEDB, files);
FACEDB_FEATURES.clear();
FACEDB_NAMES.clear();
for (int i = 0; i < files.size(); i++) {
string filename(files[i]);
Mat f = imread(filename);
if (!f.empty()) {
extract_feature(f);
int pos = filename.find_last_of("\\");
int pos2 = filename.find_last_of(".");
filename = filename.substr(pos + 1, pos2 - pos - 1);
cout << " [" << i << "] " << filename << endl;
FACEDB_NAMES.push_back(filename);
}
}
return true;
}
void deinit() {
if (FD) delete FD;
if (FL) delete FL;
if (FR) delete FR;
if (GP) delete GP;
if (AP) delete AP;
if (EBD)delete EBD;
if (MD) delete MD;
if (CAP) delete CAP;
cout << "退出" << endl;
}
void register_face() {
cout << "======人脸注册======" << endl;
if (FEATURES.size() == 1) {
string name;
cout << "请输入名字:";
cin >> name;
string path = FACEDB + name + ".jpg";
cout << "保存" << name << "到" << path << endl;
imwrite(path, REG_FRAME);
extract_feature(REG_FRAME);
FACEDB_NAMES.push_back(name);
}
else {
if (FEATURES.size() == 0) cout << "未识别到人脸" << endl;
else cout << "识别人脸数目过多" << endl;
}
}
void recognize_face() {
extract_features();
for (int i = 0; i < FEATURES.size(); i++) {
for (int j = 0; j < FACEDB_FEATURES.size(); j++) {
float similar = FR->CalculateSimilarity(FEATURES[i].get(), FACEDB_FEATURES[j].get());
if (similar > 0.75f) {
char simch[10];
sprintf_s(simch, "%.2f", similar);
putText(FRAME, FACEDB_NAMES[j] + " " + string(simch), Point(FACE_RECTS[i].x, FACE_RECTS[i].y - 10), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
break;
}
}
}
}
void extract_features() {
SeetaImageData simg;
simg.height = FRAME.rows;
simg.width = FRAME.cols;
simg.channels = FRAME.channels();
simg.data = FRAME.data;
FEATURES.clear();
FACE_RECTS.clear();
auto faces = FD->detect(simg);
for (int i = 0; i < faces.size; i++) {
auto face = faces.data[i].pos;
SeetaPointF points[5];
FL->mark(simg, faces.data[i].pos, points);
std::shared_ptr<float> feature(new float[FR->GetExtractFeatureSize()]);
FR->Extract(simg, points, feature.get());
FEATURES.push_back(feature);
FACE_RECTS.push_back(Rect(face.x, face.y, face.width, face.height));
// detect mask
float score;
bool r = MD->detect(simg, face, &score);
// detect boy/girl
seeta::GenderPredictor::GENDER gender;
GP->PredictGenderWithCrop(simg, points, gender);
// detect age
int age;
AP->PredictAgeWithCrop(simg, points, age);
// detect eye open/close
seeta::EyeStateDetector::EYE_STATE leftstate, rightstate;
EBD->Detect(simg, points, leftstate, rightstate);
rectangle(FRAME, Rect(face.x, face.y, face.width, face.height), Scalar(0, 255, 255), 2, 8, 0);
for (int i = 0; i < 5; i++) {
circle(FRAME, Point(points[i].x, points[i].y), (face.width + face.height) / 50, Scalar(255, 0, 0), -1);
//putText(FRAME, to_string(i), Point(points[i].x, points[i].y), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
}
string mask_stat = "unmask";
if (score > 0.8f) mask_stat = "mask";
putText(FRAME, mask_stat, Point(FACE_RECTS[i].x, FACE_RECTS[i].y + 16), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
putText(FRAME, "sex:" + string((gender == 0 ? "boy" : "girl")),
Point(face.x, face.y + face.height - 10), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
putText(FRAME, "age:" + to_string(age), Point(face.x, face.y + face.height - 40), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
putText(FRAME, leftstate == seeta::EyeStateDetector::EYE_CLOSE ? "X" : "O", Point(points[0].x, points[0].y), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
putText(FRAME, rightstate == seeta::EyeStateDetector::EYE_CLOSE ? "X" : "O", Point(points[1].x, points[1].y), cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 255), 2);
}
}
void extract_feature(Mat img) {
SeetaImageData simg;
simg.height = img.rows;
simg.width = img.cols;
simg.channels = img.channels();
simg.data = img.data;
auto faces = FD->detect(simg);
if (faces.size >= 1) {
SeetaPointF points[5];
FL->mark(simg, faces.data[0].pos, points);
std::shared_ptr<float> feature(new float[FR->GetExtractFeatureSize()]);
FR->Extract(simg, points, feature.get());
FACEDB_FEATURES.push_back(feature);
}
}
效果
资源
下载地址:https://pan.baidu.com/s/1-HSEL9PbN_b6GKtFxYYsFg // 密码:ccff
参考
https://github.com/seetafaceengine/SeetaFace6