包含针对视频的简单人脸识别,后续需增加阈值确定视频中的具体人,防止跳振,还需增加相关的release和接口等操作
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <opencv4/opencv2/opencv.hpp>
#include "inspireface.h"
std::mutex mtx;
std::condition_variable face_cv;
std::atomic<bool> face_cv_flag(false);
struct FrameTokenPair{
cv::Mat frame;
std::vector<HFFaceFeatureIdentity> queryFeatureIdentitys;
std::vector<HFFaceFeature> queryFeatures;
};
class ThreadSafeTokenQueue{
private:
std::queue<FrameTokenPair> pairQueue;
std::mutex mtx_;
std::condition_variable cv_;
std::atomic<bool> stop_flag_{false};
const int max_queue_size = 50;
public:
bool addFrameToken(cv::Mat& frame, std::vector<HFFaceFeature>& queryFeatures){
std::lock_guard<std::mutex> lock(mtx_);
if (pairQueue.size() >= max_queue_size){
std::cerr << "queue is full, drop frame token" << std::endl;
return false;
}
FrameTokenPair pair;
pair.frame = frame.clone();
pair.queryFeatures = queryFeatures;
pairQueue.push(pair);
cv_.notify_one();
return true;
}
FrameTokenPair getToken(){
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock,[this](){
return !pairQueue.empty();
});
if (pairQueue.empty()){
std::cerr << "pairQueue为空,gettoken失败" << std::endl;
return {};
}
FrameTokenPair pair = std::move(pairQueue.front());
pairQueue.pop();
return pair;
}
};
bool cvMatToImageBitmap(const cv::Mat& mat, HFImageBitmap& bitmap){
if (mat.empty()){
std::cerr << "empty frame" << std::endl;
return false;
}
cv::Mat bgr_mat;
if (mat.channels() == 1){
cv::cvtColor(mat, bgr_mat, cv::COLOR_GRAY2BGR);
}else if(mat.channels() == 4){
cv::cvtColor(mat, bgr_mat, cv::COLOR_RGBA2BGR);
}else{
bgr_mat = mat;
}
HFImageBitmapData bitmapData = {0};
bitmapData.width = bgr_mat.cols;
bitmapData.height = bgr_mat.rows;
bitmapData.channels = bgr_mat.channels();
bitmapData.data = bgr_mat.data;
HResult ret = HFCreateImageBitmap(&bitmapData, &bitmap);
if (ret != HSUCCEED){
return false;
}
return true;
}
int videoDetectThread(ThreadSafeTokenQueue& threadSafeTokenQueue,
const std::string video_path){
std::cout << "人脸检测线程开始" << std::endl;
HResult ret;
HPath packPath = "/home/yfzx/project/insightface/insightface/cpp-package/inspireface/test_res/pack/Megatron";
ret = HFLaunchInspireFace(packPath); // 模型文件路径
if (ret != HSUCCEED) {
HFLogPrint(HF_LOG_ERROR, "Load Resource error: %d", ret);
return ret;
}
HOption option = HF_ENABLE_QUALITY | HF_ENABLE_FACE_RECOGNITION;
HFDetectMode detMode = HF_DETECT_MODE_ALWAYS_DETECT; // 每次都重新检测所有人脸,不追踪,视频流有额外的模式
HInt32 maxDetectNum = 20;
HInt32 detectPixelLevel = 160;
HFSession session;
ret = HFCreateInspireFaceSessionOptional(option, detMode, maxDetectNum, detectPixelLevel, -1, &session);
if (ret != HSUCCEED) {
HFLogPrint(HF_LOG_ERROR, "Create FaceContext error: %d", ret);
return ret;
}
cv::VideoCapture cap(video_path);
if (!cap.isOpened()){
std::cerr << "failed to open video file" << video_path <<std::endl;
return -1;
}
int frame_width = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int frame_height = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
int total_frames = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_COUNT));
double fps = cap.get(cv::CAP_PROP_FPS);
std::cout << "Video info: " << frame_width << "x" << frame_height
<< ", FPS: " << fps << ", Total frames: " << total_frames << std::endl;
int frame_count = 0;
cv::Mat frame;
while(cap.read(frame)){
// 1. 将opencv帧转为inspire face的image bitmap
HFImageBitmap bitmap = {0};
if (!cvMatToImageBitmap(frame, bitmap)){
frame_count++;
continue;
}
// 2. 创建图像流
HFImageStream stream = {0};
ret = HFCreateImageStreamFromImageBitmap(bitmap, HF_CAMERA_ROTATION_0, &stream);
if (ret != HSUCCEED){
std::cerr << "failed to create image stream frame" << frame_count << std::endl;
frame_count++;
continue;
}
// 3. 执行人脸识别
// HFSession session = {0};
HFMultipleFaceData multipleFaceData = {0};
ret = HFExecuteFaceTrack(session, stream, &multipleFaceData);
if (ret != HSUCCEED){
std::cerr << "检测人脸出错,error code:" << ret << std::endl;
}
std::vector<HFFaceFeature> queryFeatures;
if (multipleFaceData.detectedNum > 0){
for (int i = 0; i < multipleFaceData.detectedNum; ++i) {
// HFFaceFeature queryFeature;
HFFaceFeature feature;
ret = HFCreateFaceFeature(&feature);
if (ret != HSUCCEED){
std::cout << "创建 face feature error :" << ret << std::endl;
}
ret = HFFaceFeatureExtractTo(session, stream, multipleFaceData.tokens[i], feature);
if (ret != HSUCCEED) {
HFLogPrint(HF_LOG_ERROR, "Extract feature error: %d", ret);
return ret;
}
// queryFeature.feature = &feature;
// queryFeature.id = -1;
queryFeatures.push_back(feature);
}
threadSafeTokenQueue.addFrameToken(frame, queryFeatures);
}else{
continue; // 未检测到人脸
}
}
std::cout << "读取frame完成" << std::endl;
return 1;
}
int faceRecognitionThread(ThreadSafeTokenQueue& threadSafeTokenQueue,
char *db_path = "../database/case_crud.db"){
HResult ret;
// The resource file must be loaded before it can be used
ret = HFLaunchInspireFace("/home/yfzx/project/insightface/insightface/cpp-package/inspireface/test_res/pack/Megatron"); // 模型文件路径
if (ret != HSUCCEED) {
HFLogPrint(HF_LOG_ERROR, "Load Resource error: %d", ret);
return ret;
}
HFFeatureHubConfiguration featureHubConfig;
featureHubConfig.primaryKeyMode = HF_PK_AUTO_INCREMENT; // 自增ID
featureHubConfig.enablePersistence = 1; // 持久化到DB
featureHubConfig.persistenceDbPath = db_path; // db路径
featureHubConfig.searchMode = HF_SEARCH_MODE_EXHAUSTIVE; // 全量检索
featureHubConfig.searchThreshold = 0.48f; // 匹配阈值
ret = HFFeatureHubDataEnable(featureHubConfig);
if (ret != HSUCCEED) {
HFLogPrint(HF_LOG_ERROR, "Enable feature hub error: %d", ret);
return ret;
}
HInt32 faceCount;
ret = HFFeatureHubGetFaceCount(&faceCount);
std::cout<<"人脸数据库中人脸数量为:" << faceCount<< std::endl;
HFloat confidence;
while (true){
FrameTokenPair tokenPair = threadSafeTokenQueue.getToken();
for (int i = 0; i < tokenPair.queryFeatures.size(); ++i) {
HFFaceFeatureIdentity searchedIdentity = {0};
// searchedIdentity.feature = &tokenPair.queryFeatures[i];
// searchedIdentity.id = -1;
std::cout << tokenPair.queryFeatures[i].size << std::endl << tokenPair.queryFeatures.data() << std::endl;
ret = HFFeatureHubFaceSearch(tokenPair.queryFeatures[i], &confidence, &searchedIdentity);
if (ret != HSUCCEED){
std::cerr << "查询数据库失败" << std::endl;
continue;
}
std::cout << "查询到人脸,人脸id为:" << searchedIdentity.id << std::endl;
}
}
}
int main(){
ThreadSafeTokenQueue threadSafeTokenQueue;
std::string videopath = "../testdata/wyr.mp4";
char *db_path = "../database/case_crud.db";
std::thread t1(videoDetectThread,std::ref(threadSafeTokenQueue),videopath);
std::thread t2(faceRecognitionThread, std::ref(threadSafeTokenQueue), db_path);
t1.join();
t2.join();
return 1;
}