InspireFace 是一个跨平台的人脸识别软件开发工具包(SDK),是用 C/C++ 开发的。它能够支持多种操作系统以及多种用于推理的后端类型,例如 CPU、GPU 和 NPU。

InspireFace已经支持RK3588平台,你可以下载源码直接编译或者下载相关模型直接运行。

仓库地址:https://github.com/zg9uagfv/InspireFaceOnRK3588#

人脸导入数据库

添加人脸到数据库,代码支持从图像目录批量添加人脸图片。数据库文件路径:database/face_features.db

#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <opencv2/opencv.hpp>
#include <inspirecv/inspirecv.h>
#include <inspireface/inspireface.hpp>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <algorithm>

/**
 * @brief 从图像目录中提取所有人脸特征并添加到数据库
 * 
 * @param image_dir 图像目录路径
 * @param model_path 模型路径
 * @return int 0表示成功,非0表示失败
 */
int AddFacesFromDirectory(const std::string& image_dir, const std::string& model_path) {
    // Initialize InspireFace
    auto context = inspire::Launch::GetInstance();
    context->SwitchImageProcessingBackend(inspire::Launch::IMAGE_PROCESSING_CPU);
    
    int load_result = context->Load(model_path);
    if (load_result != 0) {
        std::cerr << "错误: 无法加载模型 (错误代码: " << load_result << ")" << std::endl;
        return -1;
    }

    // Create session with face detection and recognition enabled
    inspire::CustomPipelineParameter param;
    param.enable_recognition = true;
    param.enable_face_quality = true;
    
    std::shared_ptr<inspire::Session> session(
        inspire::Session::CreatePtr(inspire::DETECT_MODE_ALWAYS_DETECT, 1, param, 320));
    
    if (session == nullptr) {
        std::cerr << "错误: 无法创建会话" << std::endl;
        return -1;
    }

    // Initialize FeatureHubDB with persistence
    auto feature_hub = inspire::FeatureHubDB::GetInstance();
    inspire::DatabaseConfiguration db_config;
    db_config.enable_persistence = true;  // Enable persistence
    db_config.primary_key_mode = inspire::PrimaryKeyMode::AUTO_INCREMENT;  // Use auto increment ID
    db_config.recognition_threshold = 0.48f;
    
    // Create database directory if it doesn't exist
    struct stat info;
    if (stat("database", &info) != 0) {
        #if defined(_WIN32)
        _mkdir("database");
        #else
        mkdir("database", 0755);
        #endif
        std::cout << "创建数据库目录: database" << std::endl;
    }
    
    db_config.persistence_db_path = "database/face_features.db";
    
    int32_t hub_result = feature_hub->EnableHub(db_config);
    if (hub_result != 0) {
        std::cerr << "错误: 无法启用FeatureHubDB (错误代码: " << hub_result << ")" << std::endl;
        return -1;
    }
    
    // Get current face count in database to determine next ID
    int32_t face_count_before = feature_hub->GetFaceFeatureCount();
    int32_t next_id = face_count_before + 1;
    std::cout << "开始处理目录: " << image_dir << std::endl;
    std::cout << "数据库中现有人脸数量: " << face_count_before << std::endl;
    std::cout << "将从ID " << next_id << " 开始添加" << std::endl;

    // Open directory
    DIR* dir = opendir(image_dir.c_str());
    if (!dir) {
        std::cerr << "错误: 无法打开目录 " << image_dir << std::endl;
        return -1;
    }

    // Read directory entries
    struct dirent* entry;
    std::vector<std::string> image_files;
    
    while ((entry = readdir(dir)) != nullptr) {
        std::string filename = entry->d_name;
        // Check if file has image extension
        std::string ext = filename.substr(filename.find_last_of(".") + 1);
        std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
        
        if (ext == "jpg" || ext == "jpeg" || ext == "png" || ext == "bmp") {
            std::string full_path = image_dir + "/" + filename;
            image_files.push_back(full_path);
            std::cout << "找到图像文件: " << full_path << std::endl;
        }
    }
    
    closedir(dir);
    
    if (image_files.empty()) {
        std::cerr << "目录中未找到图像文件" << std::endl;
        return -1;
    }
    
    std::cout << "总共找到 " << image_files.size() << " 个图像文件" << std::endl;
    
    int success_count = 0;
    // Process each image file
    for (const auto& image_path : image_files) {
        std::cout << "\n处理图像: " << image_path << std::endl;
        
        // Load image
        cv::Mat image = cv::imread(image_path);
        if (image.empty()) {
            std::cerr << "错误: 无法加载图像 " << image_path << std::endl;
            continue;
        }

        // Convert OpenCV Mat to InspireCV Image
        inspirecv::Image img(image.cols, image.rows, 3, image.data, false);
        inspirecv::FrameProcess process = inspirecv::FrameProcess::Create(
            img.Data(), img.Height(), img.Width(), inspirecv::BGR, inspirecv::ROTATION_0);

        // Detect faces
        std::vector<inspire::FaceTrackWrap> faces;
        int detect_result = session->FaceDetectAndTrack(process, faces);
        if (detect_result != 0) {
            std::cerr << "警告: 人脸检测失败, 错误代码: " << detect_result << std::endl;
            continue;
        }

        if (faces.empty()) {
            std::cerr << "错误: 图像中未检测到人脸 " << image_path << std::endl;
            continue;
        }

        std::cout << "在 " << image_path << " 中检测到 " << faces.size() << " 张人脸" << std::endl;

        // Process the first detected face
        auto& face = faces[0];
        
        // Check if the face is frontal
        float yaw = face.face3DAngle.yaw;
        float pitch = face.face3DAngle.pitch;
        float roll = face.face3DAngle.roll;
        const float angle_threshold = 15.0f;
        bool is_frontal = (std::abs(yaw) < angle_threshold) && 
                          (std::abs(pitch) < angle_threshold) && 
                          (std::abs(roll) < angle_threshold);
        
        if (!is_frontal) {
            std::cerr << "警告: 检测到的人脸不是正脸,可能影响识别效果" << std::endl;
            std::cout << "人脸角度 - 偏航角: " << yaw << ", 俯仰角: " << pitch << ", 翻滚角: " << roll << std::endl;
        }
        
        // Extract face features
        inspire::FaceEmbedding feature;
        int extract_result = session->FaceFeatureExtract(process, face, feature);
        if (extract_result != 0) {
            std::cerr << "错误: 人脸特征提取失败, 错误代码: " << extract_result << std::endl;
            continue;
        }

        std::cout << "人脸特征提取成功,特征维度: " << feature.embedding.size() << std::endl;

        // Add face feature to database with auto increment ID
        int64_t result_id;
        std::vector<float> feature_vector(feature.embedding.begin(), feature.embedding.end());
        int32_t insert_result = feature_hub->FaceFeatureInsert(feature_vector, -1, result_id);  // -1 for auto increment
        
        if (insert_result == 0) {
            std::cout << "成功将人脸特征添加到数据库,ID: " << result_id << std::endl;
            success_count++;
        } else {
            std::cerr << "错误: 无法将人脸特征添加到数据库 (错误代码: " << insert_result << ")" << std::endl;
        }
    }
    
    // Print final database status
    int32_t face_count_after = feature_hub->GetFaceFeatureCount();
    std::cout << "\n处理完成!" << std::endl;
    std::cout << "成功添加 " << success_count << " 个人脸特征到数据库" << std::endl;
    std::cout << "数据库中现有人脸数量: " << face_count_after << std::endl;
    
    // Print all IDs in database
    if (face_count_after > 0) {
        auto& existing_ids = feature_hub->GetExistingIds();
        std::cout << "数据库中的人脸ID: ";
        for (const auto& id : existing_ids) {
            std::cout << id << " ";
        }
        std::cout << std::endl;
    }
    
    return (success_count > 0) ? 0 : -1;
}

int main(int argc, char** argv) {
    if (argc < 3) {
        std::cout << "用法: " << argv[0] << " <模型路径> [图像目录]" << std::endl;
        std::cout << "  如果提供图像目录,则从目录中所有图像提取人脸特征" << std::endl;
        std::cout << "示例:" << std::endl;
        std::cout << "  " << argv[0] << " ../model /path/to/image/directory" << std::endl;
        return -1;
    }

    std::string model_path = argv[1];

    if (argc >= 2) {
        // Check if argv[2] is a directory
        struct stat info;
        if (stat(argv[2], &info) == 0 && info.st_mode & S_IFDIR) {
            // It's a directory, process all images in the directory
            return AddFacesFromDirectory(argv[2], model_path);
        } else {
			std::cout<<"输入的参数不是目录"<<std::endl;
			return -1;
		}
    } else {
        std::cout << "参数错误,请检查用法" << std::endl;
        return -1;
    }
}

实时人脸识别

#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <opencv2/opencv.hpp>
#include <inspirecv/inspirecv.h>
#include <inspireface/inspireface.hpp>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <sys/stat.h>
#include <unistd.h>

// Function to parse command line arguments
bool ParseArguments(int argc, char** argv, std::string& model_path, int& camera_index) {
    if (argc < 2 || argc > 3) {
        std::cout << "用法: " << argv[0] << " <模型路径> [摄像头索引]" << std::endl;
        std::cout << "  摄像头索引: 0 表示默认摄像头, 1 表示第二个摄像头, 以此类推 (默认: 0)" << std::endl;
        return false;
    }

    model_path = argv[1];
    camera_index = 0;
    
    if (argc == 3) {
        camera_index = std::stoi(argv[2]);
    }
    
    return true;
}

// Function to initialize camera
bool InitializeCamera(cv::VideoCapture& cap, int camera_index) {
    cap.open(camera_index);
    if (!cap.isOpened()) {
        std::cerr << "错误: 无法打开摄像头 " << camera_index << std::endl;
        // Try alternative camera paths for USB cameras
        std::vector<std::string> camera_paths = {
            "/dev/video0", "/dev/video1", "/dev/video2", "/dev/video3"
        };
        
        bool camera_opened = false;
        for (const auto& path : camera_paths) {
            std::cout << "尝试打开 " << path << std::endl;
            cap.open(path);
            if (cap.isOpened()) {
                std::cout << "成功打开 " << path << std::endl;
                camera_opened = true;
                break;
            }
        }
        
        if (!camera_opened) {
            std::cerr << "错误: 无法打开任何摄像头设备" << std::endl;
            return false;
        }
    }

    // Set camera resolution
    cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
    cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
    
    return true;
}

// Function to load model
bool LoadModel(const std::string& model_path) {
    // Global init(only once)
    auto context = inspire::Launch::GetInstance();
    
    // Try to switch to CPU-based image processing to avoid RGA allocation issues
    // This must be done before loading the model
    auto launch_context = inspire::Launch::GetInstance();
    launch_context->SwitchImageProcessingBackend(
        inspire::Launch::IMAGE_PROCESSING_CPU);
    
    std::cout << "成功切换到CPU图像处理后端" << std::endl;
    
    int load_result = context->Load(model_path);
    if (load_result != 0) {
        std::cerr << "错误: 无法从 " << model_path << " 加载模型 (错误代码: " << load_result << ")" << std::endl;
        std::cerr << "请检查模型路径是否正确以及模型是否兼容." << std::endl;
        return false;
    }

    printf("模型加载成功!!!!!!!!!!\n");
    return true;
}

// Function to create session
std::shared_ptr<inspire::Session> CreateSession() {
    // Create session with face detection and recognition enabled
    inspire::CustomPipelineParameter param;
    param.enable_recognition = true;
	param.enable_liveness = true;
	param.enable_face_quality = true;
    
    std::shared_ptr<inspire::Session> session(
        inspire::Session::CreatePtr(inspire::DETECT_MODE_ALWAYS_DETECT, 1, param, 320));
    
    if (session == nullptr) {
        std::cerr << "错误: 无法创建会话" << std::endl;
    }
    
    return session;
}

// Function to initialize FeatureHubDB
std::shared_ptr<inspire::FeatureHubDB> InitializeFeatureHub() {
    auto feature_hub = inspire::FeatureHubDB::GetInstance();
    inspire::DatabaseConfiguration db_config;
    db_config.enable_persistence = true;  // Enable persistence
    db_config.recognition_threshold = 0.48f;  // Set recognition threshold
    
    // Check if database directory exists
    struct stat info;
    if (stat("database", &info) == 0) {
        db_config.persistence_db_path = "database/face_features.db";
        std::cout << "从数据库文件加载人脸数据: " << db_config.persistence_db_path << std::endl;
    } else {
        std::cout << "警告: 数据库目录不存在,将创建新的数据库" << std::endl;
        // Create database directory
        #if defined(_WIN32)
        _mkdir("database");
        #else
        mkdir("database", 0755);
        #endif
        db_config.persistence_db_path = "database/face_features.db";
    }
    
    int32_t hub_result = feature_hub->EnableHub(db_config);
    if (hub_result != 0) {
        std::cerr << "警告: 无法启用FeatureHubDB (错误代码: " << hub_result << ")" << std::endl;
        // Try without persistence as fallback
        db_config.enable_persistence = false;
        hub_result = feature_hub->EnableHub(db_config);
        if (hub_result != 0) {
            std::cerr << "错误: 无法启用FeatureHubDB,即使禁用持久化 (错误代码: " << hub_result << ")" << std::endl;
            return nullptr;
        }
    } else {
        std::cout << "FeatureHubDB 初始化成功,使用持久化数据库" << std::endl;
        // Print database info
        int32_t face_count = feature_hub->GetFaceFeatureCount();
        std::cout << "数据库中现有人脸数量: " << face_count << std::endl;
        
        // Print all IDs in database for debugging
        if (face_count > 0) {
            auto& existing_ids = feature_hub->GetExistingIds();
            std::cout << "数据库中的人脸ID: ";
            for (const auto& id : existing_ids) {
                std::cout << id << " ";
            }
            std::cout << std::endl;
        }
    }
    
    return feature_hub;
}

// Function to configure session parameters
void ConfigureSession(std::shared_ptr<inspire::Session> session) {
    // Configure face detection threshold (default is typically 0.5)
    // Lower values will detect more faces but may include false positives
    // Higher values will detect fewer faces but with higher confidence
    session->SetFaceDetectThreshold(0.7f);
    
    // Configure minimum face pixel size (default is 0, meaning no minimum)
    // Increase this value to filter out small faces
    session->SetFilterMinimumFacePixelSize(150);
}

// Function to check if GUI is available
bool CheckGUIAvailability() {
    bool gui_available = true;
    try {
        cv::namedWindow("人脸检测", cv::WINDOW_AUTOSIZE);
    } catch (const cv::Exception& e) {
        std::cerr << "警告: GUI不可用, 运行在无头模式下" << std::endl;
        gui_available = false;
    }
    return gui_available;
}

// Function to check if face is frontal
bool IsFrontalFace(const inspire::FaceTrackWrap& face) {
    // Check if the face is frontal (facing forward)
    // For a frontal face: yaw, pitch, and roll should be close to 0
    float yaw = face.face3DAngle.yaw;
    float pitch = face.face3DAngle.pitch;
    float roll = face.face3DAngle.roll;
    
    // Define thresholds for frontal face detection
    const float angle_threshold = 15.0f; // degrees
    return (std::abs(yaw) < angle_threshold) && 
           (std::abs(pitch) < angle_threshold) && 
           (std::abs(roll) < angle_threshold);
}

// Function to compare face with database and return match result
bool CompareFaceWithDatabase(std::shared_ptr<inspire::FeatureHubDB> feature_hub, 
                            const inspire::Embedded& embedding, 
                            cv::Mat& frame, 
                            const inspire::FaceRect& face_rect,
                            int64_t& matched_id,
                            double& similarity) {
    // Print debug info
    std::cout << "开始人脸比对..." << std::endl;
    std::cout << "特征向量维度: " << embedding.size() << std::endl;
    
    // Check database status
    int32_t face_count = feature_hub->GetFaceFeatureCount();
    std::cout << "数据库中人脸数量: " << face_count << std::endl;
    
    if (face_count == 0) {
        std::cout << "警告: 数据库为空,无法进行比对" << std::endl;
        cv::putText(frame, "数据库为空", 
                   cv::Point(face_rect.x, face_rect.y - 30), 
                   cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                   cv::Scalar(0, 0, 255), 2);
        matched_id = -1;
        similarity = 0.0;
        return false;
    }
    
    // Compare with faces in the database
    std::vector<inspire::FaceSearchResult> search_results;
    int32_t search_result = feature_hub->SearchFaceFeatureTopK(embedding, search_results, 3, false);
    std::cout << "比对结果代码: " << search_result << ", 找到匹配数量: " << search_results.size() << std::endl;
    
    if (search_result == 0 && !search_results.empty()) {
        // Get the top match
        auto& top_match = search_results[0];
        matched_id = top_match.id;
        similarity = top_match.similarity;
        
        std::cout << "找到匹配的人脸 - ID: " << matched_id << ", 相似度: " << similarity << std::endl;
        
        // Display match information on the image
        std::string match_info = "匹配ID: " + std::to_string(matched_id);
        std::string similarity_info = "相似度: " + std::to_string(static_cast<int>(similarity * 100)) + "%";
        
        cv::putText(frame, match_info, 
                   cv::Point(face_rect.x, face_rect.y - 30), 
                   cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                   cv::Scalar(255, 0, 0), 2);
        cv::putText(frame, similarity_info, 
                   cv::Point(face_rect.x, face_rect.y - 50), 
                   cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                   similarity > 0.7 ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 165, 255), 2);
        
        // Return true if match is found
        return true;
    } else {
        std::cout << "未找到匹配的人脸" << std::endl;
        std::cout << "搜索结果数量: " << search_results.size() << std::endl;
        cv::putText(frame, "未匹配", 
                   cv::Point(face_rect.x, face_rect.y - 30), 
                   cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                   cv::Scalar(0, 0, 255), 2);
        matched_id = -1;
        similarity = 0.0;
        return false;
    }
}

// Function to save face image with matched ID
void SaveFaceImageWithId(const cv::Mat& frame, const inspire::FaceRect& face_rect, int64_t matched_id) {
    // Save face as JPEG image to pic directory with matched ID in filename
    // Add bounds checking to prevent invalid ROI
    int x = std::max(0, face_rect.x);
    int y = std::max(0, face_rect.y);
    int width = std::min(face_rect.width, frame.cols - x);
    int height = std::min(face_rect.height, frame.rows - y);
    
    // Ensure we have a valid region
    if (width > 0 && height > 0) {
        try {
            // Create a copy of the ROI instead of a reference
            cv::Mat face_img;
            cv::Mat(frame, cv::Rect(x, y, width, height)).copyTo(face_img);
            
            if (!face_img.empty()) {
                // Create filename with matched ID
                auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
                    std::chrono::system_clock::now().time_since_epoch()).count();
                std::string filename = "results/face_id_" + std::to_string(matched_id) + "_" + std::to_string(timestamp) + ".jpg";
                std::cout << "尝试保存图像: " << filename << " 尺寸 " << width << "x" << height << std::endl;
                
                // Try different compression parameters
                std::vector<int> compression_params;
                compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
                compression_params.push_back(90);
                
                bool saved = cv::imwrite(filename, face_img, compression_params);
                if (saved) {
                    std::cout << "保存人脸图像: " << filename << std::endl;
                } else {
                    std::cerr << "无法保存人脸图像: " << filename << std::endl;
                    // Try saving to current directory as fallback
                    std::string fallback_filename = "face_id_" + std::to_string(matched_id) + "_" + std::to_string(timestamp) + ".jpg";
                    bool fallback_saved = cv::imwrite(fallback_filename, face_img, compression_params);
                    if (fallback_saved) {
                        std::cout << "保存人脸图像到当前目录: " << fallback_filename << std::endl;
                    } else {
                        std::cerr << "也无法保存人脸图像到当前目录" << std::endl;
                    }
                }
            } else {
                std::cerr << "无法创建人脸图像ROI副本" << std::endl;
            }
        } catch (const cv::Exception& ex) {
            std::cerr << "保存图像时发生异常: " << ex.what() << std::endl;
        }
    } else {
        std::cerr << "无效的人脸区域: " << face_rect.x << "," << face_rect.y << " " << face_rect.width << "x" << face_rect.height << std::endl;
    }
}

// Function to check if face has glasses with reflections
bool HasGlassesWithReflections(const cv::Mat& frame, const inspire::FaceTrackWrap& face) {
    // This is a simplified implementation for detecting glasses reflections
    // In a real application, you might want to use more sophisticated methods
    
    // Get face rectangle
    auto rect = face.rect;
    
    // Define regions where glasses reflections typically occur
    // These are approximate positions for the lenses area
    int eye_region_y = rect.y + rect.height / 3;  // Roughly where eyes are located
    int eye_height = rect.height / 5;  // Height of eye region
    
    // Left eye region
    int left_eye_x = rect.x + rect.width / 4;
    int left_eye_width = rect.width / 4;
    
    // Right eye region
    int right_eye_x = rect.x + rect.width / 2;
    int right_eye_width = rect.width / 4;
    
    // Extract eye regions
    cv::Rect left_eye_rect(left_eye_x, eye_region_y, left_eye_width, eye_height);
    cv::Rect right_eye_rect(right_eye_x, eye_region_y, right_eye_width, eye_height);
    
    // Ensure regions are within frame bounds
    left_eye_rect &= cv::Rect(0, 0, frame.cols, frame.rows);
    right_eye_rect &= cv::Rect(0, 0, frame.cols, frame.rows);
    
    // Check if regions are valid
    if (left_eye_rect.width <= 0 || left_eye_rect.height <= 0 ||
        right_eye_rect.width <= 0 || right_eye_rect.height <= 0) {
        return false;
    }
    
    // Extract eye regions from frame
    cv::Mat left_eye_region = frame(left_eye_rect);
    cv::Mat right_eye_region = frame(right_eye_rect);
    
    // Convert to grayscale for easier analysis
    cv::Mat left_gray, right_gray;
    cv::cvtColor(left_eye_region, left_gray, cv::COLOR_BGR2GRAY);
    cv::cvtColor(right_eye_region, right_gray, cv::COLOR_BGR2GRAY);
    
    // Apply threshold to detect bright spots (potential reflections)
    cv::Mat left_thresh, right_thresh;
    cv::threshold(left_gray, left_thresh, 200, 255, cv::THRESH_BINARY);
    cv::threshold(right_gray, right_thresh, 200, 255, cv::THRESH_BINARY);
    
    // Count white pixels (bright spots)
    int left_white_pixels = cv::countNonZero(left_thresh);
    int right_white_pixels = cv::countNonZero(right_thresh);
    
    // Calculate the percentage of bright pixels
    double left_ratio = (double)left_white_pixels / (left_eye_rect.width * left_eye_rect.height);
    double right_ratio = (double)right_white_pixels / (right_eye_rect.width * right_eye_rect.height);
    
    // If more than 10% of pixels in either eye region are very bright, 
    // we consider it as potential glasses reflection
    const double reflection_threshold = 0.1;
    bool has_reflection = (left_ratio > reflection_threshold) || (right_ratio > reflection_threshold);
    
    std::cout << "眼镜反光检测 - 左眼反光比例: " << left_ratio << ", 右眼反光比例: " << right_ratio << std::endl;
    
    return has_reflection;
}

// Main function
int main(int argc, char** argv) {
    std::string model_path;
    int camera_index;
    
    // Parse command line arguments
    if (!ParseArguments(argc, argv, model_path, camera_index)) {
        return -1;
    }

    // Initialize OpenCV video capture
    cv::VideoCapture cap;
    if (!InitializeCamera(cap, camera_index)) {
        return -1;
    }

    // Load model
    if (!LoadModel(model_path)) {
        return -1;
    }

    // Create session
    auto session = CreateSession();
    if (session == nullptr) {
        return -1;
    }
    
    // Initialize FeatureHubDB for face recognition comparison
    auto feature_hub = InitializeFeatureHub();
    if (feature_hub == nullptr) {
        return -1;
    }
    
    // Configure session parameters
    ConfigureSession(session);

    cv::Mat frame;
    bool gui_available = CheckGUIAvailability();

    std::cout << "按 'q' 键退出" << std::endl;
    std::cout << "模型成功加载自: " << model_path << std::endl;
    std::cout << "摄像头成功打开, 索引: " << camera_index << std::endl;
    
    // Variables for timing
    auto last_time = std::chrono::high_resolution_clock::now();
    
    while (true) {
        // Capture frame from camera
        cap >> frame;
        if (frame.empty()) {
            std::cerr << "错误: 无法捕获帧" << std::endl;
            break;
        }

        // Convert OpenCV Mat to InspireCV Image
        inspirecv::Image img(frame.cols, frame.rows, 3, frame.data, false);
        inspirecv::FrameProcess process = inspirecv::FrameProcess::Create(
            img.Data(), img.Height(), img.Width(), inspirecv::BGR, inspirecv::ROTATION_0);

        // Detect and track faces
        std::vector<inspire::FaceTrackWrap> results;
        int detect_result = session->FaceDetectAndTrack(process, results);
        if (detect_result != 0) {
            std::cerr << "警告: 人脸检测失败, 错误代码: " << detect_result << std::endl;
        }

        // Calculate and display time interval
        auto current_time = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_time);
        last_time = current_time;
        
        std::string time_info = "时间间隔: " + std::to_string(duration.count()) + " 毫秒";
        if (gui_available) {
            cv::putText(frame, time_info, 
                       cv::Point(10, 30), 
                       cv::FONT_HERSHEY_SIMPLEX, 0.7, 
                       cv::Scalar(0, 0, 255), 2);
        }

        // Print time interval to console for all modes
        std::cout << "人脸检测时间间隔: " << duration.count() << " 毫秒" << std::endl;

        // Process each detected face
        std::cout << "检测到 " << results.size() << " 张人脸" << std::endl;
        
        // Get face quality confidence for blur detection
        std::vector<float> face_quality_confidence = session->GetFaceQualityConfidence();
        
        for (size_t i = 0; i < results.size(); i++) {
            auto& face = results[i];
            
            // Draw face rectangle
            auto rect = face.rect;
            if (gui_available) {
                cv::rectangle(frame, cv::Rect(rect.x, rect.y, rect.width, rect.height), 
                             cv::Scalar(0, 255, 0), 2);
            }
            
            // Print face detection score/quality
            std::cout << "人脸检测质量分数: ";
            for (int j = 0; j < 5; j++) {
                std::cout << face.quality[j] << " ";
            }
            std::cout << std::endl;
            
            // Check face quality confidence for blur detection
            float quality_score = 0.0f;
            if (i < face_quality_confidence.size()) {
                quality_score = face_quality_confidence[i];
                std::cout << "人脸质量评分 (模糊度检测): " << quality_score << std::endl;
            }
            
            // Check if the face is frontal
            bool is_frontal = IsFrontalFace(face);
            std::string frontal_status = is_frontal ? "正脸" : "非正脸";
            std::cout << "人脸状态: " << frontal_status << std::endl;
            
            // Display frontal status on the image
            if (gui_available) {
                cv::putText(frame, frontal_status, 
                           cv::Point(rect.x, rect.y + rect.height + 20), 
                           cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                           is_frontal ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 0, 255), 2);
            }
            
            // Check for glasses with reflections
            bool has_glasses_reflection = HasGlassesWithReflections(frame, face);
            if (has_glasses_reflection) {
                std::cout << "检测到眼镜反光,可能影响识别质量" << std::endl;
                if (gui_available) {
                    cv::putText(frame, "眼镜反光", 
                               cv::Point(rect.x, rect.y + rect.height + 60), 
                               cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                               cv::Scalar(0, 165, 255), 2);
                }
            }
            
            // Skip face recognition if not frontal
            if (!is_frontal) {
                std::cout << "跳过非正脸的人脸识别" << std::endl;
                continue; // Skip to the next face
            }
            
            // Skip face recognition if face is too blurry (quality score is too low)
            // Quality score ranges from 0.0 (very blurry) to 1.0 (very sharp)
            const float quality_threshold = 0.5f;
            if (quality_score < quality_threshold) {
                std::cout << "跳过模糊人脸的人脸识别 (质量评分: " << quality_score << ")" << std::endl;
                if (gui_available) {
                    cv::putText(frame, "模糊", 
                               cv::Point(rect.x, rect.y + rect.height + 40), 
                               cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                               cv::Scalar(0, 0, 255), 2);
                }
                continue; // Skip to the next face
            }
            
            // Skip face recognition if there are glasses with reflections
            if (has_glasses_reflection) {
                std::cout << "跳过有眼镜反光的人脸识别" << std::endl;
                continue; // Skip to the next face
            }
            
            // Extract face features (only for frontal and sharp faces without glasses reflections)
            inspire::FaceEmbedding feature;
            int extract_result = session->FaceFeatureExtract(process, face, feature);
            if (extract_result != 0) {
                std::cerr << "警告: 人脸特征提取失败, 错误代码: " << extract_result << std::endl;
            } else {
                std::cout << "人脸特征提取成功" << std::endl;
                
                // Compare with faces in the database and get match result
                int64_t matched_id;
                double similarity;
                bool is_matched = CompareFaceWithDatabase(feature_hub, feature.embedding, frame, rect, matched_id, similarity);
                
                // Save face image only if match is found
                if (is_matched && matched_id != -1) {
                    SaveFaceImageWithId(frame, rect, matched_id);
                }
            }
            
            // Display feature vector length (for demonstration) - only if extraction was successful
            if (extract_result == 0 && gui_available) {
                std::string feature_info = "特征维度: " + std::to_string(feature.embedding.size());
                cv::putText(frame, feature_info, 
                           cv::Point(rect.x, rect.y - 10), 
                           cv::FONT_HERSHEY_SIMPLEX, 0.7, 
                           cv::Scalar(0, 255, 0), 2);
            }
            
            // Display quality score on the image
            if (gui_available) {
                std::string quality_info = "质量: " + std::to_string(static_cast<int>(quality_score * 100)) + "%";
                cv::putText(frame, quality_info, 
                           cv::Point(rect.x, rect.y + rect.height + 40), 
                           cv::FONT_HERSHEY_SIMPLEX, 0.6, 
                           quality_score > 0.5 ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 0, 255), 2);
            }
        }

        // Show the frame with detections if GUI is available
        if (gui_available) {
            cv::imshow("人脸检测", frame);
        }
        
        // Check for key press to exit (works in both GUI and headless modes)
        int key = cv::waitKey(1) & 0xFF;
        if (key == 'q' || key == 'Q' || key == 27) {  // 'q' or 'Q' key or ESC key
            std::cout << "检测到退出按键. 正在关闭..." << std::endl;
            break;
        } 

        // Additional headless mode processing
        if (!gui_available) {
            // In headless mode, add a small delay and check for exit condition
            // We'll use a simple counter to occasionally print status
            static int frame_count = 0;
            if (++frame_count % 30 == 0) {
                std::cout << "已处理 " << frame_count << " 帧..." << std::endl;
                std::cout << "时间间隔: " << duration.count() << " 毫秒" << std::endl;
                // For headless mode, we'll break after 3000 frames (about 5 seconds at 60fps)
                // You can modify this condition as needed
                if (frame_count >= 3000) {
                    std::cout << "无头模式下处理3000帧后停止" << std::endl;
                    break;
                }
            }
        }
    }

    // Release resources
    cap.release();
    if (gui_available) {
        cv::destroyAllWindows();
    }
    std::cout << "应用程序成功终止." << std::endl;
    return 0;
}

更多推荐