#ifndef ANSFR_H #define ANSFR_H #define ANSFR_API __declspec(dllexport) #pragma once #include "ANSFRCommon.h" #include "FaceDatabase.h" #include #include #define MAX_FACE_CHECKER_FRAMES 20 #define FACE_CONFIDENT_LEVEL 5 namespace ANSCENTER { struct FaceDetection { FaceResultObject faceObject; // Bounding box int faceConfLevel; // face confident level, if face is detected then it will increase otherwise, it will be recreased. }; class FaceChecker { public: FaceChecker(int maxFrames = MAX_FACE_CHECKER_FRAMES, float minScore = 0.7f) : maxFrames(maxFrames), minScore(minScore) {} // Add detected faces from a new frame std::vector ValidateDetectedFaces(const std::vector& faces); std::vector UniqueFaces(const std::vector& faces); private: int maxFrames; float minScore; std::recursive_mutex _mutex; std::unordered_map>> frameBufferMap; // Keyed by cameraId }; ///// // Fix #11: ScopedTimer now logs via SPDLogger instead of being a dead no-op class ScopedTimer { public: explicit ScopedTimer(const std::string& operation_name) : _name(operation_name) , _start(std::chrono::steady_clock::now()) { } ~ScopedTimer() noexcept { try { auto end = std::chrono::steady_clock::now(); auto ms = std::chrono::duration_cast(end - _start).count(); ANSCENTER::SPDLogger::GetInstance("ANSFR").LogDebug( "ScopedTimer", _name + " took " + std::to_string(ms) + " ms", __FILE__, __LINE__); } catch (...) {} // Never throw from destructor } private: std::string _name; std::chrono::steady_clock::time_point _start; }; class ANSFR_API ANSFacialRecognition { public: [[nodiscard]] int Initialize(const std::string & licenseKey, const std::string& configFile, const std::string& databaseFilePath, const std::string& recogniserFilePath, const std::string& detectorFilePath="", int precisionType=0, float knownPersonThreshold=0.35, bool enableAgeGender=true, bool enableFaceEmotions=true, bool enableHeadPose=true, int minFaceSize=30, float faceDetectorScoreThreshold=0.5, bool faceliveness=true, bool antiSpoofing=true); [[nodiscard]] bool LoadEngine(); int InsertUser(const std::string& userCode,const std::string& userName); int UpdateUser(int userId, const std::string& userCode, const std::string& userName); int DeleteUser(int userId); int DeleteUsers(const std::vector& userIds); int InsertFace(int userId, const cv::Mat& image); [[nodiscard]] std::vector InsertMultipleFaces(int userId, const cv::Mat& image); [[nodiscard]] int CheckFace(const cv::Mat& image);// return 0 if face is not valid otherwise 1 int DeleteFace(int faceId); int DeleteFacesByUser(int userId); int GetUser(int userId, std::string& userRecord); int GetUsers(std::string& userRecords, std::vector&userIds); int GetFace(int faceId, std::string& faceRecord); int GetFaces(int userId, std::string& faceRecords); void CheckLicense(); bool Reload(); bool UpdateUserDictionary(); bool UpdateParameters(float knownPersonThreshold = 0.35,bool enableAgeGender = true,bool enableFaceEmotions = true,bool enableHeadPose = true, int minFaceSize=112, float faceDetectorScoreThreshold=0.5, bool faceliveness=true, bool antiSpoof=true, bool removeFakeFace=false); bool GetFaceParameters(float& knownPersonThreshold, bool& enableAgeGender, bool& enableFaceEmotions, bool& enableHeadPose, int &minFaceSize, float& faceDetectorScoreThreshold, bool& faceliveness, bool& antiSpoof, bool& removeFakeFace); bool UpdateFaceQueue(int queueSize, int faceThresholdSize, bool enableFaceQueue); bool GetFaceQueue(int& queueSize, int& faceThresholdSize, bool& enableFaceQueue); [[nodiscard]] std::vector Recognize(const cv::Mat& frame); // Expect to return on 1 object [[nodiscard]] std::vector Inference(const cv::Mat& input); [[nodiscard]] std::vector Detect(const cv::Mat& input); [[nodiscard]] std::vector FaceDetect(const cv::Mat& input); [[nodiscard]] std::vector Recognize(const cv::Mat& frame, const std::string& camera_id); // Expect to return on 1 object [[nodiscard]] std::vector Inference(const cv::Mat& input, const std::string& camera_id); [[nodiscard]] std::vector Detect(const cv::Mat& input, const std::string& camera_id); [[nodiscard]] std::vector FaceDetect(const cv::Mat& input, const std::string& camera_id); [[nodiscard]] std::string FaceObjectsToJsonString(const std::vector& faces); [[nodiscard]] std::string FaceToJsonString(const std::vector& faces); [[nodiscard]] std::string PolygonToString(const std::vector& polygon); [[nodiscard]] std::string KeypointsToString(const std::vector& kps); enum class LogLevel { Debug, Info, Warn, Error, Fatal }; void LogThreadSafe(const std::string& function, const std::string& message, LogLevel level = LogLevel::Error); void LogError(const std::string& function,const std::string& message,const std::string& camera_id); void MarkAsUnknown(FaceResultObject& face); ANSFacialRecognition(); ~ANSFacialRecognition() noexcept; void UnloadEngine(); void Destroy(); void SetMaxSlotsPerGpu(int n) { m_maxSlotsPerGpu = n; } private: int m_maxSlotsPerGpu{ 1 }; // set by dllmain based on GPU topology int GetUser(int userId, UserRecord& userRecord); int GetUser(int userId, const std::string& userCode,const std::string& userName, UserRecord& userRecord); int GetUsers(std::vector& userRecords, std::vector& userIds); int GetFace(int faceId, FaceRecord& faceRecord); int GetFaces(int userId, std::vector& faceRecords); std::vector UpdateFaceAttributes(const std::vector& resultObjects, const std::string& camera_id = ""); std::vector CheckFaces(const std::vector& faceObjects,bool checkFaceSize=true); std::string GetOpenVINODevice(); ModelConfig CreateDetectorModelConfig(); bool InitializeDetector(); bool InitializeRecognizer(); bool InitializeAntispoofingModel(const std::string& deviceName); bool InitializeAgeGenderModel(const std::string& deviceName); bool InitializeEmotionModel(const std::string& deviceName); bool InitializeHeadPoseModel(const std::string& deviceName); void ensureUniqueUserIdWithHighestConfidence(std::vector& resultObjects); Object GetLargestObject(const std::vector& objects); bool AreFacesSimilar(const FaceResultObject& face1, const FaceResultObject& face2); size_t GenerateFaceHash(const FaceResultObject& face, const std::vector& detectedObjects); std::vector FindFrequentFaces(const std::deque>& faceQueue, const std::vector& detectedObjects, int occurrenceThreshold = 8); protected: bool _licenseValid; std::string _licenseKey; std::string _detectorFilePath; std::string _recognizerFilePath; std::string _recognizerModelFolder; std::string _databaseFilePath; int _precisionType; std::map _userDict; ANSCENTER::FRConfig _config; std::unique_ptr _db =std::make_unique(); EngineType engineType; bool _enableAgeGender; bool _enableFaceEmotions; bool _enableFaceLandmarks; bool _enableHeadPose; bool _enableAntiSpoof; bool _enableFaceliveness; bool _removeFakeFaces; bool _isInitialized{false}; int _minFaceSize; int _queueSize; // number of frames to store in the queue int _faceThresholdSize; // number of frames to consider for face recognition bool _enableFaceQueue; double _faceDetectorScoreThreshold; // Face threshold score for face checker int _attributeInterval{ 5 }; // Run slow attributes every N frames (1=every frame) FaceChecker faceChecker; // Add fine-grained mutexes for shared resources std::mutex _detectionMutex; // Protects face detector std::mutex _recognitionMutex; // Protects face recognizer (Feature/Forward inference only) std::mutex _databaseMutex; // For database operations // Double-buffer synchronization: protects atomic swap of recognizer index + userDict // shared_lock for inference (concurrent reads), unique_lock for reload (exclusive write) mutable std::shared_mutex _indexSwapMutex; // Reload guard: prevents redundant index rebuilds. // Set to true after any DB mutation; cleared by a successful Reload(). // If third-party code calls Reload() after a mutation that already auto-reloaded, // the flag will be false and Reload() returns immediately (no-op). std::atomic _reloadNeeded{ true }; std::mutex _ageGenderMutex; // For age/gender detector std::mutex _emotionsMutex; // For emotions detector std::mutex _antispoofMutex; // For antispoof detector std::mutex _headPoseMutex; // For head pose detector std::mutex _loggerMutex; // Protects logger (if not thread-safe) std::mutex _faceQueueMutex; // NEW: Protects face queue operations std::mutex _configMutex; // For configuration std::mutex _cameraMutex; // For camera data //std::unordered_map>> _cameraFaceQueues; std::recursive_mutex _mutex; ANSCENTER::SPDLogger& _logger = ANSCENTER::SPDLogger::GetInstance("ANSFR"); std::shared_ptr core; // Persistent OpenVINO core instance std::unique_ptr_recognizer = nullptr; std::unique_ptr_detector = nullptr; std::unique_ptr_antiSpoofDetector = nullptr; std::unique_ptr_ageGenderDetector = nullptr; std::unique_ptr_headPoseDetector = nullptr; std::unique_ptr_emotionsDetector = nullptr; const size_t QUEUE_SIZE = 10; const size_t FACE_THRESHOLD_SIZE = 5; // Cached attribute JSON per tracked face (everything except head pose). // Keyed by trackId from ANSMOT. Updated every _attributeInterval frames. // On non-attribute frames, head pose is run fresh and spliced into this JSON. struct CachedFaceAttributes { std::string attributeJson; // Full JSON from FaceAttributeToJsonString bool isUnknown = false; // Whether face was marked unknown (extreme pose etc.) }; struct CameraData { std::deque> _detectionQueue; // That stores the detection results int attributeFrameCounter = 0; // counts frames for attribute skip logic std::unordered_map cachedAttributes; // trackId → cached attrs // Adaptive interval state int currentAttributeInterval = 5; // current adaptive interval int stableFrameCount = 0; // frames with no new/lost trackIds int previousTrackIdCount = 0; // trackId count from last frame void clear() { for (auto& detectionVector : _detectionQueue) { detectionVector.clear(); } _detectionQueue.clear(); attributeFrameCounter = 0; cachedAttributes.clear(); currentAttributeInterval = 5; stableFrameCount = 0; previousTrackIdCount = 0; } }; std::unordered_map _cameras; CameraData& GetCameraData(const std::string& cameraId) { std::lock_guard lock(_mutex); // Use try_emplace to insert a default CameraData if cameraId does not exist auto [iterator, inserted] = _cameras.try_emplace(cameraId, CameraData{}); return iterator->second; // Return the reference to CameraData } void EnqueueDetection(const std::vector& detectedObjects, const std::string& cameraId); std::deque> DequeueDetection(const std::string& cameraId); std::vector PostProcess(const std::vector& detectedObjects, const std::string& cameraId); std::vector BuildFaceResultObjects(const std::vector& validFaces, const cv::Mat& originalInput, const std::string& camera_id); void ProcessFaceAttributes(Face& face, const cv::Mat& mask); // Validation helper methods bool ValidateInitialization(); bool ValidateInput(const cv::Mat& input); bool ValidateComponents(); // Frame preparation cv::Mat PrepareInputFrame(const cv::Mat& input); // Pipeline stage methods with timing std::vector DetectFaces(const cv::Mat& frame, const std::string& camera_id); std::vector RecognizeFaces(const cv::Mat& frame, const std::vector& validFaces); }; }; extern "C" ANSFR_API int CreateANSRFHandle(ANSCENTER::ANSFacialRecognition * *Handle, const char* licenseKey, const char* configFilePath, const char* databaseFilePath, const char* recogniserFilePath, const char* detectorFilePath, int precisionType=0, float knownPersonThreshold=0.35, int enableAgeGender=1, int enableFaceEmotions=1, int enableHeadPose=1, int minFaceSize=30, float faceDetectorThreshold=0.5, int enableFaceLiveness=1, int enableAntiSpoofing=1); extern "C" ANSFR_API int LoadANSRFEngine(ANSCENTER::ANSFacialRecognition * *Handle); extern "C" ANSFR_API int ReleaseANSRFHandle(ANSCENTER::ANSFacialRecognition * *Handle); extern "C" ANSFR_API std::string RunANSRFDetector(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_string, unsigned int bufferLength); extern "C" ANSFR_API std::string RunANSRFDetectorBinary(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height); extern "C" ANSFR_API std::string RunANSRFInference(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_string, unsigned int bufferLength); extern "C" ANSFR_API std::string RunANSRFInferenceBinary(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height); extern "C" ANSFR_API std::string RunANSRFRecognition(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_string, unsigned int bufferLength); extern "C" ANSFR_API std::string RunANSRFRecognitionBinary(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height); extern "C" ANSFR_API std::string RunANSRFFaceDetector(ANSCENTER::ANSFacialRecognition** Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height); //// For LabVIEW API extern "C" ANSFR_API int RunDetector_LV(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult); extern "C" ANSFR_API int RunInference_LV(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult); extern "C" ANSFR_API int RunRecognition_LV(ANSCENTER::ANSFacialRecognition * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult); extern "C" ANSFR_API int RunDetectorWithCamId_LV(ANSCENTER::ANSFacialRecognition** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId,LStrHandle detectionResult); extern "C" ANSFR_API int RunInferenceWithCamId_LV(ANSCENTER::ANSFacialRecognition** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId,LStrHandle detectionResult); extern "C" ANSFR_API int RunRecognitionWithCamId_LV(ANSCENTER::ANSFacialRecognition** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId,LStrHandle detectionResult); extern "C" ANSFR_API int RunFaceDetection_LV(ANSCENTER::ANSFacialRecognition** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId, LStrHandle detectionResult); extern "C" ANSFR_API int RunInferenceComplete_LV(ANSCENTER::ANSFacialRecognition** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, LStrHandle detectionResult, LStrHandle imageStr); extern "C" ANSFR_API int RunFaceDetectionComplete_LV(ANSCENTER::ANSFacialRecognition** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize,LStrHandle detectionResult, LStrHandle imageStr); extern "C" ANSFR_API int RunFaceRecogniserComplete_LV(ANSCENTER::ANSFacialRecognition** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize,LStrHandle detectionResult, LStrHandle imageStr); // User management APIs extern "C" ANSFR_API int InsertUser(ANSCENTER::ANSFacialRecognition * *Handle, const char* userCode, const char* userName); extern "C" ANSFR_API int UpdateUser(ANSCENTER::ANSFacialRecognition * *Handle, int userId, const char* userCode, const char* userName); extern "C" ANSFR_API int DeleteUser(ANSCENTER::ANSFacialRecognition * *Handle, int userId); extern "C" ANSFR_API int DeleteUsers(ANSCENTER::ANSFacialRecognition * *Handle, int* userIds, int count); extern "C" ANSFR_API int InsertFace(ANSCENTER::ANSFacialRecognition * *Handle, int userId, unsigned char* jpeg_string, unsigned int bufferLength); extern "C" ANSFR_API int InsertFaces(ANSCENTER::ANSFacialRecognition** Handle, int userId, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle faceIds); extern "C" ANSFR_API int CheckFaceEmbedding(ANSCENTER::ANSFacialRecognition** Handle, unsigned char* jpeg_string, unsigned int bufferLength); extern "C" ANSFR_API int InsertFaceBinary(ANSCENTER::ANSFacialRecognition * *Handle, int userId, unsigned char* jpeg_bytes, unsigned int width, unsigned int height); extern "C" ANSFR_API int DeleteFace(ANSCENTER::ANSFacialRecognition * *Handle, int faceId); // How to we know the faceId? extern "C" ANSFR_API int Reload(ANSCENTER::ANSFacialRecognition * *Handle); extern "C" ANSFR_API int UpdateParameters(ANSCENTER::ANSFacialRecognition * *Handle, float knownPersonThreshold, int enableAgeGender, int enableFaceEmotions, int enableHeadPose, int minFaceSize, float faceDetectorThreshold, int faceliveness, int antiSpoof, int removeFakeFaces); extern "C" ANSFR_API int GetParamters(ANSCENTER::ANSFacialRecognition** Handle, LStrHandle faceParams); extern "C" ANSFR_API int UpdateFaceQueue(ANSCENTER::ANSFacialRecognition** Handle, int queueSize, int numKnownFaceInQueue, int enableFaceQueue); extern "C" ANSFR_API int GetFaceQueue(ANSCENTER::ANSFacialRecognition** Handle, LStrHandle faceQueue); extern "C" ANSFR_API int GetUserString(ANSCENTER::ANSFacialRecognition * *Handle, int userId, std::string& userRecord); extern "C" ANSFR_API int GetUsersString(ANSCENTER::ANSFacialRecognition * *Handle, std::string & userRecords, std::vector& userIds); extern "C" ANSFR_API int GetFaceString(ANSCENTER::ANSFacialRecognition * *Handle, int faceId, std::string & faceRecord); extern "C" ANSFR_API int GetFacesString(ANSCENTER::ANSFacialRecognition * *Handle, int userId, std::string & faceRecords); // For LabVIEW extern "C" ANSFR_API int GetUser(ANSCENTER::ANSFacialRecognition * *Handle,int userId, LStrHandle userRecord); extern "C" ANSFR_API int GetUsers(ANSCENTER::ANSFacialRecognition * *Handle,LStrHandle userRecords); extern "C" ANSFR_API int GetFace(ANSCENTER::ANSFacialRecognition * *Handle,int faceId, LStrHandle faceRecord); extern "C" ANSFR_API int GetFaces(ANSCENTER::ANSFacialRecognition * *Handle,int userId, LStrHandle faceRecords); extern "C" ANSFR_API int DeleteFacesByUser(ANSCENTER::ANSFacialRecognition * *Handle,int userId); extern "C" ANSFR_API double BlurCalculation(unsigned char* jpeg_string, unsigned int bufferLength); #endif