Refactor project structure
This commit is contained in:
3435
modules/ANSFR/ANSFR.cpp
Normal file
3435
modules/ANSFR/ANSFR.cpp
Normal file
File diff suppressed because it is too large
Load Diff
352
modules/ANSFR/ANSFR.h
Normal file
352
modules/ANSFR/ANSFR.h
Normal 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
|
||||
1134
modules/ANSFR/ANSFRCommon.cpp
Normal file
1134
modules/ANSFR/ANSFRCommon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
370
modules/ANSFR/ANSFRCommon.h
Normal file
370
modules/ANSFR/ANSFRCommon.h
Normal 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
|
||||
2110
modules/ANSFR/ANSFaceRecognizer.cpp
Normal file
2110
modules/ANSFR/ANSFaceRecognizer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
130
modules/ANSFR/ANSFaceRecognizer.h
Normal file
130
modules/ANSFR/ANSFaceRecognizer.h
Normal 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
|
||||
23
modules/ANSFR/ANSGpuFrameRegistry.cpp
Normal file
23
modules/ANSFR/ANSGpuFrameRegistry.cpp
Normal 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
692
modules/ANSFR/ARCFaceRT.cpp
Normal 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
134
modules/ANSFR/ARCFaceRT.h
Normal 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
|
||||
78
modules/ANSFR/CMakeLists.txt
Normal file
78
modules/ANSFR/CMakeLists.txt
Normal 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
|
||||
)
|
||||
909
modules/ANSFR/FaceDatabase.cpp
Normal file
909
modules/ANSFR/FaceDatabase.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
modules/ANSFR/FaceDatabase.h
Normal file
53
modules/ANSFR/FaceDatabase.h
Normal 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
412
modules/ANSFR/FaceNet.cpp
Normal 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
49
modules/ANSFR/FaceNet.h
Normal 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
|
||||
366
modules/ANSFR/RetinaFaceTRT.cpp
Normal file
366
modules/ANSFR/RetinaFaceTRT.cpp
Normal 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__);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
51
modules/ANSFR/RetinaFaceTRT.h
Normal file
51
modules/ANSFR/RetinaFaceTRT.h
Normal 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
|
||||
84
modules/ANSFR/WebClient.cpp
Normal file
84
modules/ANSFR/WebClient.cpp
Normal 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
45
modules/ANSFR/WebClient.h
Normal 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
246
modules/ANSFR/decode.cpp
Normal 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
120
modules/ANSFR/decode.h
Normal 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
1490
modules/ANSFR/dllmain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
modules/ANSFR/framework.h
Normal file
7
modules/ANSFR/framework.h
Normal 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
12
modules/ANSFR/macros.h
Normal 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
5
modules/ANSFR/pch.cpp
Normal 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
18
modules/ANSFR/pch.h
Normal 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
28831
modules/ANSFR/shell.c
Normal file
File diff suppressed because it is too large
Load Diff
252611
modules/ANSFR/sqlite3.c
Normal file
252611
modules/ANSFR/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
13257
modules/ANSFR/sqlite3.h
Normal file
13257
modules/ANSFR/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
719
modules/ANSFR/sqlite3ext.h
Normal file
719
modules/ANSFR/sqlite3ext.h
Normal 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 */
|
||||
Reference in New Issue
Block a user