Refactor project structure

This commit is contained in:
2026-03-28 19:56:39 +11:00
parent 1d267378b2
commit 8a2e721058
511 changed files with 59 additions and 48 deletions

3435
modules/ANSFR/ANSFR.cpp Normal file

File diff suppressed because it is too large Load Diff

352
modules/ANSFR/ANSFR.h Normal file
View File

@@ -0,0 +1,352 @@
#ifndef ANSFR_H
#define ANSFR_H
#define ANSFR_API __declspec(dllexport)
#pragma once
#include "ANSFRCommon.h"
#include "FaceDatabase.h"
#include <shared_mutex>
#include <atomic>
#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<FaceResultObject> ValidateDetectedFaces(const std::vector<FaceResultObject>& faces);
std::vector<FaceResultObject> UniqueFaces(const std::vector<FaceResultObject>& faces);
private:
int maxFrames;
float minScore;
std::recursive_mutex _mutex;
std::unordered_map<std::string, std::vector<std::vector<FaceDetection>>> 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<std::chrono::milliseconds>(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<int>& userIds);
int InsertFace(int userId, const cv::Mat& image);
[[nodiscard]] std::vector<int> 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<int>&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<FaceResultObject> Recognize(const cv::Mat& frame); // Expect to return on 1 object
[[nodiscard]] std::vector<FaceResultObject> Inference(const cv::Mat& input);
[[nodiscard]] std::vector<FaceResultObject> Detect(const cv::Mat& input);
[[nodiscard]] std::vector<Object> FaceDetect(const cv::Mat& input);
[[nodiscard]] std::vector<FaceResultObject> Recognize(const cv::Mat& frame, const std::string& camera_id); // Expect to return on 1 object
[[nodiscard]] std::vector<FaceResultObject> Inference(const cv::Mat& input, const std::string& camera_id);
[[nodiscard]] std::vector<FaceResultObject> Detect(const cv::Mat& input, const std::string& camera_id);
[[nodiscard]] std::vector<Object> FaceDetect(const cv::Mat& input, const std::string& camera_id);
[[nodiscard]] std::string FaceObjectsToJsonString(const std::vector<FaceResultObject>& faces);
[[nodiscard]] std::string FaceToJsonString(const std::vector<Object>& faces);
[[nodiscard]] std::string PolygonToString(const std::vector<cv::Point2f>& polygon);
[[nodiscard]] std::string KeypointsToString(const std::vector<float>& 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();
private:
int GetUser(int userId, UserRecord& userRecord);
int GetUser(int userId, const std::string& userCode,const std::string& userName, UserRecord& userRecord);
int GetUsers(std::vector<UserRecord>& userRecords, std::vector<int>& userIds);
int GetFace(int faceId, FaceRecord& faceRecord);
int GetFaces(int userId, std::vector<FaceRecord>& faceRecords);
std::vector<FaceResultObject> UpdateFaceAttributes(const std::vector<FaceResultObject>& resultObjects,
const std::string& camera_id = "");
std::vector<Object> CheckFaces(const std::vector<Object>& 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<FaceResultObject>& resultObjects);
Object GetLargestObject(const std::vector<Object>& objects);
bool AreFacesSimilar(const FaceResultObject& face1, const FaceResultObject& face2);
size_t GenerateFaceHash(const FaceResultObject& face, const std::vector<FaceResultObject>& detectedObjects);
std::vector<FaceResultObject> FindFrequentFaces(const std::deque<std::vector<FaceResultObject>>& faceQueue,
const std::vector<FaceResultObject>& 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<std::string, std::string> _userDict;
ANSCENTER::FRConfig _config;
std::unique_ptr<FaceDatabase> _db =std::make_unique<FaceDatabase>();
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<bool> _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<std::string, std::deque<std::vector<FaceResultObject>>> _cameraFaceQueues;
std::recursive_mutex _mutex;
ANSCENTER::SPDLogger& _logger = ANSCENTER::SPDLogger::GetInstance("ANSFR");
std::shared_ptr<ov::Core> core; // Persistent OpenVINO core instance
std::unique_ptr<ANSFRBase>_recognizer = nullptr;
std::unique_ptr<ANSFDBase>_detector = nullptr;
std::unique_ptr<AntispoofingClassifier>_antiSpoofDetector = nullptr;
std::unique_ptr<AgeGenderDetection>_ageGenderDetector = nullptr;
std::unique_ptr<HeadPoseDetection>_headPoseDetector = nullptr;
std::unique_ptr<EmotionsDetection>_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<std::vector<FaceResultObject>> _detectionQueue; // That stores the detection results
int attributeFrameCounter = 0; // counts frames for attribute skip logic
std::unordered_map<int, CachedFaceAttributes> 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<std::string, CameraData> _cameras;
CameraData& GetCameraData(const std::string& cameraId) {
std::lock_guard<std::recursive_mutex> 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<FaceResultObject>& detectedObjects, const std::string& cameraId);
std::deque<std::vector<FaceResultObject>> DequeueDetection(const std::string& cameraId);
std::vector<FaceResultObject> PostProcess(const std::vector<FaceResultObject>& detectedObjects, const std::string& cameraId);
std::vector<FaceResultObject> BuildFaceResultObjects(const std::vector<Object>& 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<Object> DetectFaces(const cv::Mat& frame, const std::string& camera_id);
std::vector<FaceResultObject> RecognizeFaces(const cv::Mat& frame, const std::vector<Object>& 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<int>& 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

File diff suppressed because it is too large Load Diff

370
modules/ANSFR/ANSFRCommon.h Normal file
View File

@@ -0,0 +1,370 @@
#ifndef ANSFRCOMMON_H
#define ANSFRCOMMON_H
#define ANSF_API __declspec(dllexport)
#pragma once
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "NvInfer.h"
//#include <cublasLt.h>
#include "ANSLicense.h"
#include "LabVIEWHeader/extcode.h"
#include "Utility.h"
#include "macros.h"
#include "ANSEngineCommon.h"
#include "openvino/openvino.hpp"
using namespace nvinfer1;
#define CUDACHECK(status) \
do\
{\
auto ret = (status);\
if (ret != 0)\
{\
std::cerr << "Cuda failure: " << ret << std::endl;\
abort();\
}\
} while (0)
namespace ANSCENTER {
struct UserRecord {
int UserId;
std::string UserCode;
std::string UserName;
std::vector<int> FaceIds;
};
struct FaceRecord {
int FaceId;
int UserId;
std::string ImagePath;
};
struct alignas(float) Detection {
float bbox[4]; //x1 y1 x2 y2
float class_confidence;
float landmark[10];
};
struct CroppedFace {
cv::Mat face;
cv::Mat faceMat;
int x1, y1, x2, y2;
};
struct Bbox {
int x1, y1, x2, y2;
float score;
};
struct Paths {
std::string absPath;
std::string className;
};
struct AnchorBox {
float cx;
float cy;
float sx;
float sy;
};
struct FRConfig {
std::string _databasePath;
int _videoFrameWidth;
int _videoFrameHeight;
std::string _detEngineFile;
std::vector<int> _detInputShape;
std::string _detInputName;
std::vector<std::string> _detOutputNames;
int _detMaxBatchSize;
float _detThresholdNMS;
float _detThresholdBbox;
std::vector<int> _recInputShape;
int _recOutputDim;
std::string _recEngineFile;
int _maxFacesPerScene;
float _knownPersonThreshold;
int _recMaxBatchSize;
std::string _recInputName;
std::string _recOutputName;
bool _gen;
std::string _gen_imgSource;
bool _gen_imgIsCropped;
bool _apiImageIsCropped;
};
struct HeadPoseResults {
float angle_r;
float angle_p;
float angle_y;
};
struct AgeGenderResults {
float age;
float maleProb;
};
//// Class for face attribute
class ANSF_API Face {
public:
//using Ptr = std::shared_ptr<Face>;
explicit Face(size_t id, cv::Rect& location);
void updateAge(float value);
void updateGender(float value);
void updateEmotions(std::map<std::string, float> values);
void updateHeadPose(HeadPoseResults values);
void updateLandmarks(std::vector<float> values);
void updateRealFaceConfidence(float value);
void updateFaceLiveness(int value);
int getAge();
bool isMale();
bool isReal();
float getAntispoofingScore();
int getFaceLiveness();
std::map<std::string, float> getEmotions();
std::pair<std::string, float> getMainEmotion();
HeadPoseResults getHeadPose();
const std::vector<float>& getLandmarks();
size_t getId();
void ageGenderEnable(bool value);
void emotionsEnable(bool value);
void headPoseEnable(bool value);
void landmarksEnable(bool value);
void antispoofingEnable(bool value);
void faceLivenessEnable(bool value);
bool isAgeGenderEnabled();
bool isEmotionsEnabled();
bool isHeadPoseEnabled();
bool isLandmarksEnabled();
bool isAntispoofingEnabled();
bool isFaceLivenessEnabled();
public:
cv::Rect _location;
float _intensity_mean;
private:
size_t _id;
float _age;
float _maleScore;
float _femaleScore;
std::map<std::string, float> _emotions;
HeadPoseResults _headPose;
std::vector<float> _landmarks;
float _realFaceConfidence;
int _faceLiveness;
bool _isAgeGenderEnabled;
bool _isEmotionsEnabled;
bool _isHeadPoseEnabled;
bool _isLandmarksEnabled;
bool _isAntispoofingEnabled;
bool _isFaceLivenessEnabled;
};
class ANSF_API BaseDetection {
public:
BaseDetection(const std::string& pathToModel, bool doRawOutputMessages);
virtual ~BaseDetection() = default;
virtual std::shared_ptr<ov::Model> read(const ov::Core& core) = 0;
bool enabled() const;
ov::InferRequest request;
ov::Tensor inTensor;
std::string pathToModel;
ov::Shape inShape;
const bool doRawOutputMessages;
};
class ANSF_API HeadPoseDetection :public BaseDetection {
public:
std::recursive_mutex _mutex;
std::string outputAngleR;
std::string outputAngleP;
std::string outputAngleY;
std::string _modelFilePath;
//size_t enquedFaces;
cv::Mat cameraMatrix;
HeadPoseDetection(const std::string& pathToModel, bool doRawOutputMessages);
virtual std::shared_ptr<ov::Model> read(const ov::Core& core) override;
HeadPoseResults runInfer(const cv::Mat& frame);
//void submitRequest();
//void enqueue(const cv::Mat& face);
//HeadPoseResults getResult(int idx);
//std::vector<HeadPoseResults> getAllResults();
};
class ANSF_API AgeGenderDetection :public BaseDetection {
public:
std::recursive_mutex _mutex;
std::string outputAge;
std::string outputGender;
//size_t enquedFaces;
std::string _modelFilePath;
AgeGenderDetection(const std::string& pathToModel,
bool doRawOutputMessages);
virtual std::shared_ptr<ov::Model> read(const ov::Core& core) override;
AgeGenderResults runInfer(const cv::Mat& frame);
//void submitRequest();
//void enqueue(const cv::Mat& face);
//AgeGenderResults getResult(int idx);
//std::vector<AgeGenderResults> getAllResults();
};
class ANSF_API EmotionsDetection : public BaseDetection {
public:
std::recursive_mutex _mutex;
//size_t enquedFaces;
std::string _modelFilePath;
const std::vector<std::string> emotionsVec = { "neutral", "happy", "sad", "surprise", "anger" };
EmotionsDetection(const std::string& pathToModel,
bool doRawOutputMessages);
virtual std::shared_ptr<ov::Model> read(const ov::Core& core) override;
std::map<std::string, float> runInfer(const cv::Mat& frame);
//void submitRequest();
//void enqueue(const cv::Mat& face);
//std::map<std::string, float> getResult(int idx);
//std::vector<std::map<std::string, float>> getAllResults();
};
class FacialLandmarksDetection : public BaseDetection {
public:
//size_t enquedFaces;
std::vector<std::vector<float>> landmarks_results;
std::vector<cv::Rect> faces_bounding_boxes;
std::string _modelFilePath;
std::recursive_mutex _mutex;
FacialLandmarksDetection(const std::string& pathToModel,
bool doRawOutputMessages);
virtual std::shared_ptr<ov::Model> read(const ov::Core& core) override;
std::vector<float> runInfer(const cv::Mat& frame);
//void submitRequest();
//void enqueue(const cv::Mat& face);
//std::vector<float> getResult(int idx);
};
class ANSF_API AntispoofingClassifier : public BaseDetection {
public:
std::recursive_mutex _mutex;
//size_t enquedFaces;
std::string _modelFilePath;
AntispoofingClassifier(const std::string& pathToModel,bool doRawOutputMessages);
virtual std::shared_ptr<ov::Model> read(const ov::Core& core) override;
float runInfer(const cv::Mat& frame);
//void submitRequest();
//void enqueue(const cv::Mat& frame);
//float getResult(int idx);
};
class ANSFRHelper {
public:
static unsigned char* CVMatToBytes(cv::Mat image, unsigned int& bufferLengh);
static void GetCroppedFaces(const cv::Mat& input, std::vector<Object>& outputBbox, int resize_w, int resize_h, std::vector<struct CroppedFace>& croppedFaces);
static void GetFilePaths(std::string rootPath, std::vector<struct Paths>& paths);
static bool LoadConfigFile(std::string configFile, FRConfig& config);
static std::string StringCurrentDateTime(); // Return current datetime in string format
static std::string FaceObjectsToJsonString(const std::vector<FaceResultObject>& faces);
static cv::Mat PreprocessImg(cv::Mat& img, int inputW, int inputH);
static int ReadFilesInDir(const char* pDirName, std::vector<std::string>& fileNames);
static cv::Rect GetRectAdaptLandmark(cv::Mat& img, int inputW, int inputH, float bBox[4], float lmk[10]);
static float IOU(float lbox[4], float rbox[4]);
static bool CMP(const Detection& a, const Detection& b);
static void NMS(std::vector<Detection>& res, float* output, float nms_thresh = 0.4);
static std::map<std::string, Weights> LoadWeights(const std::string file);
static Weights GetWeights(std::map<std::string, Weights>& weightMap, std::string key);
static IScaleLayer* AddBatchNorm2D(INetworkDefinition* network, std::map<std::string, Weights>& weightMap, ITensor& input, std::string lName, float eps);
static std::string UserRecordToJsonString(const UserRecord userRecord);
static std::string UserRecordsToJsonString(const std::vector<UserRecord> userRecords);
static std::string FaceRecordToJsonString(const FaceRecord faceRecord);
static std::string FaceRecordsToJsonString(const std::vector<FaceRecord> faceRecords);
static std::string FaceAttributeToJsonString(Face face);
};
float calcIoU(cv::Rect& src, cv::Rect& dst);
float calcMean(const cv::Mat& src);
Face matchFace(cv::Rect rect, std::list<Face>& faces);
// Class to extend TensorRT logger
/* class TRTLogger : public nvinfer1::ILogger {
public:
void log(Severity severity, const char* msg) noexcept override {
if (severity <= Severity::kWARNING) {
std::cout << "TENSORRT_ENGINE:" << msg << std::endl;
}
}
void LogDebug(const std::string source, std::string message) {
std::cout << source << ":" << message << std::endl;
}
void LogInfo(const std::string source, std::string message) {
std::cout << source << ":" << message << std::endl;
}
void LogError(const std::string source, std::string message) {
std::cout << source << ":" << message << std::endl;
}
void LogFatal(const std::string source, std::string message) {
std::cout << source << ":" << message << std::endl;
}
~TRTLogger() {
}
};
class MatMul {
public:
MatMul();
~MatMul();
void Init(float* knownEmbeds, int numRow, int numCol);
void Calculate(float* embeds, int embedCount, float* outputs);
private:
cudaDataType_t cudaDataType = CUDA_R_32F;
cublasComputeType_t computeType = CUBLAS_COMPUTE_32F;
cublasLtHandle_t ltHandle;
cublasOperation_t transa = CUBLAS_OP_T;
cublasOperation_t transb = CUBLAS_OP_N;
void* workspace;
const size_t workspaceSize = 1024 * 1024 * 4;
cudaStream_t stream;
float* dA, * dB, * dC;
const float alpha = 1, beta = 0;
int m, n, k, lda, ldb, ldc;
cublasLtMatmulDesc_t operationDesc = NULL;
cublasLtMatrixLayout_t Adesc = NULL;
cublasLtMatmulPreference_t preference = NULL;
};
class Int8EntropyCalibrator2 : public nvinfer1::IInt8EntropyCalibrator2
{
public:
Int8EntropyCalibrator2(int batchsize, int input_w, int input_h, const char* img_dir, const char* calib_table_name, const char* input_blob_name, bool read_cache = true);
virtual ~Int8EntropyCalibrator2();
int getBatchSize() const TRT_NOEXCEPT override;
bool getBatch(void* bindings[], const char* names[], int nbBindings) TRT_NOEXCEPT override;
const void* readCalibrationCache(size_t& length) TRT_NOEXCEPT override;
void writeCalibrationCache(const void* cache, size_t length) TRT_NOEXCEPT override;
private:
int batchsize_;
int input_w_;
int input_h_;
int img_idx_;
std::string img_dir_;
std::vector<std::string> img_files_;
size_t input_count_;
std::string calib_table_name_;
const char* input_blob_name_;
bool read_cache_;
void* device_input_;
std::vector<char> calib_cache_;
};*/
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
#ifndef ANSFACERECOGNISER_H
#define ANSFACERECOGNISER_H
#pragma once
#include "ANSFRCommon.h"
#include "hnswlib/hnswlib.h"
#include "cnn.hpp"
#include "face_reid.hpp"
#include "openvino/openvino.hpp"
#include <faiss/IndexFlat.h>
#include <faiss/IndexIDMap.h>
#include <faiss/gpu/GpuIndexFlat.h>
#include <faiss/gpu/StandardGpuResources.h>
#include <unordered_map>
#include "engine.h"
#include "engine/EnginePoolManager.h"
#include "ONNXEngine.h"
#define USE_ONNX_ENGINE
//#define CPU_MODE
//#define USE_CPU_BATCH_MODE
namespace ANSCENTER {
class ANSFaceRecognizer : public ANSFRBase {
public:
virtual bool Initialize(std::string licenseKey,
ModelConfig modelConfig,
const std::string& modelZipFilePath,
const std::string& modelZipPassword,
std::string& labelMap) override;
virtual bool LoadModel(const std::string& modelZipFilePath,
const std::string& modelZipPassword) override;
bool OptimizeModel(bool fp16, std::string& optimizedModelFolder);
// Single face feature
std::vector<float> Feature(const cv::Mat& image,const ANSCENTER::Object& bBox);
// Full pipeline: embeddings -> FAISS search -> results
std::vector<FaceResultObject> Match(const cv::Mat& input, const std::vector<ANSCENTER::Object>& bBox, const std::map<std::string, std::string>& userDict);
cv::Mat GetCropFace(const cv::Mat& input, const ANSCENTER::Object& bBox);
void Init();
void AddEmbedding(const std::string& className, float embedding[]);
void AddEmbedding(const std::string& className, const std::vector<float>& embedding);
// Double-buffer support: atomically swap FAISS index + mapping
void SwapIndex(std::shared_ptr<faiss::IndexIDMap> newIndex,
std::unordered_map<faiss::idx_t, std::string>&& newFaceIdToUserId);
int GetEmbeddingSize() const { return FACE_EMBEDDING_SIZE; }
std::shared_ptr<faiss::gpu::StandardGpuResources> GetGpuResources() const { return m_gpuResources; }
bool UpdateParamater(double knownPersonThreshold) {
_modelConfig.unknownPersonThreshold = knownPersonThreshold;
m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
return true;
}
~ANSFaceRecognizer();
bool Destroy();
// L2-normalize a vector in-place (public — used by ANSFR::Reload)
static void L2NormalizeInPlace(std::vector<float>& vec);
private:
bool LoadEngine(const std::string& xmlModelPath, bool engineOptimisation = true);
// Batched forward: one embedding per Object.mask (caller must hold _mutex)
std::vector<std::vector<float>> ForwardUnlocked(const cv::Mat& input,const std::vector<ANSCENTER::Object>& outputBbox);
// FAISS search (caller must hold _mutex)
std::tuple<std::vector<std::string>, std::vector<float>>
SearchForFacesUnlocked(const std::vector<std::vector<float>>& detectedEmbeddings);
std::string GetOpenVINODevice();
// Single-face GPU inference
std::vector<float> RunArcFace(const cv::Mat& input);
// Batched GPU inference
std::vector<std::vector<float>> RunArcFaceBatch(
const std::vector<cv::Mat>& faceROIs,
const std::vector<cv::cuda::GpuMat>& gpuFaceROIs = {});
protected:
const int GPU_FACE_WIDTH = 112;
const int GPU_FACE_HEIGHT = 112;
const int FACE_EMBEDDING_SIZE = 512;
std::unordered_map<faiss::idx_t, std::string> _faceIdToUserId;
faiss::idx_t _nextFaceId = 0; // Sequential ID counter for backward-compat AddEmbedding
ModelConfig _modelConfig;
std::string _modelFilePath;
std::string _landmarkModelFilePath;
ANSCENTER::Options m_options;
const std::array<float, 3> SUB_VALS{ 0.5f, 0.5f, 0.5f };
const std::array<float, 3> DIV_VALS{ 0.5f, 0.5f, 0.5f };
const bool NORMALIZE = true;
std::recursive_mutex _mutex;
float m_knownPersonThresh = 0.35f;
EngineType engineType;
#ifdef USE_ONNX_ENGINE
std::unique_ptr<GlintArcFace> faceRecognizer = nullptr;
const int CPU_FACE_WIDTH = 112;
const int CPU_FACE_HEIGHT = 112;
#else
std::unique_ptr<VectorCNN> faceRecognizer = nullptr; // OpenVINO
const int CPU_FACE_WIDTH = 160;
const int CPU_FACE_HEIGHT = 160;
#endif
// Pooled GPU buffers to avoid per-frame allocation (Fix #8)
cv::cuda::Stream m_gpuStream;
cv::cuda::GpuMat m_gpuImg;
cv::cuda::GpuMat m_gpuResized;
cv::cuda::GpuMat m_gpuRgb;
std::shared_ptr<Engine<float>> m_trtEngine = nullptr; // NVIDIA TensorRT
EnginePoolManager<float>::PoolKey m_poolKey;
bool m_usingSharedPool = false;
int m_maxSlotsPerGpu{ -1 }; // -1 = elastic mode (on-demand slots, auto-cleanup)
void SetMaxSlotsPerGpu(int n) override { m_maxSlotsPerGpu = n; }
std::shared_ptr<faiss::IndexIDMap> faiss_index;
std::shared_ptr<faiss::gpu::StandardGpuResources> m_gpuResources;
};
}
#endif

View File

@@ -0,0 +1,23 @@
// ANSGpuFrameRegistry.cpp — Cross-DLL singleton resolver for ANSFR.dll.
//
// Finds the canonical ANSGpuFrameRegistry instance exported by ANSCV.dll
// via GetProcAddress. No link dependency on ANSCV.lib needed.
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include "ANSGpuFrameRegistry.h"
ANSGpuFrameRegistry* ANSGpuFrameRegistry::resolveProcessWide() {
// ANSCV.dll is always loaded before inference starts (it provides RTSP).
HMODULE hMod = GetModuleHandleA("ANSCV.dll");
if (hMod) {
typedef ANSGpuFrameRegistry* (*GetInstanceFn)();
auto fn = reinterpret_cast<GetInstanceFn>(
GetProcAddress(hMod, "ANSGpuFrameRegistry_GetInstance"));
if (fn) return fn();
}
// Fallback: local instance (unit tests without ANSCV.dll).
static ANSGpuFrameRegistry local;
return &local;
}

692
modules/ANSFR/ARCFaceRT.cpp Normal file
View File

@@ -0,0 +1,692 @@
#include "ARCFaceRT.h"
#include "NvOnnxParser.h"
namespace ANSCENTER {
bool ArcFace::Initialize(std::string licenseKey,
ModelConfig modelConfig,
const std::string& modelZipFilePath,
const std::string& modelZipPassword,
std::string& labelMap) {
bool result = ANSFRBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
if (!result) return false;
try {
_modelConfig = modelConfig;
_modelConfig.modelType = ModelType::FACERECOGNIZE;
_modelConfig.detectionType = DetectionType::FACERECOGNIZER;
m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
if (m_knownPersonThresh == 0.0f) m_knownPersonThresh = 0.35f;
std::string onnxfile50 = CreateFilePath(_modelFolder, "ansfacerecognizer50.onnx");
if (std::filesystem::exists(onnxfile50)) {
_modelFilePath = onnxfile50;
_logger.LogDebug("ArcFace::Initialize. Loading arcface50 weight", _modelFilePath, __FILE__, __LINE__);
}
else {
std::string onnxfile = CreateFilePath(_modelFolder, "ansfacerecognizer.onnx");
if (std::filesystem::exists(onnxfile)) {
_modelFilePath = onnxfile;
_logger.LogDebug("ArcFace::Initialize. Loading arcface weight", _modelFilePath, __FILE__, __LINE__);
}
else {
_logger.LogError("ArcFace::Initialize. Model arcface.onnx file does not exist", _modelFilePath, __FILE__, __LINE__);
return false;
}
}
// Configure engine with batch support
m_options.precision = ANSCENTER::Precision::FP32;
m_options.optBatchSize = 8; // expected typical batch
m_options.maxBatchSize = 32; // maximum number of faces per frame you want
m_options.calibrationBatchSize = 8;
m_options.deviceIndex = 0;
m_trtEngine.UpdateOptions(m_options);
if (FileExist(_modelFilePath)) {
bool succ = m_trtEngine.buildLoadNetwork(_modelFilePath, SUB_VALS, DIV_VALS, NORMALIZE);
if (!succ) {
_logger.LogError("ArcFace::Initialize. Unable to build or load TensorRT engine.",
_modelFilePath, __FILE__, __LINE__);
return false;
}
}
else {
_logger.LogError("ArcFace::Initialize. Model file does not exist",
_modelFilePath, __FILE__, __LINE__);
return false;
}
Init();
_isInitialized = true;
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ArcFace::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
try {
bool result = ANSFRBase::LoadModel(modelZipFilePath, modelZipPassword);
if (!result) return false;
std::string onnxfile50 = CreateFilePath(_modelFolder, "ansfacerecognizer50.onnx");
if (std::filesystem::exists(onnxfile50)) {
_modelFilePath = onnxfile50;
_logger.LogDebug("ArcFace::LoadModel. Loading arcface50 weight", _modelFilePath, __FILE__, __LINE__);
}
else {
std::string onnxfile = CreateFilePath(_modelFolder, "ansfacerecognizer.onnx");
if (std::filesystem::exists(onnxfile)) {
_modelFilePath = onnxfile;
_logger.LogDebug("ArcFace::LoadModel. Loading arcface weight", _modelFilePath, __FILE__, __LINE__);
}
else {
_logger.LogError("ArcFace::LoadModel. Model arcface.onnx file does not exist",
_modelFilePath, __FILE__, __LINE__);
return false;
}
}
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace50::LoadModel", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ArcFace::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
if (!FileExist(_modelFilePath)) {
optimizedModelFolder = "";
return false;
}
optimizedModelFolder = GetParentFolder(_modelFilePath);
m_options.optBatchSize = 8;
m_options.maxBatchSize = 32;
m_options.engineFileDir = optimizedModelFolder;
m_options.precision = Precision::FP32;
Engine<float> engine(m_options);
auto succ = engine.buildWithRetry(_modelFilePath, SUB_VALS, DIV_VALS, NORMALIZE);
if (!succ) {
const std::string errMsg =
"Error: Unable to build the TensorRT engine. Try increasing TensorRT log severity to kVERBOSE.";
_logger.LogError("ArcFace::OptimizeModel", errMsg, __FILE__, __LINE__);
return false;
}
return true;
}
std::vector<float> ArcFace::Feature(const cv::Mat& image, const ANSCENTER::Object& bBox) {
std::vector<float> embedding;
// Early validation before locking
if (image.empty()) {
return embedding;
}
if (image.cols < 10 || image.rows < 10) {
return embedding;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
return RunArcFace(bBox.mask);
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::Feature", e.what(), __FILE__, __LINE__);
return std::vector<float>();
}
}
std::vector<FaceResultObject> ArcFace::Match(
const cv::Mat& input,
const std::vector<ANSCENTER::Object>& bBox,
const std::map<std::string, std::string>& userDict) {
std::vector<FaceResultObject> resultObjects;
// Early validation before locking
if (input.empty()) {
return resultObjects;
}
if (input.cols < 10 || input.rows < 10) {
return resultObjects;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (!_isInitialized) {
_logger.LogError("ArcFace::Match", "Model is not initialized", __FILE__, __LINE__);
return resultObjects;
}
try {
// Get embeddings
std::vector<std::vector<float>> detectedEmbeddings = Forward(input, bBox);
// Search for matches
std::vector<std::string> names;
std::vector<float> sims;
std::tie(names, sims) = SearchForFaces(detectedEmbeddings);
if (names.empty()) {
_logger.LogError("ArcFace::Match", "No face is match", __FILE__, __LINE__);
return resultObjects;
}
// Pre-reserve result space
const size_t resultCount = std::min(names.size(), bBox.size());
resultObjects.reserve(resultCount);
// Build result objects
for (size_t i = 0; i < resultCount; ++i) {
FaceResultObject resultObject;
// Determine if face is known or unknown
const bool isUnknown = (sims[i] > m_knownPersonThresh);
if (isUnknown) {
resultObject.isUnknown = true;
resultObject.userId = "0";
resultObject.userName = "Unknown";
resultObject.confidence = 1.0f;
}
else {
resultObject.isUnknown = false;
resultObject.userId = names[i];
// Safe map lookup with fallback
auto it = userDict.find(names[i]);
resultObject.userName = (it != userDict.end()) ? it->second : names[i];
resultObject.confidence = 1.0f - sims[i];
}
resultObject.similarity = sims[i];
// Copy bounding box and additional data
resultObject.box = bBox[i].box;
resultObject.mask = bBox[i].mask;
resultObject.cameraId = bBox[i].cameraId;
resultObject.trackId = bBox[i].trackId;
resultObject.polygon = bBox[i].polygon;
resultObject.kps = bBox[i].kps;
resultObject.extraInformation = bBox[i].extraInfo;
resultObjects.push_back(std::move(resultObject));
}
return resultObjects;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::Match", e.what(), __FILE__, __LINE__);
return resultObjects;
}
}
cv::Mat ArcFace::GetCropFace(const cv::Mat& input, const ANSCENTER::Object& bBox) {
try {
std::vector<ANSCENTER::Object> outputBbox;
outputBbox.reserve(1);
outputBbox.push_back(bBox);
std::vector<CroppedFace> crFaces;
ANSFRHelper::GetCroppedFaces(input, outputBbox, 112, 112, crFaces);
if (crFaces.empty()) {
return cv::Mat();
}
return crFaces[0].faceMat;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::GetCropFace", e.what(), __FILE__, __LINE__);
return cv::Mat();
}
}
bool ArcFace::LoadEngine(const std::string onnxModelPath, bool engineOptimisation) {
try {
if (!FileExist(onnxModelPath)) {
_logger.LogError("ArcFace::LoadEngine", "Cannot find the raw ONNX model file.", __FILE__, __LINE__);
return false;
}
m_options.precision = ANSCENTER::Precision::FP32;
m_options.optBatchSize = 8;
m_options.maxBatchSize = 32;
m_options.calibrationBatchSize = 8;
m_options.deviceIndex = 0;
m_trtEngine.UpdateOptions(m_options);
if (FileExist(onnxModelPath)) {
bool succ = m_trtEngine.buildLoadNetwork(onnxModelPath, SUB_VALS, DIV_VALS, NORMALIZE);
if (!succ) {
_logger.LogError("ArcFace::LoadEngine. Unable to build or load TensorRT engine.",
onnxModelPath, __FILE__, __LINE__);
return false;
}
}
else {
_logger.LogError("ArcFace::LoadEngine. Model file does not exist",
onnxModelPath, __FILE__, __LINE__);
return false;
}
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::LoadEngine", e.what(), __FILE__, __LINE__);
return false;
}
}
std::vector<float> ArcFace::RunArcFace(const cv::Mat& inputImage) {
std::vector<float> embedding;
// Early validation before locking
if (inputImage.empty()) {
_logger.LogError("ArcFace::RunArcFace", "Input image is empty", __FILE__, __LINE__);
return embedding;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
if (!_isInitialized) {
_logger.LogError("ArcFace::RunArcFace", "Model is not initialized", __FILE__, __LINE__);
return embedding;
}
// GPU preprocessing pipeline
cv::cuda::Stream stream;
cv::cuda::GpuMat d_img;
// Upload to GPU
d_img.upload(inputImage, stream);
// Handle grayscale conversion on GPU
if (inputImage.channels() == 1) {
cv::cuda::GpuMat d_bgr;
cv::cuda::cvtColor(d_img, d_bgr, cv::COLOR_GRAY2BGR, 0, stream);
d_img = d_bgr;
}
// Resize on GPU if needed
if (inputImage.cols != FACE_WIDTH || inputImage.rows != FACE_HEIGHT) {
cv::cuda::GpuMat d_resized;
cv::cuda::resize(d_img, d_resized, cv::Size(FACE_WIDTH, FACE_HEIGHT),
0, 0, cv::INTER_LINEAR, stream);
d_img = d_resized;
}
// BGR to RGB conversion on GPU
cv::cuda::GpuMat d_rgb;
cv::cuda::cvtColor(d_img, d_rgb, cv::COLOR_BGR2RGB, 0, stream);
// Prepare inference inputs
std::vector<cv::cuda::GpuMat> inputVec;
inputVec.emplace_back(std::move(d_rgb));
std::vector<std::vector<cv::cuda::GpuMat>> inputs;
inputs.emplace_back(std::move(inputVec));
// Run inference
std::vector<std::vector<std::vector<float>>> featureVectors;
bool succ = m_trtEngine.runInference(inputs, featureVectors);
stream.waitForCompletion();
if (!succ) {
_logger.LogError("ArcFace::RunArcFace", "Failed to run inference.", __FILE__, __LINE__);
return embedding;
}
if (!featureVectors.empty() && !featureVectors[0].empty()) {
embedding = std::move(featureVectors[0][0]);
}
return embedding;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::RunArcFace", e.what(), __FILE__, __LINE__);
return embedding;
}
}
std::vector<std::vector<float>> ArcFace::RunArcFaceBatch(const std::vector<cv::Mat>& faceROIs) {
std::vector<std::vector<float>> embeddings;
try {
if (!_isInitialized) {
_logger.LogError("ArcFace::RunArcFaceBatch", "Model is not initialized", __FILE__, __LINE__);
return embeddings;
}
if (faceROIs.empty()) {
return embeddings;
}
if (faceROIs.size() > static_cast<size_t>(m_options.maxBatchSize)) {
_logger.LogError("ArcFace::RunArcFaceBatch",
"Batch size exceeds maxBatchSize", __FILE__, __LINE__);
return embeddings;
}
const auto& inputDims = m_trtEngine.getInputDims();
if (inputDims.empty() || inputDims[0].nbDims < 3) {
_logger.LogError("ArcFace::RunArcFaceBatch",
"Invalid engine input dims", __FILE__, __LINE__);
return embeddings;
}
// Pre-reserve embeddings space
embeddings.reserve(faceROIs.size());
// GPU preprocessing pipeline
cv::cuda::Stream stream;
std::vector<cv::cuda::GpuMat> batchGpu;
batchGpu.reserve(faceROIs.size());
const cv::Size targetSize(FACE_WIDTH, FACE_HEIGHT);
for (size_t i = 0; i < faceROIs.size(); ++i) {
const cv::Mat& roi = faceROIs[i];
if (roi.empty()) {
_logger.LogWarn("ArcFace::RunArcFaceBatch",
"Empty ROI at index " + std::to_string(i) + ", skipping",
__FILE__, __LINE__);
continue;
}
// Upload to GPU
cv::cuda::GpuMat d_img;
d_img.upload(roi, stream);
// Handle grayscale conversion on GPU
if (roi.channels() == 1) {
cv::cuda::GpuMat d_bgr;
cv::cuda::cvtColor(d_img, d_bgr, cv::COLOR_GRAY2BGR, 0, stream);
d_img = d_bgr;
}
// Resize on GPU if needed
if (roi.cols != FACE_WIDTH || roi.rows != FACE_HEIGHT) {
cv::cuda::GpuMat d_resized;
cv::cuda::resize(d_img, d_resized, targetSize, 0, 0, cv::INTER_LINEAR, stream);
d_img = d_resized;
}
// BGR to RGB conversion on GPU
cv::cuda::GpuMat d_rgb;
cv::cuda::cvtColor(d_img, d_rgb, cv::COLOR_BGR2RGB, 0, stream);
batchGpu.emplace_back(std::move(d_rgb));
}
if (batchGpu.empty()) {
return embeddings;
}
// Prepare inference inputs
std::vector<std::vector<cv::cuda::GpuMat>> inputs;
inputs.reserve(1);
inputs.emplace_back(std::move(batchGpu));
// Run inference
std::vector<std::vector<std::vector<float>>> featureVectors;
bool succ = m_trtEngine.runInference(inputs, featureVectors);
stream.waitForCompletion();
if (!succ) {
_logger.LogError("ArcFace::RunArcFaceBatch", "runInference failed", __FILE__, __LINE__);
return embeddings;
}
if (featureVectors.empty() || featureVectors[0].empty()) {
_logger.LogError("ArcFace::RunArcFaceBatch", "Empty featureVectors", __FILE__, __LINE__);
return embeddings;
}
embeddings = std::move(featureVectors[0]);
return embeddings;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::RunArcFaceBatch", e.what(), __FILE__, __LINE__);
return embeddings;
}
}
std::vector<std::vector<float>> ArcFace::Forward(const cv::Mat& input,const std::vector<ANSCENTER::Object>& outputBbox)
{
std::vector<std::vector<float>> detectedEmbeddings;
// Early validation before locking
if (input.empty()) {
_logger.LogError("ArcFace::Forward",
"Input image is empty", __FILE__, __LINE__);
return detectedEmbeddings;
}
if (outputBbox.empty()) {
return detectedEmbeddings;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Pre-reserve output space
detectedEmbeddings.reserve(outputBbox.size());
// Collect valid face ROIs
std::vector<cv::Mat> faceROIs;
faceROIs.reserve(outputBbox.size());
for (const auto& obj : outputBbox) {
if (!obj.mask.empty()) {
faceROIs.push_back(obj.mask);
}
}
if (faceROIs.empty()) {
return detectedEmbeddings;
}
// Run batch inference
detectedEmbeddings = RunArcFaceBatch(faceROIs);
return detectedEmbeddings;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::Forward", e.what(), __FILE__, __LINE__);
return detectedEmbeddings;
}
}
std::tuple<std::vector<std::string>, std::vector<float>>
ArcFace::SearchForFaces(const std::vector<std::vector<float>>& detectedEmbeddings) {
std::vector<std::string> detectedUsers;
std::vector<float> simValues;
// Early exit before locking
if (detectedEmbeddings.empty()) {
return std::make_tuple(detectedUsers, simValues);
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Pre-reserve output space
detectedUsers.reserve(detectedEmbeddings.size());
simValues.reserve(detectedEmbeddings.size());
if (!classNames.empty() && faiss_index && faiss_index->ntotal > 0) {
// Determine k based on database size
const int k = std::min(3, static_cast<int>(faiss_index->ntotal));
// Pre-allocate search buffers (reuse across iterations)
std::vector<faiss::idx_t> indices(k);
std::vector<float> distances(k);
std::vector<float> matchEmbedding(faiss_index->d);
for (const auto& embedding : detectedEmbeddings) {
if (embedding.size() != FACE_EMBEDDING_SIZE) {
detectedUsers.push_back("0");
simValues.push_back(1.0f);
continue;
}
// Search FAISS index
faiss_index->search(1, embedding.data(), k,
distances.data(), indices.data());
// Find best match (minimum distance for L2)
auto min_it = std::min_element(distances.begin(), distances.end());
int best_index = static_cast<int>(std::distance(distances.begin(), min_it));
// Validate index
faiss::idx_t id = indices[best_index];
if (id < 0 || id >= static_cast<faiss::idx_t>(classNames.size())) {
detectedUsers.push_back("0");
simValues.push_back(1.0f);
continue;
}
// Reconstruct embedding and compute similarity
faiss_index->reconstruct(id, matchEmbedding.data());
float cosine = CosineSimilarity(embedding, matchEmbedding, false);
float similarity = 1.0f - cosine;
detectedUsers.push_back(classNames[id]);
simValues.push_back(std::abs(similarity));
}
}
else {
detectedUsers.assign(detectedEmbeddings.size(), "0");
simValues.assign(detectedEmbeddings.size(), 1.0f);
}
return std::make_tuple(std::move(detectedUsers), std::move(simValues));
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::SearchForFaces", e.what(), __FILE__, __LINE__);
// Return appropriate sized vectors on error
detectedUsers.assign(detectedEmbeddings.size(), "0");
simValues.assign(detectedEmbeddings.size(), 1.0f);
return std::make_tuple(detectedUsers, simValues);
}
}
void ArcFace::Init() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
classNames.clear();
if (faiss_index) {
faiss_index->reset();
}
else {
faiss_index = std::make_unique<faiss::IndexFlatL2>(FACE_EMBEDDING_SIZE);
}
}
catch (const std::exception& e) {
_isInitialized = false;
_logger.LogFatal("ArcFace::Init", e.what(), __FILE__, __LINE__);
}
}
ArcFace::~ArcFace() {
try {
Destroy();
}
catch (const std::exception& e) {
// Log but don't throw - exceptions in destructors are dangerous
_logger.LogError("ArcFace::~ArcFace", e.what(), __FILE__, __LINE__);
}
catch (...) {
// Catch all exceptions to prevent std::terminate
_logger.LogError("ArcFace::~ArcFace", "Unknown exception during destruction", __FILE__, __LINE__);
}
}
bool ArcFace::Destroy() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
classNames.clear();
if (faiss_index) {
faiss_index->reset();
faiss_index.reset();
}
_isInitialized = false;
m_trtEngine.clearGpuBuffers();
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::Destroy", e.what(), __FILE__, __LINE__);
return false;
}
}
void ArcFace::AddEmbedding(const std::string& className, float embedding[]) {
// Validate input before locking
if (!embedding) {
_logger.LogError("ArcFace::AddEmbedding",
"Null embedding pointer.", __FILE__, __LINE__);
return;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
if (!faiss_index) {
_logger.LogError("ArcFace::AddEmbedding",
"FAISS index is not initialized.", __FILE__, __LINE__);
return;
}
// Direct add without intermediate vector copy
classNames.push_back(className);
faiss_index->add(1, embedding);
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::AddEmbedding", e.what(), __FILE__, __LINE__);
}
}
void ArcFace::AddEmbedding(const std::string& className, const std::vector<float>& embedding) {
// Early validation before locking
if (embedding.size() != FACE_EMBEDDING_SIZE) {
_logger.LogError("ArcFace::AddEmbedding",
"Embedding size does not match expected output dimension of 512.", __FILE__, __LINE__);
return;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
if (!faiss_index) {
_logger.LogError("ArcFace::AddEmbedding",
"FAISS index is not initialized.", __FILE__, __LINE__);
return;
}
classNames.push_back(className);
faiss_index->add(1, embedding.data());
}
catch (const std::exception& e) {
_logger.LogFatal("ArcFace::AddEmbedding", e.what(), __FILE__, __LINE__);
}
}
}

134
modules/ANSFR/ARCFaceRT.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef ARCFACERT_H
#define ARCFACERT_H
#pragma once
#include "ANSFRCommon.h"
//#include "fastdeploy/vision.h"
#include "hnswlib/hnswlib.h"
#include <faiss/IndexFlat.h>
#include "engine.h"
namespace ANSCENTER {
class ArcFace : public ANSFRBase {
public:
virtual bool Initialize(std::string licenseKey,
ModelConfig modelConfig,
const std::string& modelZipFilePath,
const std::string& modelZipPassword,
std::string& labelMap) override;
virtual bool LoadModel(const std::string& modelZipFilePath,
const std::string& modelZipPassword) override;
bool OptimizeModel(bool fp16, std::string& optimizedModelFolder);
// Single face feature (uses RunArcFace)
std::vector<float> Feature(const cv::Mat& image, const ANSCENTER::Object& bBox);
// Main entry: run recognition for all faces in bBox
std::vector<FaceResultObject> Match(const cv::Mat& input,
const std::vector<ANSCENTER::Object>& bBox,
const std::map<std::string, std::string>& userDict);
cv::Mat GetCropFace(const cv::Mat& input, const ANSCENTER::Object& bBox);
void Init();
void AddEmbedding(const std::string& className, float embedding[]);
void AddEmbedding(const std::string& className, const std::vector<float>& embedding);
bool UpdateParamater(double knownPersonThreshold) {
_modelConfig.unknownPersonThreshold = knownPersonThreshold;
m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
return true;
}
~ArcFace();
bool Destroy();
private:
bool LoadEngine(const std::string onnxModelPath, bool engineOptimisation = true);
// Batched forward: one embedding per Object.mask
std::vector<std::vector<float>> Forward(const cv::Mat& input,
const std::vector<ANSCENTER::Object>& outputBbox);
// Search embeddings in FAISS index
std::tuple<std::vector<std::string>, std::vector<float>>
SearchForFaces(const std::vector<std::vector<float>>& detectedEmbeddings);
// Single-face inference (kept for Feature)
std::vector<float> RunArcFace(const cv::Mat& input);
// New: batched inference helper
std::vector<std::vector<float>> RunArcFaceBatch(const std::vector<cv::Mat>& faceROIs);
protected:
std::vector<std::string> classNames;
ModelConfig _modelConfig;
std::string _modelFilePath;
float m_knownPersonThresh = 0.35f;
const int FACE_WIDTH = 112;
const int FACE_HEIGHT = 112;
const int FACE_EMBEDDING_SIZE = 512;
std::unique_ptr<faiss::IndexFlatL2> faiss_index;
ANSCENTER::Options m_options;
std::recursive_mutex _mutex;
Engine<float> m_trtEngine;
float m_ratio = 1.0f;
float m_imgWidth = 0.0f;
float m_imgHeight = 0.0f;
const std::array<float, 3> SUB_VALS{ 0.5f, 0.5f, 0.5f };
const std::array<float, 3> DIV_VALS{ 0.5f, 0.5f, 0.5f };
const bool NORMALIZE = true;
};
// Using FastDeploy Enging to perform facial recognition
// class ArcFace :public ANSFRBase {
// public:
// virtual bool Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) override;
// virtual bool LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword)override;
// bool OptimizeModel(bool fp16, std::string& optimizedModelFolder);
// std::vector<float> Feature(const cv::Mat& input, ANSCENTER::Object bBox); // Run inference and get embedding information from a cropped image
// std::vector<FaceResultObject> Match(const cv::Mat& input, std::vector<ANSCENTER::Object> bBox, std::map<std::string, std::string>userDict); // Run inference and get embedding information from a cropped image (the first bbox)
// cv::Mat GetCropFace(const cv::Mat& input, ANSCENTER::Object bBox);
// void Init();
// void AddEmbedding(const std::string& className, float embedding[]);
// void AddEmbedding(const std::string& className, const std::vector<float>& embedding);
// bool UpdateParamater(double knownPersonThreshold) {
// _modelConfig.unknownPersonThreshold = knownPersonThreshold;
// _modelConfig.unknownPersonThreshold = knownPersonThreshold;
// m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
// return true;
// }
//~ArcFace();
// bool Destroy();
// private:
// bool LoadEngine(const std::string engineFile, bool engineOptimisation = true);
// std::vector<std::vector<float>> Forward(const cv::Mat& input, std::vector<ANSCENTER::Object> outputBbox);
// std::tuple<std::vector<std::string>, std::vector<float>> SearchForFaces(const std::vector<std::vector<float>>& detectedEmbeddings);
//std::vector<float> RunArcFace(const cv::Mat& input);
// protected:
// std::vector<std::string> classNames;
// ModelConfig _modelConfig;
// std::string _modelFilePath;
// float m_knownPersonThresh;
// const int FACE_WIDTH = 112;
// const int FACE_HEIGHT = 112;
// const int FACE_EMBEDDING_SIZE = 512;
// std::unique_ptr<faiss::IndexFlatL2> faiss_index; // Use shared_ptr
// ANSCENTER::Options m_options;
// std::recursive_mutex _mutex;
// Engine<float> m_trtEngine;
// float m_ratio = 1;
// float m_imgWidth = 0;
// float m_imgHeight = 0;
// const std::array<float, 3> SUB_VALS{ 0.5f, 0.5f, 0.5f };
// const std::array<float, 3> DIV_VALS{ 0.5f, 0.5f, 0.5f };
// const bool NORMALIZE = true;
// };
}
#endif

View File

@@ -0,0 +1,78 @@
# ANSFR — Face Recognition DLL (ONNX + TensorRT + FAISS + SQLite)
# Explicit source list from original vcxproj
set(ANSFR_SOURCES
ANSFR.cpp
ANSFRCommon.cpp
ANSFaceRecognizer.cpp
ANSGpuFrameRegistry.cpp
FaceDatabase.cpp
FaceNet.cpp
dllmain.cpp
pch.cpp
sqlite3.c
# Engine sources compiled into ANSFR
${CMAKE_SOURCE_DIR}/engines/ONNXEngine/ONNXEngine.cpp
${CMAKE_SOURCE_DIR}/engines/OpenVINOEngine/src/faceapp/cnn.cpp
${CMAKE_SOURCE_DIR}/engines/OpenVINOEngine/src/utils/args_helper.cpp
${CMAKE_SOURCE_DIR}/engines/OpenVINOEngine/src/utils/common.cpp
)
file(GLOB ANSFR_HEADERS "*.h")
add_library(ANSFR SHARED ${ANSFR_SOURCES} ${ANSFR_HEADERS})
target_include_directories(ANSFR PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(ANSFR PRIVATE
${CMAKE_SOURCE_DIR}/engines/ONNXEngine
${CMAKE_SOURCE_DIR}/engines/OpenVINOEngine/include
${CMAKE_SOURCE_DIR}/engines/TensorRTAPI/include
${CMAKE_SOURCE_DIR}/modules/ANSODEngine
${SHARED_INCLUDE_DIR}
)
target_include_directories(ANSFR PRIVATE
${CMAKE_SOURCE_DIR}/modules/ANSMOT
${CMAKE_SOURCE_DIR}/engines/OpenVINOEngine/include/faceapp
${ANSLIBS_DIR}/faiss
)
target_link_libraries(ANSFR
PUBLIC ANSLibsLoader
PRIVATE ANSODEngine
PRIVATE ANSLicensingSystem
PRIVATE labview
PRIVATE spdlog_dep
PRIVATE opencv
PRIVATE onnxruntime
PRIVATE tensorrt
PRIVATE openvino
PRIVATE faiss
PRIVATE mkl
PRIVATE boost
PRIVATE ANSMOT
PRIVATE CUDA::cudart_static
PRIVATE CUDA::cublas
PRIVATE CUDA::cublasLt
PRIVATE nvinfer_10.lib
PRIVATE nvonnxparser_10.lib
)
# Boost asio needs full boost install path
target_include_directories(ANSFR PRIVATE ${RESEARCH_DIR}/boost_1_88_0)
target_compile_definitions(ANSFR PRIVATE UNICODE _UNICODE
ANSFR_EXPORTS
ENGINE_EXPORTS
ORT_API_MANUAL_INIT
_USRDLL
)
target_precompile_headers(ANSFR PRIVATE pch.h)
# sqlite3.c is pure C — skip precompiled headers and force C language
set_source_files_properties(sqlite3.c PROPERTIES
SKIP_PRECOMPILE_HEADERS ON
LANGUAGE C
)

View File

@@ -0,0 +1,909 @@
#include "FaceDatabase.h"
namespace ANSCENTER {
FaceDatabase::FaceDatabase() {
m_db = nullptr;
m_embedding_dim = 0;
}
bool FaceDatabase::Initialize(std::string path, int embedding_dim, std::string dataFolder) {
std::lock_guard<std::mutex> lock(_mutex);
try {
sqlite3_stmt* stmt;
_dataFolder = dataFolder;
m_embedding_dim = embedding_dim;
std::string sql;
int rc;
int isNotEmpty = 0;
//this->_logger.LogDebug("FaceDatabase::Initialize. Database path: ", path, __FILE__, __LINE__);
std::string faceDatabaseFolder = GetParentFolder(path);
if (!FolderExist(faceDatabaseFolder)) fs::create_directory(faceDatabaseFolder);
std::string imageDataFolder = CreateFilePath(faceDatabaseFolder, "data");
if (!FolderExist(imageDataFolder)) fs::create_directory(imageDataFolder);
rc = sqlite3_open(path.c_str(), &m_db);
if (rc) {
_logger.LogError("FaceDatabase::Initialize", std::string("Can't open database: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
else {
_logger.LogDebug("FaceDatabase::Initialize", "Opened database successfully", __FILE__, __LINE__);
}
// Fix #2: Enable WAL mode for concurrent read/write from separate processes
sqlite3_exec(m_db, "PRAGMA journal_mode=WAL;", nullptr, nullptr, nullptr);
// Fix #2: 5-second busy timeout for multi-process contention
sqlite3_exec(m_db, "PRAGMA busy_timeout=5000;", nullptr, nullptr, nullptr);
// Fix #2: Enable foreign key enforcement
sqlite3_exec(m_db, "PRAGMA foreign_keys=ON;", nullptr, nullptr, nullptr);
// Prepare
sql = "SELECT name FROM sqlite_master;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::Initialize. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
// Step
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
isNotEmpty = 1;
break;
}
sqlite3_finalize(stmt);
if (isNotEmpty) {
//this->_logger.LogDebug("FaceDatabase::Initialize.", "Database is not empty", __FILE__, __LINE__);
}
else {
//this->_logger.LogDebug("FaceDatabase::Initialize.", "Empty Database, initiating...", __FILE__, __LINE__);
// Prepare
sql = "CREATE TABLE IF NOT EXISTS USER ( \
ID INTEGER PRIMARY KEY AUTOINCREMENT, \
CODE TEXT, \
NAME TEXT, \
UNIQUE(ID, CODE)\
)";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::Initialize. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
// this->_logger.LogError("FaceDatabase::Initialize. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
else {
// this->_logger.LogDebug("FaceDatabase::Initialize.", "Table `USER` created successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
// Prepare
sql = "CREATE TABLE IF NOT EXISTS FACE ( \
ID INTEGER PRIMARY KEY AUTOINCREMENT, \
USER INTEGER, \
IMG_PATH TEXT, \
EMBEDDING BLOB, \
UNIQUE(ID, USER), \
FOREIGN KEY(USER) REFERENCES USER(ID) \
)";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::Initialize. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
// this->_logger.LogError("FaceDatabase::Initialize. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
else {
// this->_logger.LogDebug("FaceDatabase::Initialize", "Table `FACE` created successfully.", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
}
return true;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::Initialize", e.what(), __FILE__, __LINE__);
//sqlite3_finalize(stmt);
return false;
}
}
int FaceDatabase::InsertUser(std::string userId, std::string userName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
// Prepare
sql = "INSERT INTO USER (CODE, NAME) VALUES (?, ?);";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
return -2;
}
// Binding
rc = sqlite3_bind_text(stmt, 1, userId.c_str(), int(userId.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
rc = sqlite3_bind_text(stmt, 2, userName.c_str(), int(userName.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -4;
}
if (rc == SQLITE_DONE) {
// this->_logger.LogDebug("FaceDatabase::InsertUser:", "Record successfully created", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
int id = (int)sqlite3_last_insert_rowid(m_db);
resultCode = id;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::InsertUser", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::UpdateUser(int userId, std::string newUserCode, std::string newUserName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int resultCode = 0;
int rc;
// Prepare
// UPDATE USER SET CODE ="0003", NAME= "Biden" WHERE ID =1
sql = "UPDATE USER SET CODE=?, NAME=? WHERE ID=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
return -1;
}
// Binding
rc = sqlite3_bind_text(stmt, 1, newUserCode.c_str(), int(newUserCode.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
rc = sqlite3_bind_text(stmt, 2, newUserName.c_str(), int(newUserName.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
rc = sqlite3_bind_int(stmt, 3, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_DONE) {
// this->_logger.LogDebug("FaceDatabase::InsertUser:", "Record successfully created", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::InsertUser", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
int FaceDatabase::InsertFace(int userId, std::string imgPath, float embedding[]) {
std::lock_guard<std::mutex> lock(_mutex);
if (!FileExist(imgPath)) return 0;
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
// Prepare
sql = "INSERT INTO FACE (USER, IMG_PATH, EMBEDDING) VALUES (?, ?, ?);";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//std::cout << "SQL error: " << sqlite3_errmsg(m_db);
return -2;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertFace. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
rc = sqlite3_bind_text(stmt, 2, imgPath.c_str(), int(imgPath.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertFace. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
rc = sqlite3_bind_blob(stmt, 3, embedding, sizeof(float) * m_embedding_dim, SQLITE_STATIC);
if (rc != SQLITE_OK) {
//::cout << "SQL bind error: " << sqlite3_errmsg(m_db);
resultCode = -3;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::InsertFace. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -4;
}
if (rc == SQLITE_DONE) {
//this->_logger.LogDebug("FaceDatabase::InsertFace.", "Embedding for user created successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
int id = (int)sqlite3_last_insert_rowid(m_db);
resultCode = id;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::InsertFace", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::DeleteFace(int id) {
try {
// Fix #4: Single lock scope — GetFaceById + DELETE in one atomic section
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int userId;
int resultCode = 0;
std::string faceImagePath;
// Look up face image path (now inside lock — no TOCTOU race)
{
sqlite3_stmt* lookupStmt;
rc = sqlite3_prepare_v2(m_db, "SELECT USER, IMG_PATH FROM FACE WHERE ID=?;", -1, &lookupStmt, NULL);
if (rc == SQLITE_OK) {
sqlite3_bind_int(lookupStmt, 1, id);
if (sqlite3_step(lookupStmt) == SQLITE_ROW) {
userId = sqlite3_column_int(lookupStmt, 0);
const char* pathText = reinterpret_cast<const char*>(sqlite3_column_text(lookupStmt, 1));
faceImagePath = pathText ? std::string(pathText) : "";
}
}
sqlite3_finalize(lookupStmt);
}
// Delete image file
if (!faceImagePath.empty() && FileExist(faceImagePath)) {
if (!fs::remove(faceImagePath)) {
_logger.LogError("FaceDatabase::DeleteFace", "Failed to delete face image: " + faceImagePath, __FILE__, __LINE__);
}
}
//_mutex.lock();
// Prepare
sql = "DELETE FROM FACE WHERE ID=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteFace. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, id);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteFace. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::DeleteFace. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_DONE) {
// this->_logger.LogDebug("FaceDatabase::DeleteFace.", "Embedding for user deleted successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::DeleteFace", e.what(), __FILE__, __LINE__);
//_mutex.unlock();
return 0;
}
}
int FaceDatabase::DeleteUser(int userId) {
try {
std::string sql;
sqlite3_stmt* stmt;
int resultCode = 0;
int rc;
std::vector<int> faceIds;
faceIds.clear();
GetFaceIdsByUser(userId, faceIds);
int numOfFaces = (int)faceIds.size();
//this->_logger.LogDebug("FaceDatabase::DeleteUser. Number of Face for this user", std::to_string(numOfFaces), __FILE__, __LINE__);
if (numOfFaces > 0) {
for (int i = 0; i < numOfFaces; i++) {
if (!DeleteFace(faceIds[i])) {
//this->_logger.LogError("FaceDatabase::DeleteUser. Failed to delete face id", std::to_string(numOfFaces), __FILE__, __LINE__);
}
}
}
std::string userImageFolderName = "User" + std::to_string(userId);
std::string userImageFolder = CreateFilePath(_dataFolder, userImageFolderName);
if (FolderExist(userImageFolder)) {
if (!DeleteFolder(userImageFolder)) {
//this->_logger.LogError("FaceDatabase::DeleteUser. Failed to delete user data folder", userImageFolder, __FILE__, __LINE__);
}
}
// Delete person
std::lock_guard<std::mutex> lock(_mutex);
//_mutex.lock();
// Prepare
sql = "DELETE FROM USER WHERE ID=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::DeleteUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_DONE) {
//this->_logger.LogDebug("FaceDatabase::DeleteUser.", "Deleted person successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::DeleteUser", e.what(), __FILE__, __LINE__);
return 0;
}
}
int FaceDatabase::GetUserId(std::string code) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
// Prepare
sql = "SELECT ID FROM USER WHERE CODE =?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
int resultCode = 0;
int userId = -1;
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_text(stmt, 1, code.c_str(), int(code.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_ROW) {
//this->_logger.LogDebug("FaceDatabase::GetUser.", "Get User information successfully", __FILE__, __LINE__);
userId = sqlite3_column_int(stmt, 0);
}
sqlite3_finalize(stmt);
resultCode = userId;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUserId", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::GetUser(int userId, std::string& code, std::string& userName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int resultCode = 0;
userName = "";
code = "";
int rc;
// Prepare
sql = "SELECT * FROM USER WHERE ID =?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_ROW) {
//this->_logger.LogDebug("FaceDatabase::GetUser.", "Get User information successfully", __FILE__, __LINE__);
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
code = codeText ? std::string(codeText) : "";
userName = nameText ? std::string(nameText) : "";
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUserId", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::GetFaceIdsByUser(int userId, std::vector<int>& faceIds)
{
std::lock_guard<std::mutex> lock(_mutex);
faceIds.clear();
std::string sql;
sqlite3_stmt* stmt;
int rc;
sql = "SELECT ID FROM FACE WHERE USER=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
int resultCode = 0;
int faceId;
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceIdsByUser", std::string("SQL error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceIdsByUser", std::string("SQL bind error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
faceId = sqlite3_column_int(stmt, 0);
faceIds.push_back(faceId);
}
if (rc != SQLITE_DONE) {
_logger.LogError("FaceDatabase::GetFaceIdsByUser", std::string("SQL step error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetFaceIdsByUser", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
int FaceDatabase::GetUsers(std::vector<int>& ids, std::vector<std::string>& codes, std::vector<std::string>& userNames) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int userId;
std::string userCode;
std::string userName;
int resutCode = 0;
ids.clear();
codes.clear();
userNames.clear();
sql = "SELECT * FROM USER;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUsers. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resutCode = -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
userId = sqlite3_column_int(stmt, 0);
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
userCode = codeText ? std::string(codeText) : "";
userName = nameText ? std::string(nameText) : "";
ids.push_back(userId);
codes.push_back(userCode);
userNames.push_back(userName);
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUsers. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resutCode = 1;
return resutCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUsers", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
int FaceDatabase::GetFaceById(int faceId, int& userId, std::string& imagePath) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
userId = -1;
imagePath = "";
// SELECT *FROM FACE WHERE ID =1
sql = "SELECT * FROM FACE WHERE ID =?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceById", std::string("SQL error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, faceId);
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceById", std::string("SQL bind error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
userId = sqlite3_column_int(stmt, 1);
const char* pathText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
imagePath = pathText ? std::string(pathText) : "";
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetFaceIds. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetFaceIds", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
std::map<std::string, std::string> FaceDatabase::GetUserDict() {
std::lock_guard<std::mutex> lock(_mutex);
std::map<std::string, std::string> userDict;
userDict.clear();
std::string sql;
sqlite3_stmt* stmt;
int rc;
std::string userId;
std::string userCode;
std::string userName;
// get user id and name
sql = "SELECT * FROM USER;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUserDict. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* idText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
userId = idText ? std::string(idText) : "";
userCode = codeText ? std::string(codeText) : "";
userName = nameText ? std::string(nameText) : "";
if (!userCode.empty()) {
userDict[userId] = userName+"(" + userCode + ")";
}
else {
userDict[userId] = userName;
}
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUserDict. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
return userDict;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUserDict", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return userDict;
}
}
int FaceDatabase::GetNumEmbeddings() {
std::lock_guard<std::mutex> lock(_mutex);
try {
int numEmbeds = 0;
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
// Prepare
sql = "SELECT COUNT(*) FROM FACE;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetNumEmbeddings. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetNumEmbeddings. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
numEmbeds = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
resultCode = numEmbeds;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetNumEmbeddings", e.what(), __FILE__, __LINE__);
return 0;
}
}
int FaceDatabase::GetEmbeddings(std::unique_ptr<ANSFRBase>& recognizer) {
try {
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
std::string userId;
int numEmbeds = GetNumEmbeddings();
_logger.LogDebug("FaceDatabase::GetEmbeddings", "There are " + std::to_string(numEmbeds) + " embeddings in database", __FILE__, __LINE__);
// get embedding
//_mutex.lock();
std::lock_guard<std::mutex> lock(_mutex);
sql = "SELECT * FROM FACE;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* userIdText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
if (!userIdText) continue;
userId = std::string(userIdText);
float* embedding = (float*)sqlite3_column_blob(stmt, 3);
if (!embedding) continue;
recognizer->AddEmbedding(userId, embedding);
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 0;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetEmbeddings", e.what(), __FILE__, __LINE__);
//_mutex.unlock();
return 0;
}
}
int FaceDatabase::GetEmbeddings(ANSFRBase& recognizer) {
try {
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
std::string userId;
int numEmbeds = GetNumEmbeddings();
_logger.LogDebug("FaceDatabase::GetEmbeddings", "There are " + std::to_string(numEmbeds) + " embeddings in database", __FILE__, __LINE__);
// get embedding
//_mutex.lock();
std::lock_guard<std::mutex> lock(_mutex);
sql = "SELECT * FROM FACE;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* userIdText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
if (!userIdText) continue;
userId = std::string(userIdText);
float* embedding = (float*)sqlite3_column_blob(stmt, 3);
if (!embedding) continue;
recognizer.AddEmbedding(userId, embedding);
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 0;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetEmbeddings", e.what(), __FILE__, __LINE__);
//_mutex.unlock();
return 0;
}
}
int FaceDatabase::GetAllEmbeddingRecords(std::vector<FaceEmbeddingRecord>& records) {
try {
std::lock_guard<std::mutex> lock(_mutex);
records.clear();
int numEmbeds = 0;
{
// Count for pre-allocation (inline, mutex already held)
sqlite3_stmt* countStmt;
int rc = sqlite3_prepare_v2(m_db, "SELECT COUNT(*) FROM FACE;", -1, &countStmt, NULL);
if (rc == SQLITE_OK && sqlite3_step(countStmt) == SQLITE_ROW) {
numEmbeds = sqlite3_column_int(countStmt, 0);
}
sqlite3_finalize(countStmt);
}
records.reserve(numEmbeds);
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(m_db,
"SELECT ID, USER, EMBEDDING FROM FACE;", -1, &stmt, NULL);
if (rc != SQLITE_OK) {
return -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int faceId = sqlite3_column_int(stmt, 0);
const char* userIdText = reinterpret_cast<const char*>(
sqlite3_column_text(stmt, 1));
if (!userIdText) continue;
const float* embData = reinterpret_cast<const float*>(
sqlite3_column_blob(stmt, 2));
int embBytes = sqlite3_column_bytes(stmt, 2);
int embSize = embBytes / static_cast<int>(sizeof(float));
if (!embData || embSize <= 0) continue;
FaceEmbeddingRecord record;
record.faceId = faceId;
record.userId = std::string(userIdText);
record.embedding.assign(embData, embData + embSize);
records.push_back(std::move(record));
}
sqlite3_finalize(stmt);
return 0;
}
catch (const std::exception& e) {
return -1;
}
}
int FaceDatabase::GetUsersWithFaceIds(std::vector<int>& ids, std::vector<std::string>& codes,
std::vector<std::string>& userNames, std::vector<std::vector<int>>& faceIdsByUser) {
// Fix #12: Single JOIN query replaces N+1 GetUser + GetFaceIdsByUser calls
std::lock_guard<std::mutex> lock(_mutex);
ids.clear();
codes.clear();
userNames.clear();
faceIdsByUser.clear();
try {
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(m_db,
"SELECT U.ID, U.CODE, U.NAME, F.ID AS FACE_ID "
"FROM USER U LEFT JOIN FACE F ON U.ID = F.USER "
"ORDER BY U.ID;",
-1, &stmt, NULL);
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetUsersWithFaceIds",
std::string("SQL error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
return -1;
}
int lastUserId = -1;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int userId = sqlite3_column_int(stmt, 0);
if (userId != lastUserId) {
// New user row
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
ids.push_back(userId);
codes.push_back(codeText ? std::string(codeText) : "");
userNames.push_back(nameText ? std::string(nameText) : "");
faceIdsByUser.emplace_back();
lastUserId = userId;
}
// Append face ID (may be NULL if user has no faces — LEFT JOIN)
if (sqlite3_column_type(stmt, 3) != SQLITE_NULL) {
faceIdsByUser.back().push_back(sqlite3_column_int(stmt, 3));
}
}
if (rc != SQLITE_DONE) {
_logger.LogError("FaceDatabase::GetUsersWithFaceIds",
std::string("SQL step error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
return 1;
}
catch (const std::exception& e) {
_logger.LogError("FaceDatabase::GetUsersWithFaceIds", e.what(), __FILE__, __LINE__);
return 0;
}
}
FaceDatabase::~FaceDatabase() {
try {
Destroy();
}
catch (std::exception& e) {
_logger.LogError("FaceDatabase::~FaceDatabase", e.what(), __FILE__, __LINE__);
}
}
bool FaceDatabase::Destroy() {
std::lock_guard<std::mutex> lock(_mutex);
try {
if (!m_db) return true;
int rc = sqlite3_close(m_db);
if (rc == SQLITE_OK) {
//this->_logger.LogDebug("Database::Destroy", "Success!", __FILE__, __LINE__);
m_db = nullptr;
return true;
}
else {
std::string errorCode = "Cannot close database connection.Error code:" + std::to_string(rc);
//this->_logger.LogError("Database::Destroy", errorCode, __FILE__, __LINE__);
return false;
}
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::Destroy", e.what(), __FILE__, __LINE__);
return false;
}
}
}

View File

@@ -0,0 +1,53 @@
#ifndef FACEDATABASE_H
#define FACEDATABASE_H
#pragma once
#include <iostream>
#include <map>
//#include "ARCFaceRT.h"
#include "sqlite3.h"
#include <vector>
#include "ANSFRCommon.h"
namespace ANSCENTER {
// Structured embedding record for double-buffer reload
struct FaceEmbeddingRecord {
int faceId; // FACE.ID (unique per face)
std::string userId; // FACE.USER (= USER.ID, as string)
std::vector<float> embedding;
};
class FaceDatabase {
public:
FaceDatabase();
~FaceDatabase();
bool Destroy();
bool Initialize(std::string path, int embeddingDim, std::string dataFolder);
int InsertUser(std::string code, std::string userName);
int UpdateUser(int UserId, std::string newUserCode, std::string newUserName);
int InsertFace(int userId, std::string imgPath, float embedding[]);
int DeleteFace(int id);
int DeleteUser(int UserId);
int GetFaceIdsByUser(int userId, std::vector<int>& faceIds);
int GetUserId(std::string code);
int GetUser(int userId, std::string& code, std::string& userName);
int GetUsers(std::vector<int>& ids, std::vector<std::string>& codes, std::vector<std::string>& userNames);
// Fix #12: Batch query — returns users with their face IDs in a single query
int GetUsersWithFaceIds(std::vector<int>& ids, std::vector<std::string>& codes,
std::vector<std::string>& userNames, std::vector<std::vector<int>>& faceIdsByUser);
int GetFaceById(int faceId, int& userId, std::string& imagePath);
std::map<std::string, std::string> GetUserDict();
int GetEmbeddings(ANSFRBase& recognizer);
int GetEmbeddings(std::unique_ptr<ANSFRBase>& recognizer);
// Returns all face embedding records (FACE.ID + USER.ID + embedding) for double-buffer reload
int GetAllEmbeddingRecords(std::vector<FaceEmbeddingRecord>& records);
private:
sqlite3* m_db;
int m_embedding_dim;
std::string _dataFolder;
std::mutex _mutex;
// Fix #10: Logger for database operations
ANSCENTER::SPDLogger& _logger = ANSCENTER::SPDLogger::GetInstance("FaceDB");
int GetNumEmbeddings();
};
}
#endif

412
modules/ANSFR/FaceNet.cpp Normal file
View File

@@ -0,0 +1,412 @@
#include "FaceNet.h"
namespace ANSCENTER {
std::string ANSFaceNet::GetOpenVINODevice() {
ov::Core core;
std::vector<std::string> available_devices = core.get_available_devices();
// Prioritize devices: NPU > GPU > CPU
std::vector<std::string> priority_devices = {"GPU", "CPU" };
for (const auto& device : priority_devices) {
if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) {
return device; // Return the first available device based on priority
}
}
// Default to CPU if no prioritized devices are found
return "CPU";
}
bool ANSFaceNet::Initialize(std::string licenseKey,ModelConfig modelConfig,
const std::string& modelZipFilePath,const std::string& modelZipPassword,std::string& labelMap)
{
bool result = ANSFRBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
if (!result) return false;
try {
_modelConfig = modelConfig;
_modelConfig.modelType = ModelType::FACERECOGNIZE;
_modelConfig.detectionType = DetectionType::FACERECOGNIZER;
// We need to get the modelfolder from here
std::string faceidModel = CreateFilePath(_modelFolder, "ansfacenet.xml");
if (std::filesystem::exists(faceidModel)) {
_modelFilePath = faceidModel;
this->_logger.LogDebug("ANSFaceNet::Initialize. Loading ansfacenet weight", _modelFilePath, __FILE__, __LINE__);
}
else {
this->_logger.LogError("ANSFaceNet::Initialize. Model ansfacenet.xml file is not exist", _modelFilePath, __FILE__, __LINE__);
return false;
}
m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
if (m_knownPersonThresh == 0)m_knownPersonThresh = 0.35;
std::string deviceName = GetOpenVINODevice();
ov::Core core;
CnnConfig reid_config(_modelFilePath, "Face Re-Identification");
reid_config.m_deviceName = deviceName;
//core.set_property(deviceName, ov::hint::performance_mode(ov::hint::PerformanceMode::THROUGHPUT));
reid_config.m_max_batch_size = 1;
reid_config.m_core = core;
this->faceRecognizer = std::make_unique<VectorCNN>(reid_config);
if (faceRecognizer == nullptr) {
this->_logger.LogFatal("ANSFaceNet::Initialize", "Failed to initialize face recognizer model", __FILE__, __LINE__);
return false;
}
Init();
_isInitialized = true;
return true;
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSFaceNet::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
try {
// We need to get the _modelFolder
bool result = ANSFRBase::LoadModel(modelZipFilePath, modelZipPassword);
if (!result) return false;
// We need to get the modelfolder from here
std::string faceidModel = CreateFilePath(_modelFolder, "ansfacenet.xml");
if (std::filesystem::exists(faceidModel)) {
_modelFilePath = faceidModel;
this->_logger.LogDebug("ANSFaceNet::LoadModel. Loading ansfacenet weight", _modelFilePath, __FILE__, __LINE__);
}
else {
this->_logger.LogError("ANSFaceNet::Initialize. Model ansfacenet.xml file is not exist", _modelFilePath, __FILE__, __LINE__);
return false;
}
return true;
}
catch (std::exception& e) {
this->_logger.LogFatal("ArcFace50::LoadModel", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSFaceNet::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
if (!FileExist(_modelFilePath)) {
optimizedModelFolder = "";
return false;
}
return true;
}
std::vector<float> ANSFaceNet::Feature(const cv::Mat& image, const ANSCENTER::Object& bBox) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<float> embeddingResult;
try {
if (image.empty()) return embeddingResult;
if ((image.cols < 10) || (image.rows < 10)) return embeddingResult;
std::vector<cv::Mat> embeddings;
std::vector<cv::Mat> face_rois;
face_rois.clear();
embeddings.clear();
face_rois.push_back(bBox.mask);
faceRecognizer->Compute(face_rois, &embeddings);
embeddingResult.assign(embeddings[0].begin<float>(), embeddings[0].end<float>());
return embeddingResult;
}
catch (std::exception& e) {
std::vector<float> embeddingResult;
embeddingResult.clear();
this->_logger.LogFatal("ANSFaceNet::Feature", e.what(), __FILE__, __LINE__);
return embeddingResult;
}
}
std::vector<FaceResultObject> ANSFaceNet::Match(
const cv::Mat& input,
const std::vector<ANSCENTER::Object>& bBox,
const std::map<std::string, std::string>& userDict) {
std::vector<FaceResultObject> resultObjects;
// Early validation before locking
if (input.empty()) {
return resultObjects;
}
if (input.cols < 10 || input.rows < 10) {
return resultObjects;
}
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (!_isInitialized) {
_logger.LogError("ANSFaceNet::Match", "Model is not initialized", __FILE__, __LINE__);
return resultObjects;
}
try {
// Convert grayscale to 3-channel BGR if needed
cv::Mat processedImage;
if (input.channels() == 1) {
cv::cvtColor(input, processedImage, cv::COLOR_GRAY2BGR);
}
else {
processedImage = input;
}
// Get embeddings
std::vector<std::vector<float>> detectedEmbeddings = Forward(processedImage, bBox);
// Search for matches
std::vector<std::string> names;
std::vector<float> sims;
std::tie(names, sims) = SearchForFaces(detectedEmbeddings);
// Check if we got results
if (names.empty()) {
_logger.LogError("ANSFaceNet::Match", "No face is match", __FILE__, __LINE__);
return resultObjects;
}
// Pre-reserve result space
const size_t resultCount = std::min(names.size(), bBox.size());
resultObjects.reserve(resultCount);
// Build result objects
for (size_t i = 0; i < resultCount; ++i) {
FaceResultObject resultObject;
// Determine if face is known or unknown
const bool isUnknown = (sims[i] > m_knownPersonThresh);
if (isUnknown) {
resultObject.isUnknown = true;
resultObject.userId = "0";
resultObject.userName = "Unknown";
resultObject.confidence = 1.0f; // 100% unknown
}
else {
resultObject.isUnknown = false;
resultObject.userId = names[i];
// Safe map lookup
auto it = userDict.find(names[i]);
resultObject.userName = (it != userDict.end()) ? it->second : names[i];
resultObject.confidence = 1.0f - sims[i];
}
resultObject.similarity = sims[i];
// Copy bounding box (no clamping - keeping original logic)
resultObject.box.x = bBox[i].box.x;
resultObject.box.y = bBox[i].box.y;
resultObject.box.width = bBox[i].box.width;
resultObject.box.height = bBox[i].box.height;
// Copy additional data
resultObject.mask = bBox[i].mask;
resultObject.cameraId = bBox[i].cameraId;
resultObject.trackId = bBox[i].trackId;
resultObject.polygon = bBox[i].polygon;
resultObject.kps = bBox[i].kps;
resultObject.extraInformation = bBox[i].extraInfo;
resultObjects.push_back(std::move(resultObject));
}
return resultObjects;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSFaceNet::Match", e.what(), __FILE__, __LINE__);
return resultObjects;
}
}
cv::Mat ANSFaceNet::GetCropFace(const cv::Mat& input, const ANSCENTER::Object& bBox) {
try {
cv::Mat processedImage;
if (input.channels() == 1) {
cv::cvtColor(input, processedImage, cv::COLOR_GRAY2BGR);
}
else {
processedImage = input;
}
std::vector<ANSCENTER::Object> outputBbox;
outputBbox.push_back(bBox);
std::vector<struct CroppedFace> crFaces;
crFaces.clear();
ANSFRHelper::GetCroppedFaces(processedImage, outputBbox, FACE_WIDTH, FACE_HEIGHT, crFaces);
return crFaces[0].faceMat;
}
catch (std::exception& e) {
cv::Mat result;
this->_logger.LogFatal("ANSFaceNet::GetCropFace", e.what(), __FILE__, __LINE__);
return result;
}
}
bool ANSFaceNet::LoadEngine(const std::string xmlModelPath, bool engineOptimisation) {
try {
if (!FileExist(xmlModelPath)) {
this->_logger.LogError("ANSFaceNet::LoadEngine", "Cannot find the raw XML model file.", __FILE__, __LINE__);
return false;
}
return true;
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::LoadEngine", e.what(), __FILE__, __LINE__);
return false;
}
}
// Private methods, can be replacable
void ANSFaceNet::AddEmbedding(const std::string& className, float embedding[]) {
//std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Check if faiss_index is initialized
if (!faiss_index) {
this->_logger.LogFatal("ANSFaceNet::AddEmbedding", "Search_index is not initialized.", __FILE__, __LINE__);
return;
}
// Convert embedding array to vector and check size
std::vector<float> vec(embedding, embedding + FACE_EMBEDDING_SIZE);
if (vec.size() != FACE_EMBEDDING_SIZE) {
this->_logger.LogFatal("ANSFaceNet::AddEmbedding", "Embedding size does not match expected output dimension of 512.", __FILE__, __LINE__);
return;
}
// Add class name and get index
classNames.push_back(className);
// Add embedding to faiss_index
faiss_index->add(1, vec.data());
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::AddEmbedding", e.what(), __FILE__, __LINE__);
}
}
void ANSFaceNet::AddEmbedding(const std::string& className, const std::vector<float>& embedding) {
//std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Ensure faiss_index is initialized
if (!faiss_index) {
this->_logger.LogFatal("ANSFaceNet::AddEmbedding", "Search_index is not initialized.", __FILE__, __LINE__);
return;
}
// Check embedding size
if (embedding.size() != FACE_EMBEDDING_SIZE) {
this->_logger.LogFatal("ANSFaceNet::AddEmbedding", "Embedding size does not match expected output dimension of 512.", __FILE__, __LINE__);
return;
}
// Add class name
classNames.push_back(className);
// Add embedding to faiss_index
faiss_index->add(1, embedding.data());
}
catch (const std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::AddEmbedding", e.what(), __FILE__, __LINE__);
}
}
std::vector<std::vector<float>> ANSFaceNet::Forward(const cv::Mat& input, std::vector<ANSCENTER::Object> outputBbox) {
//std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<std::vector<float>> detectedEmbeddings;
try {
std::vector<cv::Mat> embeddings;
std::vector<cv::Mat> face_rois;
detectedEmbeddings.clear();
if (outputBbox.size() > 0) {
for (int i = 0; i < outputBbox.size(); i++) {
face_rois.clear();
embeddings.clear();
std::vector<float> embeddingRs;
face_rois.push_back(outputBbox[i].mask);
faceRecognizer->Compute(face_rois, &embeddings);
embeddingRs.assign(embeddings[0].begin<float>(), embeddings[0].end<float>());
detectedEmbeddings.push_back(embeddingRs);
}
}
return detectedEmbeddings;
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::Forward", e.what(), __FILE__, __LINE__);
return detectedEmbeddings;
}
}
std::tuple<std::vector<std::string>, std::vector<float>> ANSFaceNet::SearchForFaces(const std::vector<std::vector<float>>& detectedEmbeddings)
{
//std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<std::string> detectedUsers;
std::vector<float> simValues;
try {
// Check if there are class names available
if (!classNames.empty()) {
const int k = 3; // Number of nearest neighbors to retrieve
// Process each detected embedding
for (const auto& embedding : detectedEmbeddings) {
// Prepare vectors to hold search results
std::vector<faiss::idx_t> indices(k);
std::vector<float> distances(k);
// Perform the search for the k nearest neighbors
faiss_index->search(1, embedding.data(), k, distances.data(), indices.data());
// Find the index with the maximum distance
auto min_it = std::min_element(distances.begin(), distances.end());
int best_index = std::distance(distances.begin(), min_it);
// Map the index to the corresponding class name and calculate similarity
std::vector<float> matchEmbedding(faiss_index->d);
faiss_index->reconstruct(indices[best_index], matchEmbedding.data());
float similarity = 1 - CosineSimilarity(embedding, matchEmbedding, false);
detectedUsers.push_back(classNames.at(indices[best_index]));
simValues.push_back(std::abs(similarity));
}
}
else {
// If no class names are available, mark all users as "unknown"
detectedUsers.assign(detectedEmbeddings.size(), "0");
simValues.assign(detectedEmbeddings.size(), 1.0f);
}
return std::make_tuple(detectedUsers, simValues);
}
catch (const std::exception& e) {
// Log the error and return default values for the failed search
detectedUsers.assign(1, "0");
simValues.assign(1, 1.0f);
this->_logger.LogFatal("ANSFaceNet::SearchForFaces", e.what(), __FILE__, __LINE__);
return std::make_tuple(detectedUsers, simValues);
}
}
void ANSFaceNet::Init() {
try {
classNames.clear();
if (faiss_index) {
faiss_index->reset();
}
else {
faiss_index = std::make_unique<faiss::IndexFlatL2>(FACE_EMBEDDING_SIZE);
}
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::Init", e.what(), __FILE__, __LINE__);
}
}
ANSFaceNet::~ANSFaceNet() {
try {
Destroy();
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::~ANSFaceNet()", e.what(), __FILE__, __LINE__);
}
}
bool ANSFaceNet::Destroy() {
try {
// Clear all resources safely
classNames.clear();
// Reset and release faiss_index
if (faiss_index) {
faiss_index->reset();
faiss_index.reset(); // Release shared pointer ownership
}
faceRecognizer.reset();
return true;
}
catch (std::exception& e) {
this->_logger.LogFatal("ANSFaceNet::Destroy", e.what(), __FILE__, __LINE__);
return false;
}
}
}

49
modules/ANSFR/FaceNet.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef FACENET_H
#define FACENET_H
#pragma once
#include "ANSFRCommon.h"
#include "hnswlib/hnswlib.h"
#include "cnn.hpp"
#include "face_reid.hpp"
#include "openvino/openvino.hpp"
#include <faiss/IndexFlat.h>
namespace ANSCENTER {
class ANSFaceNet :public ANSFRBase {
public:
virtual bool Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) override;
virtual bool LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword)override;
bool OptimizeModel(bool fp16, std::string& optimizedModelFolder);
std::vector<float> Feature(const cv::Mat& image, const ANSCENTER::Object& bBox); // Run inference and get embedding information from a cropped image
std::vector<FaceResultObject> Match(const cv::Mat& input, const std::vector<ANSCENTER::Object>& bBox, const std::map<std::string, std::string>& userDict); // Run inference and get embedding information from a cropped image (the first bbox)
cv::Mat GetCropFace(const cv::Mat& input, const ANSCENTER::Object& bBox);
void Init();
void AddEmbedding(const std::string& className, float embedding[]);
void AddEmbedding(const std::string& className, const std::vector<float>& embedding);
bool UpdateParamater(double knownPersonThreshold) {
_modelConfig.unknownPersonThreshold = knownPersonThreshold;
m_knownPersonThresh = _modelConfig.unknownPersonThreshold;
return true;
}
~ANSFaceNet();
bool Destroy();
private:
bool LoadEngine(const std::string engineFile, bool engineOptimisation = true);
std::vector<std::vector<float>> Forward(const cv::Mat& input, std::vector<ANSCENTER::Object> outputBbox);
std::tuple<std::vector<std::string>, std::vector<float>> SearchForFaces(const std::vector<std::vector<float>>& detectedEmbeddings);
std::string GetOpenVINODevice();
protected:
std::vector<std::string> classNames;
ModelConfig _modelConfig;
std::string _modelFilePath;
ov::Core core;
float m_knownPersonThresh;
const int FACE_WIDTH = 116;
const int FACE_HEIGHT = 116;
const int FACE_EMBEDDING_SIZE = 512;
std::unique_ptr<VectorCNN>faceRecognizer = nullptr;
std::unique_ptr<faiss::IndexFlatL2> faiss_index; // Use shared_ptr
std::recursive_mutex _mutex;
};
}
#endif

View File

@@ -0,0 +1,366 @@
#include "RetinaFaceTRT.h"
// This is standalone Retina Face detector using TensorRT (We will not use as it is not inherit the ANSFD class)
namespace ANSCENTER {
RetinaFaceTRT::RetinaFaceTRT()
{
m_outputBbox.clear();
}
bool RetinaFaceTRT::Initialize(const std::string engineFile,
int frameWidth,
int frameHeight,
std::string inputName,
std::vector<std::string> outputNames,
std::vector<int> inputShape,
int maxBatchSize,
int maxFacesPerScene,
float nms_threshold,
float bbox_threshold)
{
try
{
assert(inputShape.size() == 3);
m_INPUT_C = static_cast<const int>(inputShape[0]);
m_INPUT_H = static_cast<const int>(inputShape[1]);
m_INPUT_W = static_cast<const int>(inputShape[2]);
m_INPUT_SIZE = static_cast<const int>(m_INPUT_C * m_INPUT_H * m_INPUT_W * sizeof(float));
m_OUTPUT_SIZE_BASE = static_cast<const int>((m_INPUT_H / 8 * m_INPUT_W / 8 + m_INPUT_H / 16 * m_INPUT_W / 16 + m_INPUT_H / 32 * m_INPUT_W / 32) * 2);
m_output0 = new float[m_OUTPUT_SIZE_BASE * 4];
m_output1 = new float[m_OUTPUT_SIZE_BASE * 2];
m_maxBatchSize = static_cast<const int>(maxBatchSize);
m_maxFacesPerScene = static_cast<const int>(maxFacesPerScene);
m_nms_threshold = static_cast<const float>(nms_threshold);
m_bbox_threshold = static_cast<const float>(bbox_threshold);
// load engine from .engine file
LoadEngine(engineFile);
// create stream and pre-allocate GPU buffers memory
PreInference(inputName, outputNames);
return true;
}
catch (std::exception& e) {
this->_logger.LogFatal("RetinaFace::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
void RetinaFaceTRT::LoadEngine(const std::string engineFile)
{
try {
if (FileExist(engineFile)) {
this->_logger.LogDebug("RetinaFace::LoadEngine", "Loading RetinaFace Engine...", __FILE__, __LINE__);
std::vector<char> trtModelStream_;
size_t size{ 0 };
std::ifstream file(engineFile, std::ios::binary);
if (file.good()) {
file.seekg(0, file.end);
size = file.tellg();
file.seekg(0, file.beg);
trtModelStream_.resize(size);
file.read(trtModelStream_.data(), size);
file.close();
}
nvinfer1::IRuntime* runtime = nvinfer1::createInferRuntime(m_logger);
assert(runtime != nullptr);
m_engine = runtime->deserializeCudaEngine(trtModelStream_.data(), size);
assert(m_engine != nullptr);
m_context = m_engine->createExecutionContext();
assert(m_context != nullptr);
}
else {
this->_logger.LogError("RetinaFace::LoadEngine", "Cant find engine file", __FILE__, __LINE__);
}
}
catch (std::exception& e) {
this->_logger.LogFatal("RetinaFace::Initialize", e.what(), __FILE__, __LINE__);
}
}
void RetinaFaceTRT::PreInference(std::string inputName, std::vector<std::string> outputNames) {
try {
/*
Does not make landmark head as we do not use face alignment.
*/
// Assert
assert(outputNames.size() == 2);
#if NV_TENSORRT_MAJOR >= 10
// TRT 10+: use named tensor API
assert(m_engine->getNbIOTensors() == 3);
// Look up tensor indices by name
for (int i = 0; i < m_engine->getNbIOTensors(); ++i) {
const char* name = m_engine->getIOTensorName(i);
if (name == inputName) inputIndex = i;
else if (name == outputNames[0]) outputIndex0 = i;
else if (name == outputNames[1]) outputIndex1 = i;
}
#else
// TRT 8.x: use binding API
assert(m_engine->getNbBindings() == 3);
inputIndex = m_engine->getBindingIndex(inputName.c_str());
outputIndex0 = m_engine->getBindingIndex(outputNames[0].c_str());
outputIndex1 = m_engine->getBindingIndex(outputNames[1].c_str());
#endif
// Create GPU buffers on device
ANSFRHelper::CheckCudaStatus(cudaMalloc(&buffers[inputIndex], m_maxBatchSize * m_INPUT_SIZE));
ANSFRHelper::CheckCudaStatus(cudaMalloc(&buffers[outputIndex0], m_maxBatchSize * m_OUTPUT_SIZE_BASE * 4 * sizeof(float)));
ANSFRHelper::CheckCudaStatus(cudaMalloc(&buffers[outputIndex1], m_maxBatchSize * m_OUTPUT_SIZE_BASE * 2 * sizeof(float)));
#if NV_TENSORRT_MAJOR >= 10
// TRT 10+: bind tensor addresses
m_context->setTensorAddress(inputName.c_str(), buffers[inputIndex]);
m_context->setTensorAddress(outputNames[0].c_str(), buffers[outputIndex0]);
m_context->setTensorAddress(outputNames[1].c_str(), buffers[outputIndex1]);
#endif
// Create stream
ANSFRHelper::CheckCudaStatus(cudaStreamCreate(&stream));
}
catch (std::exception& e) {
this->_logger.LogFatal("RetinaFace::PreInference", e.what(), __FILE__, __LINE__);
}
}
void RetinaFaceTRT::PreProcess(cv::Mat& img) {
try {
// Release input vector
m_input.release();
// Resize
float w, h, x, y;
if (m_scale_h > m_scale_w) {
w = float(m_INPUT_W);
h = float(m_scale_w * img.rows);
x = 0;
y = float((m_INPUT_H - h) / 2);
}
else {
w = float(m_scale_h * img.cols);
h = float(m_INPUT_H);
x = float((m_INPUT_W - w) / 2);
y = 0;
}
cv::Mat re((int)h, (int)w, CV_8UC3);
cv::resize(img, re, re.size(), 0, 0, cv::INTER_LINEAR);
cv::Mat out((int)m_INPUT_H, (int)m_INPUT_W, CV_8UC3, cv::Scalar(128, 128, 128));
re.copyTo(out(cv::Rect(int(x), int(y), re.cols, re.rows)));
// Normalize
out.convertTo(out, CV_32F);
out = out - cv::Scalar(104, 117, 123);
std::vector<cv::Mat> temp;
cv::split(out, temp);
for (int i = 0; i < temp.size(); i++) {
m_input.push_back(temp[i]);
}
}
catch (std::exception& e) {
this->_logger.LogFatal("RetinaFace::PreProcess", e.what(), __FILE__, __LINE__);
}
}
void RetinaFaceTRT::RunInference(float* input, float* output0, float* output1) {
try {
// DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
ANSCENTER::ANSFRHelper::CheckCudaStatus(cudaMemcpyAsync(buffers[inputIndex], input, m_maxBatchSize * m_INPUT_SIZE, cudaMemcpyHostToDevice, stream));
#if NV_TENSORRT_MAJOR >= 10
m_context->enqueueV3(stream);
#else
m_context->enqueueV2(buffers, stream, nullptr);
#endif
ANSFRHelper::CheckCudaStatus(cudaMemcpyAsync(output0, buffers[outputIndex0], m_maxBatchSize * m_OUTPUT_SIZE_BASE * 4 * sizeof(float), cudaMemcpyDeviceToHost, stream));
ANSFRHelper::CheckCudaStatus(cudaMemcpyAsync(output1, buffers[outputIndex1], m_maxBatchSize * m_OUTPUT_SIZE_BASE * 2 * sizeof(float), cudaMemcpyDeviceToHost, stream));
cudaStreamSynchronize(stream);
}
catch (std::exception& e) {
this->_logger.LogFatal("RetinaFace::RunInference", e.what(), __FILE__, __LINE__);
}
}
std::vector<struct Bbox> RetinaFaceTRT::FindFace(cv::Mat& img) {
try {
m_outputBbox.clear();
int out_rows = img.rows;
int out_cols = img.cols;
m_frameWidth = static_cast<const int>(out_cols);
m_frameHeight = static_cast<const int>(out_rows);
m_scale_h = (float)(m_INPUT_H) / float(m_frameHeight);
m_scale_w = (float)(m_INPUT_W) / float(m_frameWidth);
PreProcess(img);
RunInference((float*)m_input.ptr<float>(0), m_output0, m_output1);
PostProcessing(m_output0, m_output1);
return m_outputBbox;
}
catch (std::exception& e) {
m_outputBbox.clear();
this->_logger.LogFatal("RetinaFace::FindFace", e.what(), __FILE__, __LINE__);
return m_outputBbox;
}
}
void RetinaFaceTRT::PostProcessing(float* bbox, float* conf) {
try {
m_outputBbox.clear();
std::vector<AnchorBox> anchor;
CreateAnchorRetinaface(anchor, m_INPUT_W, m_INPUT_H);
for (int i = 0; i < anchor.size(); ++i) {
if (*(conf + 1) > m_bbox_threshold) {
AnchorBox tmp = anchor[i];
AnchorBox tmp1;
Bbox result{};
// decode bbox (y - W; x - H)
tmp1.cx = float(tmp.cx + *bbox * 0.1 * tmp.sx);
tmp1.cy = float(tmp.cy + *(bbox + 1) * 0.1 * tmp.sy);
tmp1.sx = float(tmp.sx * exp(*(bbox + 2) * 0.2));
tmp1.sy = float(tmp.sy * exp(*(bbox + 3) * 0.2));
result.y1 = int((tmp1.cx - tmp1.sx / 2) * m_INPUT_W);
result.x1 = int((tmp1.cy - tmp1.sy / 2) * m_INPUT_H);
result.y2 = int((tmp1.cx + tmp1.sx / 2) * m_INPUT_W);
result.x2 = int((tmp1.cy + tmp1.sy / 2) * m_INPUT_H);
// rescale to original size
if (m_scale_h > m_scale_w) {
result.y1 = int(result.y1 / m_scale_w);
result.y2 = int(result.y2 / m_scale_w);
result.x1 = int((result.x1 - (m_INPUT_H - m_scale_w * m_frameHeight) / 2) / m_scale_w);
result.x2 = int((result.x2 - (m_INPUT_H - m_scale_w * m_frameHeight) / 2) / m_scale_w);
}
else {
result.y1 = int((result.y1 - (m_INPUT_W - m_scale_h * m_frameWidth) / 2) / m_scale_h);
result.y2 = int((result.y2 - (m_INPUT_W - m_scale_h * m_frameWidth) / 2) / m_scale_h);
result.x1 = int(result.x1 / m_scale_h);
result.x2 = int(result.x2 / m_scale_h);
}
// Clip object box coordinates to network resolution
result.y1 = CLIP(result.y1, 0, m_frameWidth - 1);
result.x1 = CLIP(result.x1, 0, m_frameHeight - 1);
result.y2 = CLIP(result.y2, 0, m_frameWidth - 1);
result.x2 = CLIP(result.x2, 0, m_frameHeight - 1);
// Get confidence
result.score = *(conf + 1);
// Push to result vector
m_outputBbox.push_back(result);
}
bbox += 4;
conf += 2;
}
std::sort(m_outputBbox.begin(), m_outputBbox.end(), MCMP);
NMS(m_outputBbox, m_nms_threshold);
if (m_outputBbox.size() > m_maxFacesPerScene)
m_outputBbox.resize(m_maxFacesPerScene);
}
catch (std::exception& e) {
m_outputBbox.clear();
this->_logger.LogFatal("RetinaFace::PostProcessing", e.what(), __FILE__, __LINE__);
}
}
void RetinaFaceTRT::CreateAnchorRetinaface(std::vector<AnchorBox>& anchor, int w, int h) {
try {
anchor.clear();
std::vector<std::vector<int>> feature_map(3), min_sizes(3);
float steps[] = { 8, 16, 32 };
for (int i = 0; i < feature_map.size(); ++i) {
feature_map[i].push_back(int(ceil(h / steps[i])));
feature_map[i].push_back(int(ceil(w / steps[i])));
}
std::vector<int> minsize1 = { 10, 20 };
min_sizes[0] = minsize1;
std::vector<int> minsize2 = { 32, 64 };
min_sizes[1] = minsize2;
std::vector<int> minsize3 = { 128, 256 };
min_sizes[2] = minsize3;
for (int k = 0; k < feature_map.size(); ++k) {
std::vector<int> min_size = min_sizes[k];
for (int i = 0; i < feature_map[k][0]; ++i) {
for (int j = 0; j < feature_map[k][1]; ++j) {
for (int l = 0; l < min_size.size(); ++l) {
float s_kx = float(min_size[l] * 1.0 / w);
float s_ky = float(min_size[l] * 1.0 / h);
float cx = float((j + 0.5) * steps[k] / w);
float cy = float((i + 0.5) * steps[k] / h);
AnchorBox axil = { cx, cy, s_kx, s_ky };
anchor.push_back(axil);
}
}
}
}
}
catch (std::exception& e) {
m_outputBbox.clear();
this->_logger.LogFatal("RetinaFace::CreateAnchorRetinaface", e.what(), __FILE__, __LINE__);
}
}
inline bool RetinaFaceTRT::MCMP(Bbox a, Bbox b) {
if (a.score > b.score)
return true;
return false;
}
void RetinaFaceTRT::NMS(std::vector<Bbox>& input_boxes, float NMS_THRESH) {
try {
std::vector<float> vArea(input_boxes.size());
for (int i = 0; i < int(input_boxes.size()); ++i) {
vArea[i] = float((input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1) * (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1));
}
for (int i = 0; i < int(input_boxes.size()); ++i) {
for (int j = i + 1; j < int(input_boxes.size());) {
float xx1 = (float)(std::max(input_boxes[i].x1, input_boxes[j].x1));
float yy1 = (float)(std::max(input_boxes[i].y1, input_boxes[j].y1));
float xx2 = (float)(std::min(input_boxes[i].x2, input_boxes[j].x2));
float yy2 = (float)(std::min(input_boxes[i].y2, input_boxes[j].y2));
float w = (float)(std::max(float(0), xx2 - xx1 + 1));
float h = (float)(std::max(float(0), yy2 - yy1 + 1));
float inter = (float)(w * h);
float ovr = (float)(inter / (vArea[i] + vArea[j] - inter));
if (ovr >= NMS_THRESH) {
input_boxes.erase(input_boxes.begin() + j);
vArea.erase(vArea.begin() + j);
}
else {
j++;
}
}
}
}
catch (std::exception& e) {
m_outputBbox.clear();
this->_logger.LogFatal("RetinaFace::NMS", e.what(), __FILE__, __LINE__);
}
}
RetinaFaceTRT::~RetinaFaceTRT() {
try {
// Release stream and buffers
ANSFRHelper::CheckCudaStatus(cudaStreamDestroy(stream));
ANSFRHelper::CheckCudaStatus(cudaFree(buffers[inputIndex]));
ANSFRHelper::CheckCudaStatus(cudaFree(buffers[outputIndex0]));
ANSFRHelper::CheckCudaStatus(cudaFree(buffers[outputIndex1]));
// checkCudaStatus(cudaFree(buffers[outputIndex2]));
}
catch (std::exception& e) {
m_outputBbox.clear();
this->_logger.LogFatal("RetinaFace::~RetinaFace", e.what(), __FILE__, __LINE__);
}
}
}

View File

@@ -0,0 +1,51 @@
#ifndef RETINAFACE_H
#define RETINAFACE_H
#pragma once
#include "ANSFRCommon.h"
#define CLIP(a, b, c) (MAX(MIN(a, c), b)) // MIN, MAX defined in opencv
namespace ANSCENTER {
// Retina will be FD subclass
class RetinaFaceTRT {
public:
RetinaFaceTRT();
~RetinaFaceTRT();
std::vector<struct Bbox> FindFace(cv::Mat& img);
bool Initialize(const std::string engineFile,
int frameWidth,
int frameHeight,
std::string inputName,
std::vector<std::string> outputNames,
std::vector<int> inputShape,
int maxBatchSize,
int maxFacesPerScene,
float nms_threshold,
float bbox_threshold);
private:
ANSCENTER::SPDLogger& _logger = ANSCENTER::SPDLogger::GetInstance("ANSFD", true);
TRTLogger m_logger;
int m_frameWidth, m_frameHeight, m_INPUT_C, m_INPUT_H, m_INPUT_W, m_INPUT_SIZE, m_OUTPUT_SIZE_BASE, m_maxBatchSize, m_maxFacesPerScene;
float m_nms_threshold, m_bbox_threshold, m_scale_h, m_scale_w;
cv::Mat m_input;
float* m_output0, * m_output1;
std::vector<struct Bbox> m_outputBbox;
nvinfer1::ICudaEngine* m_engine;
nvinfer1::IExecutionContext* m_context;
cudaStream_t stream;
void* buffers[3];
int inputIndex, outputIndex0, outputIndex1;
private:
void LoadEngine(const std::string engineFile);
void PreInference(std::string inputNames, std::vector<std::string> outputNames);
void RunInference(float* input, float* output0, float* output1);
void PreProcess(cv::Mat& img);
void PostProcessing(float* bbox, float* conf);
void CreateAnchorRetinaface(std::vector<AnchorBox>& anchor, int w, int h);
static inline bool MCMP(Bbox a, Bbox b);
void NMS(std::vector<Bbox>& input_boxes, float NMS_THRESH);
};
}
#endif

View File

@@ -0,0 +1,84 @@
#include"WebClient.h"
namespace ANSCENTER {
#include "webclient.h"
WebSocketClient::WebSocketClient(std::string host, std::string port, std::string url) {
// Look up the domain name
tcp::resolver resolver{ m_ioc };
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
auto ep = net::connect(m_ws.next_layer(), results);
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// See https://tools.ietf.org/html/rfc7230#section-5.4
host += ':' + std::to_string(ep.port());
// Perform the websocket handshake
m_ws.handshake(host, url);
}
std::string WebSocketClient::Send(std::string s) {
// Clear read buffer
m_buffer.clear();
// Send the message
m_ws.write(net::buffer(s));
// Read a message into our buffer
m_ws.read(m_buffer);
return beast::buffers_to_string(m_buffer.data());
}
WebSocketClient::~WebSocketClient() {
// Close the WebSocket connection
m_ws.close(websocket::close_code::normal);
}
HttpClient::HttpClient(std::string host, std::string port, std::string url) {
// Look up the domain name
tcp::resolver resolver{ m_ioc };
m_results = resolver.resolve(host, port);
// Set up an HTTP POST request message
m_req.method(beast::http::verb::post);
m_req.target(url);
m_req.set(http::field::host, host);
m_req.set(http::field::content_type, "application/json");
}
std::string HttpClient::Send(std::string s) {
// Make the connection on the IP address we get from a lookup
m_stream.connect(m_results);
// Clear read buffer
// m_buffer.clear();
// Clear response
m_res = {};
// Prepare response
m_req.body() = s;
m_req.prepare_payload();
// Send the HTTP request to the remote host
http::write(m_stream, m_req);
// Receive the HTTP response
http::read(m_stream, m_buffer, m_res);
// Gracefully close the socket
beast::error_code ec;
m_stream.socket().shutdown(tcp::socket::shutdown_both, ec);
// not_connected happens sometimes, so don't bother reporting it.
if (ec && ec != beast::errc::not_connected)
throw beast::system_error{ ec };
// If we get here then the connection is closed gracefully
return beast::buffers_to_string(m_res.body().data());
}
HttpClient::~HttpClient() {}
}

45
modules/ANSFR/WebClient.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef WEBCLIENT_H
#define WEBCLIENT_H
#pragma once
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/websocket.hpp>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace ANSCENTER {
class WebSocketClient {
public:
WebSocketClient(std::string host, std::string port, std::string url);
~WebSocketClient();
std::string Send(std::string s);
private:
net::io_context m_ioc; // The io_context is required for all I/O
websocket::stream<tcp::socket> m_ws{ m_ioc }; // perform our I/O
beast::flat_buffer m_buffer;
};
class HttpClient {
public:
HttpClient(std::string host, std::string port, std::string url);
~HttpClient();
std::string Send(std::string s);
private:
net::io_context m_ioc; // The io_context is required for all I/O
beast::tcp_stream m_stream{ m_ioc }; // perform our I/O
beast::flat_buffer m_buffer;
http::request<http::string_body> m_req;
http::response<http::dynamic_body> m_res;
tcp::resolver::results_type m_results;
};
}
#endif

246
modules/ANSFR/decode.cpp Normal file
View File

@@ -0,0 +1,246 @@
#include "decode.h"
#include "stdio.h"
#include "device_launch_parameters.h"
namespace nvinfer1
{
DecodePlugin::DecodePlugin()
{
}
DecodePlugin::~DecodePlugin()
{
}
// create the plugin at runtime from a byte stream
DecodePlugin::DecodePlugin(const void* data, size_t length)
{
}
void DecodePlugin::serialize(void* buffer) const TRT_NOEXCEPT
{
}
size_t DecodePlugin::getSerializationSize() const TRT_NOEXCEPT
{
return 0;
}
int DecodePlugin::initialize() TRT_NOEXCEPT
{
return 0;
}
Dims DecodePlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims) TRT_NOEXCEPT
{
//output the result to channel
int totalCount = 0;
totalCount += decodeplugin::INPUT_H / 8 * decodeplugin::INPUT_W / 8 * 2 * sizeof(decodeplugin::Detection) / sizeof(float);
totalCount += decodeplugin::INPUT_H / 16 * decodeplugin::INPUT_W / 16 * 2 * sizeof(decodeplugin::Detection) / sizeof(float);
totalCount += decodeplugin::INPUT_H / 32 * decodeplugin::INPUT_W / 32 * 2 * sizeof(decodeplugin::Detection) / sizeof(float);
return Dims3(totalCount + 1, 1, 1);
}
// Set plugin namespace
void DecodePlugin::setPluginNamespace(const char* pluginNamespace) TRT_NOEXCEPT
{
mPluginNamespace = pluginNamespace;
}
const char* DecodePlugin::getPluginNamespace() const TRT_NOEXCEPT
{
return mPluginNamespace;
}
// Return the DataType of the plugin output at the requested index
DataType DecodePlugin::getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const TRT_NOEXCEPT
{
return DataType::kFLOAT;
}
// Return true if output tensor is broadcast across a batch.
bool DecodePlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const TRT_NOEXCEPT
{
return false;
}
// Return true if plugin can use input that is broadcast across batch without replication.
bool DecodePlugin::canBroadcastInputAcrossBatch(int inputIndex) const TRT_NOEXCEPT
{
return false;
}
void DecodePlugin::configurePlugin(const PluginTensorDesc* in, int nbInput, const PluginTensorDesc* out, int nbOutput) TRT_NOEXCEPT
{
}
// Attach the plugin object to an execution context and grant the plugin the access to some context resource.
void DecodePlugin::attachToContext(cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) TRT_NOEXCEPT
{
}
// Detach the plugin object from its execution context.
void DecodePlugin::detachFromContext() TRT_NOEXCEPT {}
const char* DecodePlugin::getPluginType() const TRT_NOEXCEPT
{
return "Decode_TRT";
}
const char* DecodePlugin::getPluginVersion() const TRT_NOEXCEPT
{
return "1";
}
void DecodePlugin::destroy() TRT_NOEXCEPT
{
delete this;
}
// Clone the plugin
IPluginV2IOExt* DecodePlugin::clone() const TRT_NOEXCEPT
{
DecodePlugin* p = new DecodePlugin();
p->setPluginNamespace(mPluginNamespace);
return p;
}
__device__ float Logist(float data) { return 1. / (1. + expf(-data)); };
__global__ void CalDetection(const float* input, float* output, int num_elem, int step, int anchor, int output_elem) {
int idx = threadIdx.x + blockDim.x * blockIdx.x;
if (idx >= num_elem) return;
int h = decodeplugin::INPUT_H / step;
int w = decodeplugin::INPUT_W / step;
int total_grid = h * w;
int bn_idx = idx / total_grid;
idx = idx - bn_idx * total_grid;
int y = idx / w;
int x = idx % w;
const float* cur_input = input + bn_idx * (4 + 2 + 10) * 2 * total_grid;
const float* bbox_reg = &cur_input[0];
const float* cls_reg = &cur_input[2 * 4 * total_grid];
const float* lmk_reg = &cur_input[2 * 4 * total_grid + 2 * 2 * total_grid];
for (int k = 0; k < 2; ++k) {
float conf1 = cls_reg[idx + k * total_grid * 2];
float conf2 = cls_reg[idx + k * total_grid * 2 + total_grid];
conf2 = expf(conf2) / (expf(conf1) + expf(conf2));
if (conf2 <= 0.02) continue;
float* res_count = output + bn_idx * output_elem;
int count = (int)atomicAdd(res_count, 1);
char* data = (char*)res_count + sizeof(float) + count * sizeof(decodeplugin::Detection);
decodeplugin::Detection* det = (decodeplugin::Detection*)(data);
float prior[4];
prior[0] = ((float)x + 0.5) / w;
prior[1] = ((float)y + 0.5) / h;
prior[2] = (float)anchor * (k + 1) / decodeplugin::INPUT_W;
prior[3] = (float)anchor * (k + 1) / decodeplugin::INPUT_H;
//Location
det->bbox[0] = prior[0] + bbox_reg[idx + k * total_grid * 4] * 0.1 * prior[2];
det->bbox[1] = prior[1] + bbox_reg[idx + k * total_grid * 4 + total_grid] * 0.1 * prior[3];
det->bbox[2] = prior[2] * expf(bbox_reg[idx + k * total_grid * 4 + total_grid * 2] * 0.2);
det->bbox[3] = prior[3] * expf(bbox_reg[idx + k * total_grid * 4 + total_grid * 3] * 0.2);
det->bbox[0] -= det->bbox[2] / 2;
det->bbox[1] -= det->bbox[3] / 2;
det->bbox[2] += det->bbox[0];
det->bbox[3] += det->bbox[1];
det->bbox[0] *= decodeplugin::INPUT_W;
det->bbox[1] *= decodeplugin::INPUT_H;
det->bbox[2] *= decodeplugin::INPUT_W;
det->bbox[3] *= decodeplugin::INPUT_H;
det->class_confidence = conf2;
for (int i = 0; i < 10; i += 2) {
det->landmark[i] = prior[0] + lmk_reg[idx + k * total_grid * 10 + total_grid * i] * 0.1 * prior[2];
det->landmark[i + 1] = prior[1] + lmk_reg[idx + k * total_grid * 10 + total_grid * (i + 1)] * 0.1 * prior[3];
det->landmark[i] *= decodeplugin::INPUT_W;
det->landmark[i + 1] *= decodeplugin::INPUT_H;
}
}
}
void DecodePlugin::forwardGpu(const float* const* inputs, float* output, cudaStream_t stream, int batchSize)
{
int num_elem = 0;
int base_step = 8;
int base_anchor = 16;
int thread_count;
int totalCount = 1;
totalCount += decodeplugin::INPUT_H / 8 * decodeplugin::INPUT_W / 8 * 2 * sizeof(decodeplugin::Detection) / sizeof(float);
totalCount += decodeplugin::INPUT_H / 16 * decodeplugin::INPUT_W / 16 * 2 * sizeof(decodeplugin::Detection) / sizeof(float);
totalCount += decodeplugin::INPUT_H / 32 * decodeplugin::INPUT_W / 32 * 2 * sizeof(decodeplugin::Detection) / sizeof(float);
for (int idx = 0; idx < batchSize; ++idx) {
cudaMemsetAsync(output + idx * totalCount, 0, sizeof(float), stream);
}
for (unsigned int i = 0; i < 3; ++i)
{
num_elem = batchSize * decodeplugin::INPUT_H / base_step * decodeplugin::INPUT_W / base_step;
thread_count = (num_elem < thread_count_) ? num_elem : thread_count_;
CalDetection << < (num_elem + thread_count - 1) / thread_count, thread_count, 0, stream >> >
(inputs[i], output, num_elem, base_step, base_anchor, totalCount);
base_step *= 2;
base_anchor *= 4;
}
}
int DecodePlugin::enqueue(int batchSize, const void* const* inputs, void* TRT_CONST_ENQUEUE* outputs, void* workspace, cudaStream_t stream) TRT_NOEXCEPT
{
//GPU
//CUDA_CHECK(cudaStreamSynchronize(stream));
forwardGpu((const float* const*)inputs, (float*)outputs[0], stream, batchSize);
return 0;
};
PluginFieldCollection DecodePluginCreator::mFC{};
std::vector<PluginField> DecodePluginCreator::mPluginAttributes;
DecodePluginCreator::DecodePluginCreator()
{
mPluginAttributes.clear();
mFC.nbFields = mPluginAttributes.size();
mFC.fields = mPluginAttributes.data();
}
const char* DecodePluginCreator::getPluginName() const TRT_NOEXCEPT
{
return "Decode_TRT";
}
const char* DecodePluginCreator::getPluginVersion() const TRT_NOEXCEPT
{
return "1";
}
const PluginFieldCollection* DecodePluginCreator::getFieldNames() TRT_NOEXCEPT
{
return &mFC;
}
IPluginV2IOExt* DecodePluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc) TRT_NOEXCEPT
{
DecodePlugin* obj = new DecodePlugin();
obj->setPluginNamespace(mNamespace.c_str());
return obj;
}
IPluginV2IOExt* DecodePluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength) TRT_NOEXCEPT
{
// This object will be deleted when the network is destroyed, which will
// call PReluPlugin::destroy()
DecodePlugin* obj = new DecodePlugin(serialData, serialLength);
obj->setPluginNamespace(mNamespace.c_str());
return obj;
}
}

120
modules/ANSFR/decode.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef _DECODE_CU_H
#define _DECODE_CU_H
#include <string>
#include <vector>
#include "NvInfer.h"
#include "macros.h"
namespace decodeplugin
{
struct alignas(float) Detection{
float bbox[4]; //x1 y1 x2 y2
float class_confidence;
float landmark[10];
};
static const int INPUT_H = 480;
static const int INPUT_W = 640;
}
namespace nvinfer1
{
class DecodePlugin: public IPluginV2IOExt
{
public:
DecodePlugin();
DecodePlugin(const void* data, size_t length);
~DecodePlugin();
int getNbOutputs() const TRT_NOEXCEPT override
{
return 1;
}
Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) TRT_NOEXCEPT override;
int initialize() TRT_NOEXCEPT override;
virtual void terminate() TRT_NOEXCEPT override {};
virtual size_t getWorkspaceSize(int maxBatchSize) const TRT_NOEXCEPT override { return 0;}
virtual int enqueue(int batchSize, const void*const * inputs, void*TRT_CONST_ENQUEUE* outputs, void* workspace, cudaStream_t stream) TRT_NOEXCEPT override;
virtual size_t getSerializationSize() const TRT_NOEXCEPT override;
virtual void serialize(void* buffer) const TRT_NOEXCEPT override;
bool supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) const TRT_NOEXCEPT override {
return inOut[pos].format == TensorFormat::kLINEAR && inOut[pos].type == DataType::kFLOAT;
}
const char* getPluginType() const TRT_NOEXCEPT override;
const char* getPluginVersion() const TRT_NOEXCEPT override;
void destroy() TRT_NOEXCEPT override;
IPluginV2IOExt* clone() const TRT_NOEXCEPT override;
void setPluginNamespace(const char* pluginNamespace) TRT_NOEXCEPT override;
const char* getPluginNamespace() const TRT_NOEXCEPT override;
DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const TRT_NOEXCEPT override;
bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const TRT_NOEXCEPT override;
bool canBroadcastInputAcrossBatch(int inputIndex) const TRT_NOEXCEPT override;
void attachToContext(
cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) TRT_NOEXCEPT override;
void configurePlugin(const PluginTensorDesc* in, int nbInput, const PluginTensorDesc* out, int nbOutput) TRT_NOEXCEPT override;
void detachFromContext() TRT_NOEXCEPT override;
int input_size_;
private:
void forwardGpu(const float *const * inputs, float* output, cudaStream_t stream, int batchSize = 1);
int thread_count_ = 256;
const char* mPluginNamespace;
};
class DecodePluginCreator : public IPluginCreator
{
public:
DecodePluginCreator();
~DecodePluginCreator() override = default;
const char* getPluginName() const TRT_NOEXCEPT override;
const char* getPluginVersion() const TRT_NOEXCEPT override;
const PluginFieldCollection* getFieldNames() TRT_NOEXCEPT override;
IPluginV2IOExt* createPlugin(const char* name, const PluginFieldCollection* fc) TRT_NOEXCEPT override;
IPluginV2IOExt* deserializePlugin(const char* name, const void* serialData, size_t serialLength) TRT_NOEXCEPT override;
void setPluginNamespace(const char* libNamespace) TRT_NOEXCEPT override
{
mNamespace = libNamespace;
}
const char* getPluginNamespace() const TRT_NOEXCEPT override
{
return mNamespace.c_str();
}
private:
std::string mNamespace;
static PluginFieldCollection mFC;
static std::vector<PluginField> mPluginAttributes;
};
REGISTER_TENSORRT_PLUGIN(DecodePluginCreator);
};
#endif

1490
modules/ANSFR/dllmain.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define NOMINMAX // Prevent windows.h from defining min/max macros
// which break std::min / std::max (C2589)
// Windows Header Files
#include <windows.h>

12
modules/ANSFR/macros.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __MACROS_H
#define __MACROS_H
#if NV_TENSORRT_MAJOR >= 8
#define TRT_NOEXCEPT noexcept
#define TRT_CONST_ENQUEUE const
#else
#define TRT_NOEXCEPT
#define TRT_CONST_ENQUEUE
#endif
#endif // __MACROS_H

5
modules/ANSFR/pch.cpp Normal file
View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

18
modules/ANSFR/pch.h Normal file
View File

@@ -0,0 +1,18 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <mutex>
#endif //PCH_H

28831
modules/ANSFR/shell.c Normal file

File diff suppressed because it is too large Load Diff

252611
modules/ANSFR/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

13257
modules/ANSFR/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

719
modules/ANSFR/sqlite3ext.h Normal file
View File

@@ -0,0 +1,719 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*xsnprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*xvsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
/* Version 3.20.0 and later */
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
sqlite3_stmt**,const char**);
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
sqlite3_stmt**,const void**);
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
void *(*value_pointer)(sqlite3_value*,const char*);
int (*vtab_nochange)(sqlite3_context*);
int (*value_nochange)(sqlite3_value*);
const char *(*vtab_collation)(sqlite3_index_info*,int);
/* Version 3.24.0 and later */
int (*keyword_count)(void);
int (*keyword_name)(int,const char**,int*);
int (*keyword_check)(const char*,int);
sqlite3_str *(*str_new)(sqlite3*);
char *(*str_finish)(sqlite3_str*);
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
void (*str_append)(sqlite3_str*, const char *zIn, int N);
void (*str_appendall)(sqlite3_str*, const char *zIn);
void (*str_appendchar)(sqlite3_str*, int N, char C);
void (*str_reset)(sqlite3_str*);
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
/* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
/* Version 3.30.0 and later */
int (*drop_modules)(sqlite3*,const char**);
/* Version 3.31.0 and later */
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
const char *(*uri_key)(const char*,int);
const char *(*filename_database)(const char*);
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
const char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
/* Version 3.36.1 and later */
sqlite3_int64 (*changes64)(sqlite3*);
sqlite3_int64 (*total_changes64)(sqlite3*);
/* Version 3.37.0 and later */
int (*autovacuum_pages)(sqlite3*,
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
void*, void(*)(void*));
/* Version 3.38.0 and later */
int (*error_offset)(sqlite3*);
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
int (*vtab_distinct)(sqlite3_index_info*);
int (*vtab_in)(sqlite3_index_info*,int,int);
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
/* Version 3.39.0 and later */
int (*deserialize)(sqlite3*,const char*,unsigned char*,
sqlite3_int64,sqlite3_int64,unsigned);
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
/* Version 3.40.0 and later */
int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
/* Version 3.44.0 and later */
void *(*get_clientdata)(sqlite3*,const char*);
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->xsnprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
/* Version 3.20.0 and later */
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
#define sqlite3_result_pointer sqlite3_api->result_pointer
#define sqlite3_value_pointer sqlite3_api->value_pointer
/* Version 3.22.0 and later */
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
#define sqlite3_value_nochange sqlite3_api->value_nochange
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
/* Version 3.24.0 and later */
#define sqlite3_keyword_count sqlite3_api->keyword_count
#define sqlite3_keyword_name sqlite3_api->keyword_name
#define sqlite3_keyword_check sqlite3_api->keyword_check
#define sqlite3_str_new sqlite3_api->str_new
#define sqlite3_str_finish sqlite3_api->str_finish
#define sqlite3_str_appendf sqlite3_api->str_appendf
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
#define sqlite3_str_append sqlite3_api->str_append
#define sqlite3_str_appendall sqlite3_api->str_appendall
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
#define sqlite3_str_reset sqlite3_api->str_reset
#define sqlite3_str_errcode sqlite3_api->str_errcode
#define sqlite3_str_length sqlite3_api->str_length
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
#define sqlite3_value_frombind sqlite3_api->value_frombind
/* Version 3.30.0 and later */
#define sqlite3_drop_modules sqlite3_api->drop_modules
/* Version 3.31.0 and later */
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
#define sqlite3_uri_key sqlite3_api->uri_key
#define sqlite3_filename_database sqlite3_api->filename_database
#define sqlite3_filename_journal sqlite3_api->filename_journal
#define sqlite3_filename_wal sqlite3_api->filename_wal
/* Version 3.32.0 and later */
#define sqlite3_create_filename sqlite3_api->create_filename
#define sqlite3_free_filename sqlite3_api->free_filename
#define sqlite3_database_file_object sqlite3_api->database_file_object
/* Version 3.34.0 and later */
#define sqlite3_txn_state sqlite3_api->txn_state
/* Version 3.36.1 and later */
#define sqlite3_changes64 sqlite3_api->changes64
#define sqlite3_total_changes64 sqlite3_api->total_changes64
/* Version 3.37.0 and later */
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
/* Version 3.38.0 and later */
#define sqlite3_error_offset sqlite3_api->error_offset
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
#define sqlite3_vtab_in sqlite3_api->vtab_in
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
/* Version 3.39.0 and later */
#ifndef SQLITE_OMIT_DESERIALIZE
#define sqlite3_deserialize sqlite3_api->deserialize
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
/* Version 3.40.0 and later */
#define sqlite3_value_encoding sqlite3_api->value_encoding
/* Version 3.41.0 and later */
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
/* Version 3.44.0 and later */
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */