From 844d7396b2ef252ae2a6057d45c9426f7b8de398 Mon Sep 17 00:00:00 2001 From: Tuan Nghia Nguyen Date: Mon, 13 Apr 2026 19:48:32 +1000 Subject: [PATCH] Fix mutex lock issues --- .claude/settings.local.json | 6 +- modules/ANSLPR/ANSLPR_OCR.cpp | 255 +++++++++++++++++----- modules/ANSLPR/ANSLPR_OCR.h | 6 +- modules/ANSODEngine/ANSANOMALIB.cpp | 5 +- modules/ANSODEngine/ANSCUSTOMDETECTOR.cpp | 5 +- modules/ANSODEngine/ANSEngineCommon.h | 54 +++++ modules/ANSODEngine/ANSODEngine.cpp | 5 +- modules/ANSODEngine/ANSODHUB.cpp | 5 +- modules/ANSODEngine/ANSONNXCL.cpp | 23 +- modules/ANSODEngine/ANSONNXOBB.cpp | 23 +- modules/ANSODEngine/ANSONNXPOSE.cpp | 23 +- modules/ANSODEngine/ANSONNXSAM3.cpp | 27 +-- modules/ANSODEngine/ANSONNXSEG.cpp | 23 +- modules/ANSODEngine/ANSONNXYOLO.cpp | 115 +++++++--- modules/ANSODEngine/ANSOPENVINOCL.cpp | 18 +- modules/ANSODEngine/ANSOPENVINOOD.cpp | 18 +- modules/ANSODEngine/ANSOVSEG.cpp | 5 +- modules/ANSODEngine/ANSPOSE.cpp | 5 +- modules/ANSODEngine/ANSRTYOLO.cpp | 41 +--- modules/ANSODEngine/ANSSAM.cpp | 5 +- modules/ANSODEngine/ANSSAM3.cpp | 13 +- modules/ANSODEngine/ANSTENSORRTCL.cpp | 54 +---- modules/ANSODEngine/ANSTENSORRTPOSE.cpp | 53 +---- modules/ANSODEngine/ANSTENSORRTSEG.cpp | 45 +--- modules/ANSODEngine/ANSTENSORTRTOD.cpp | 58 +---- modules/ANSODEngine/ANSYOLO12OD.cpp | 5 +- modules/ANSODEngine/ANSYOLOOD.cpp | 16 +- modules/ANSODEngine/ANSYOLOV10OVOD.cpp | 16 +- modules/ANSODEngine/ANSYOLOV10RTOD.cpp | 45 +--- modules/ANSODEngine/ANSYOLOV12RTOD.cpp | 48 +--- 30 files changed, 445 insertions(+), 575 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d13814a..5a029ab 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -121,7 +121,11 @@ "Bash(dir /s /b \"C:\\\\Program Files \\(x86\\)\\\\National Instruments\\\\Vision\\\\*OpenCV*\")", "Bash(dir /s /b \"C:\\\\Program Files \\(x86\\)\\\\National Instruments\\\\Vision\\\\Help\\\\*\")", "Bash(findstr /i \"opencv\")", - "WebFetch(domain:documentation.help)" + "WebFetch(domain:documentation.help)", + "Bash(python /tmp/apply_guards.py)", + "Bash(python /tmp/apply_all_guards.py)", + "Bash(python /tmp/cleanup_redundant_checks.py)", + "Bash(python /tmp/final_cleanup.py)" ] } } diff --git a/modules/ANSLPR/ANSLPR_OCR.cpp b/modules/ANSLPR/ANSLPR_OCR.cpp index a61988b..f7f0cfb 100644 --- a/modules/ANSLPR/ANSLPR_OCR.cpp +++ b/modules/ANSLPR/ANSLPR_OCR.cpp @@ -1,4 +1,5 @@ #include "ANSLPR_OCR.h" +#include "ANSRTYOLO.h" #include "ANSONNXYOLO.h" #include "ANSOnnxOCR.h" #include "ANSOCRBase.h" @@ -20,6 +21,59 @@ static void WriteEventLog(const char* message, WORD eventType = EVENTLOG_INFORMA OutputDebugStringA("\n"); } +// --------------------------------------------------------------------------- +// SEH wrapper for loading ANSRTYOLO (TensorRT) models — used when NVIDIA GPU +// is detected. Falls back to ANSONNXYOLO if TRT fails. +// --------------------------------------------------------------------------- +struct LoadRtParams_OCR { + const std::string* licenseKey; + ANSCENTER::ModelConfig* config; + const std::string* modelFolder; + const char* modelName; + const char* classFile; + std::string* labels; + std::unique_ptr* detector; + bool enableTracker; + bool disableStabilization; +}; + +static bool LoadRtModel_OCR_Impl(const LoadRtParams_OCR& p) { + try { + auto rtyolo = std::make_unique(); + bool ok = rtyolo->LoadModelFromFolder( + *p.licenseKey, *p.config, p.modelName, p.classFile, + *p.modelFolder, *p.labels); + if (!ok) { + return false; + } + if (p.enableTracker) { + rtyolo->SetTracker(ANSCENTER::TrackerType::BYTETRACK, true); + } else { + rtyolo->SetTracker(ANSCENTER::TrackerType::BYTETRACK, false); + } + if (p.disableStabilization) { + rtyolo->SetStabilization(false); + } + *p.detector = std::move(rtyolo); + return true; + } + catch (...) { + p.detector->reset(); + return false; + } +} + +static bool LoadRtModel_OCR_SEH(const LoadRtParams_OCR& p, DWORD* outCode) { + *outCode = 0; + __try { + return LoadRtModel_OCR_Impl(p); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + *outCode = GetExceptionCode(); + return false; + } +} + struct LoadOnnxParams_OCR { const std::string* licenseKey; ANSCENTER::ModelConfig* config; @@ -186,40 +240,81 @@ namespace ANSCENTER bool valid = false; - // ── Step 2: Load LP detector with ONNX Runtime ─────────────── + // ── Step 2: Load LP detector ───────────────────────────────── if (FileExist(lprModel)) { - WriteEventLog("ANSALPR_OCR::LoadEngine: Step 2 - Loading LP detector with ONNX Runtime"); - this->_logger.LogInfo("ANSALPR_OCR::LoadEngine", "Step 2: Loading LP detector with ONNX Runtime", __FILE__, __LINE__); - _lpdmodelConfig.detectionType = DetectionType::DETECTION; - _lpdmodelConfig.modelType = ModelType::ONNXYOLO; - std::string _lprClasses; - { - LoadOnnxParams_OCR p{}; - p.licenseKey = &_licenseKey; - p.config = &_lpdmodelConfig; - p.modelFolder = &_modelFolder; - p.modelName = "lpd"; - p.classFile = "lpd.names"; - p.labels = &_lprClasses; - p.detector = &_lpDetector; - p.enableTracker = true; - p.disableStabilization = true; + // Try TensorRT (ANSRTYOLO) when NVIDIA GPU is detected + if (engineType == EngineType::NVIDIA_GPU) { + WriteEventLog("ANSALPR_OCR::LoadEngine: Step 2 - Loading LP detector with TensorRT"); + this->_logger.LogInfo("ANSALPR_OCR::LoadEngine", "Step 2: Loading LP detector with TensorRT", __FILE__, __LINE__); + _lpdmodelConfig.detectionType = DetectionType::DETECTION; + _lpdmodelConfig.modelType = ModelType::RTYOLO; + std::string _lprClasses; + { + LoadRtParams_OCR p{}; + p.licenseKey = &_licenseKey; + p.config = &_lpdmodelConfig; + p.modelFolder = &_modelFolder; + p.modelName = "lpd"; + p.classFile = "lpd.names"; + p.labels = &_lprClasses; + p.detector = &_lpDetector; + p.enableTracker = true; + p.disableStabilization = true; - DWORD sehCode = 0; - bool lpSuccess = LoadOnnxModel_OCR_SEH(p, &sehCode); - if (sehCode != 0) { - char buf[256]; - snprintf(buf, sizeof(buf), - "ANSALPR_OCR::LoadEngine: Step 2 LPD SEH exception 0x%08X — LP detector disabled", sehCode); - WriteEventLog(buf, EVENTLOG_ERROR_TYPE); - this->_logger.LogFatal("ANSALPR_OCR::LoadEngine", - "Step 2: LP detector crashed (SEH). LP detector disabled.", __FILE__, __LINE__); - if (_lpDetector) _lpDetector.reset(); + DWORD sehCode = 0; + bool lpSuccess = LoadRtModel_OCR_SEH(p, &sehCode); + if (sehCode != 0) { + char buf[256]; + snprintf(buf, sizeof(buf), + "ANSALPR_OCR::LoadEngine: Step 2 LPD TRT SEH exception 0x%08X — falling back to ONNX Runtime", sehCode); + WriteEventLog(buf, EVENTLOG_ERROR_TYPE); + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Step 2: LP detector TensorRT crashed (SEH). Falling back to ONNX Runtime.", __FILE__, __LINE__); + if (_lpDetector) _lpDetector.reset(); + } + else if (!lpSuccess) { + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Failed to load LP detector (TensorRT). Falling back to ONNX Runtime.", __FILE__, __LINE__); + if (_lpDetector) _lpDetector.reset(); + } } - else if (!lpSuccess) { - this->_logger.LogError("ANSALPR_OCR::LoadEngine", - "Failed to load LP detector (ONNX Runtime).", __FILE__, __LINE__); - if (_lpDetector) _lpDetector.reset(); + } + + // Fallback to ONNX Runtime (ANSONNXYOLO) if TRT was not attempted or failed + if (!_lpDetector) { + WriteEventLog("ANSALPR_OCR::LoadEngine: Step 2 - Loading LP detector with ONNX Runtime"); + this->_logger.LogInfo("ANSALPR_OCR::LoadEngine", "Step 2: Loading LP detector with ONNX Runtime", __FILE__, __LINE__); + _lpdmodelConfig.detectionType = DetectionType::DETECTION; + _lpdmodelConfig.modelType = ModelType::ONNXYOLO; + std::string _lprClasses; + { + LoadOnnxParams_OCR p{}; + p.licenseKey = &_licenseKey; + p.config = &_lpdmodelConfig; + p.modelFolder = &_modelFolder; + p.modelName = "lpd"; + p.classFile = "lpd.names"; + p.labels = &_lprClasses; + p.detector = &_lpDetector; + p.enableTracker = true; + p.disableStabilization = true; + + DWORD sehCode = 0; + bool lpSuccess = LoadOnnxModel_OCR_SEH(p, &sehCode); + if (sehCode != 0) { + char buf[256]; + snprintf(buf, sizeof(buf), + "ANSALPR_OCR::LoadEngine: Step 2 LPD SEH exception 0x%08X — LP detector disabled", sehCode); + WriteEventLog(buf, EVENTLOG_ERROR_TYPE); + this->_logger.LogFatal("ANSALPR_OCR::LoadEngine", + "Step 2: LP detector crashed (SEH). LP detector disabled.", __FILE__, __LINE__); + if (_lpDetector) _lpDetector.reset(); + } + else if (!lpSuccess) { + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Failed to load LP detector (ONNX Runtime).", __FILE__, __LINE__); + if (_lpDetector) _lpDetector.reset(); + } } } } @@ -302,37 +397,77 @@ namespace ANSCENTER // ── Step 4: Load colour classifier (optional) ──────────────── if (FileExist(colorModel) && (_lpColourModelConfig.detectionScoreThreshold > 0)) { - WriteEventLog("ANSALPR_OCR::LoadEngine: Step 4 - Loading colour classifier with ONNX Runtime"); - this->_logger.LogInfo("ANSALPR_OCR::LoadEngine", "Step 4: Loading colour classifier with ONNX Runtime", __FILE__, __LINE__); - _lpColourModelConfig.detectionType = DetectionType::CLASSIFICATION; - _lpColourModelConfig.modelType = ModelType::ONNXYOLO; - { - LoadOnnxParams_OCR p{}; - p.licenseKey = &_licenseKey; - p.config = &_lpColourModelConfig; - p.modelFolder = &_modelFolder; - p.modelName = "lpc"; - p.classFile = "lpc.names"; - p.labels = &_lpColourLabels; - p.detector = &_lpColourDetector; - p.enableTracker = false; - p.disableStabilization = false; + // Try TensorRT (ANSRTYOLO) when NVIDIA GPU is detected + if (engineType == EngineType::NVIDIA_GPU) { + WriteEventLog("ANSALPR_OCR::LoadEngine: Step 4 - Loading colour classifier with TensorRT"); + this->_logger.LogInfo("ANSALPR_OCR::LoadEngine", "Step 4: Loading colour classifier with TensorRT", __FILE__, __LINE__); + _lpColourModelConfig.detectionType = DetectionType::CLASSIFICATION; + _lpColourModelConfig.modelType = ModelType::RTYOLO; + { + LoadRtParams_OCR p{}; + p.licenseKey = &_licenseKey; + p.config = &_lpColourModelConfig; + p.modelFolder = &_modelFolder; + p.modelName = "lpc"; + p.classFile = "lpc.names"; + p.labels = &_lpColourLabels; + p.detector = &_lpColourDetector; + p.enableTracker = false; + p.disableStabilization = false; - DWORD sehCode = 0; - bool colourSuccess = LoadOnnxModel_OCR_SEH(p, &sehCode); - if (sehCode != 0) { - char buf[256]; - snprintf(buf, sizeof(buf), - "ANSALPR_OCR::LoadEngine: Step 4 LPC SEH exception 0x%08X — colour detection disabled", sehCode); - WriteEventLog(buf, EVENTLOG_ERROR_TYPE); - this->_logger.LogError("ANSALPR_OCR::LoadEngine", - "Step 4: Colour classifier crashed. Colour detection disabled.", __FILE__, __LINE__); - if (_lpColourDetector) _lpColourDetector.reset(); + DWORD sehCode = 0; + bool colourSuccess = LoadRtModel_OCR_SEH(p, &sehCode); + if (sehCode != 0) { + char buf[256]; + snprintf(buf, sizeof(buf), + "ANSALPR_OCR::LoadEngine: Step 4 LPC TRT SEH exception 0x%08X — falling back to ONNX Runtime", sehCode); + WriteEventLog(buf, EVENTLOG_ERROR_TYPE); + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Step 4: Colour classifier TensorRT crashed (SEH). Falling back to ONNX Runtime.", __FILE__, __LINE__); + if (_lpColourDetector) _lpColourDetector.reset(); + } + else if (!colourSuccess) { + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Failed to load colour classifier (TensorRT). Falling back to ONNX Runtime.", __FILE__, __LINE__); + if (_lpColourDetector) _lpColourDetector.reset(); + } } - else if (!colourSuccess) { - this->_logger.LogError("ANSALPR_OCR::LoadEngine", - "Failed to load colour detector (ONNX Runtime). Colour detection disabled.", __FILE__, __LINE__); - if (_lpColourDetector) _lpColourDetector.reset(); + } + + // Fallback to ONNX Runtime (ANSONNXYOLO) if TRT was not attempted or failed + if (!_lpColourDetector) { + WriteEventLog("ANSALPR_OCR::LoadEngine: Step 4 - Loading colour classifier with ONNX Runtime"); + this->_logger.LogInfo("ANSALPR_OCR::LoadEngine", "Step 4: Loading colour classifier with ONNX Runtime", __FILE__, __LINE__); + _lpColourModelConfig.detectionType = DetectionType::CLASSIFICATION; + _lpColourModelConfig.modelType = ModelType::ONNXYOLO; + { + LoadOnnxParams_OCR p{}; + p.licenseKey = &_licenseKey; + p.config = &_lpColourModelConfig; + p.modelFolder = &_modelFolder; + p.modelName = "lpc"; + p.classFile = "lpc.names"; + p.labels = &_lpColourLabels; + p.detector = &_lpColourDetector; + p.enableTracker = false; + p.disableStabilization = false; + + DWORD sehCode = 0; + bool colourSuccess = LoadOnnxModel_OCR_SEH(p, &sehCode); + if (sehCode != 0) { + char buf[256]; + snprintf(buf, sizeof(buf), + "ANSALPR_OCR::LoadEngine: Step 4 LPC SEH exception 0x%08X — colour detection disabled", sehCode); + WriteEventLog(buf, EVENTLOG_ERROR_TYPE); + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Step 4: Colour classifier crashed. Colour detection disabled.", __FILE__, __LINE__); + if (_lpColourDetector) _lpColourDetector.reset(); + } + else if (!colourSuccess) { + this->_logger.LogError("ANSALPR_OCR::LoadEngine", + "Failed to load colour detector (ONNX Runtime). Colour detection disabled.", __FILE__, __LINE__); + if (_lpColourDetector) _lpColourDetector.reset(); + } } } } diff --git a/modules/ANSLPR/ANSLPR_OCR.h b/modules/ANSLPR/ANSLPR_OCR.h index c9dc8f3..fa53597 100644 --- a/modules/ANSLPR/ANSLPR_OCR.h +++ b/modules/ANSLPR/ANSLPR_OCR.h @@ -14,13 +14,13 @@ namespace ANSCENTER { class ANSONNXOCR; struct OCRModelConfig; } namespace ANSCENTER { - /// ANSALPR_OCR — License plate recognition using ONNX YOLO for LP detection + /// ANSALPR_OCR — License plate recognition using YOLO for LP detection /// and ANSONNXOCR (PaddleOCR v5) for text recognition. /// /// Pipeline: - /// 1. Detect license plates using _lpDetector (ANSONNXYOLO) + /// 1. Detect license plates using _lpDetector (ANSRTYOLO on NVIDIA GPU, ANSONNXYOLO otherwise) /// 2. For each detected plate, run OCR using _ocrEngine (ANSONNXOCR) - /// 3. Optionally classify plate colour using _lpColourDetector (ANSONNXYOLO) + /// 3. Optionally classify plate colour using _lpColourDetector (ANSRTYOLO on NVIDIA GPU, ANSONNXYOLO otherwise) /// /// Supports multiple countries via the Country enum and ALPR post-processing /// from ANSOCR's ANSOCRBase infrastructure. diff --git a/modules/ANSODEngine/ANSANOMALIB.cpp b/modules/ANSODEngine/ANSANOMALIB.cpp index 1d3263f..34928e6 100644 --- a/modules/ANSODEngine/ANSANOMALIB.cpp +++ b/modules/ANSODEngine/ANSANOMALIB.cpp @@ -29,6 +29,7 @@ namespace ANSCENTER { } bool ANSANOMALIB::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -86,6 +87,7 @@ namespace ANSCENTER { bool ANSANOMALIB::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -159,6 +161,7 @@ namespace ANSCENTER { } bool ANSANOMALIB::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { std::string openVINOVersion = ov::get_openvino_version().buildNumber; //this->_logger.LogDebug("ANSANOMALIB::Initialize. OpenVINO version", openVINOVersion, __FILE__, __LINE__); @@ -232,7 +235,7 @@ namespace ANSCENTER { return RunInference(input, "ANSANOMALIBCAM"); } std::vector ANSANOMALIB::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("ANSANOMALIB::RunInference")) return {}; std::vector output; output.clear(); if (!_licenseValid) { diff --git a/modules/ANSODEngine/ANSCUSTOMDETECTOR.cpp b/modules/ANSODEngine/ANSCUSTOMDETECTOR.cpp index 524c029..2cf29eb 100644 --- a/modules/ANSODEngine/ANSCUSTOMDETECTOR.cpp +++ b/modules/ANSODEngine/ANSCUSTOMDETECTOR.cpp @@ -34,6 +34,7 @@ namespace ANSCENTER } bool ANSCUSTOMDETECTOR::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -50,6 +51,7 @@ namespace ANSCENTER } bool ANSCUSTOMDETECTOR::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _modelFolder = modelFolder; std::string labelMap; @@ -242,6 +244,7 @@ namespace ANSCENTER } bool ANSCUSTOMDETECTOR::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -268,7 +271,7 @@ namespace ANSCENTER return RunInference(input, "CustomCam"); } std::vector ANSCUSTOMDETECTOR::RunInference(const cv::Mat& input,const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("ANSCUSTOMDETECTOR::RunInference")) return {}; std::vector result; try { diff --git a/modules/ANSODEngine/ANSEngineCommon.h b/modules/ANSODEngine/ANSEngineCommon.h index 86c1da2..04aefe9 100644 --- a/modules/ANSODEngine/ANSEngineCommon.h +++ b/modules/ANSODEngine/ANSEngineCommon.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "ANSMOT.h" #include "onnxruntime_cxx_api.h" @@ -729,6 +731,58 @@ namespace ANSCENTER void LoadClassesFromString(); void LoadClassesFromFile(); std::recursive_mutex _mutex; + // Guard: true while Initialize/LoadModel is in progress on any thread. + // Inference callers can check this to fail-fast instead of blocking. + std::atomic _modelLoading{ false }; + + // Try to lock _mutex with a timeout. Returns a unique_lock that + // evaluates to true on success. On timeout, logs a warning with + // the caller name and returns an unlocked unique_lock. + std::unique_lock TryLockWithTimeout( + const char* caller, unsigned int timeoutMs = 5000) + { + const auto deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(timeoutMs); + std::unique_lock lk(_mutex, std::defer_lock); + while (!lk.try_lock()) { + if (std::chrono::steady_clock::now() >= deadline) { + _logger.LogWarn(caller, + "Mutex acquisition timed out after " + + std::to_string(timeoutMs) + " ms" + + (_modelLoading.load() ? " (model loading in progress)" : ""), + __FILE__, __LINE__); + return lk; // unlocked + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return lk; // locked + } + + // Pre-inference gate: checks _modelLoading, validates state + // (_modelLoadValid, _licenseValid, _isInitialized), and acquires + // _mutex with a timeout. Returns true if inference may proceed. + // On failure (loading in progress, timeout, or invalid state) + // returns false and the caller should return {}. + bool PreInferenceCheck(const char* caller) { + if (_modelLoading.load()) return false; + auto lk = TryLockWithTimeout(caller); + if (!lk.owns_lock()) return false; + if (!_licenseValid || !_isInitialized) + return false; + return true; // lock released here — caller proceeds unlocked + } + + // RAII helper: sets _modelLoading=true on construction, false on destruction. + // Use in Initialize/LoadModel/LoadModelFromFolder to guarantee the flag + // is always cleared, even on exceptions or early returns. + struct ModelLoadingGuard { + std::atomic& flag; + explicit ModelLoadingGuard(std::atomic& f) : flag(f) { flag.store(true); } + ~ModelLoadingGuard() { flag.store(false); } + ModelLoadingGuard(const ModelLoadingGuard&) = delete; + ModelLoadingGuard& operator=(const ModelLoadingGuard&) = delete; + }; + MoveDetectsHandler _handler; size_t QUEUE_SIZE = 20; diff --git a/modules/ANSODEngine/ANSODEngine.cpp b/modules/ANSODEngine/ANSODEngine.cpp index 27b28f1..173e61d 100644 --- a/modules/ANSODEngine/ANSODEngine.cpp +++ b/modules/ANSODEngine/ANSODEngine.cpp @@ -2135,8 +2135,9 @@ namespace ANSCENTER // Update model configuration with the new parameters (brief lock for config) if (confidenceThreshold > 0) { - std::lock_guard cfgLock(_mutex); - _modelConfig.detectionScoreThreshold = confidenceThreshold; + auto cfgLock = TryLockWithTimeout("ANSODBase::RunInferenceWithOption", 2000); + if (cfgLock.owns_lock()) + _modelConfig.detectionScoreThreshold = confidenceThreshold; } switch (mode) { case 0: // Normal mode diff --git a/modules/ANSODEngine/ANSODHUB.cpp b/modules/ANSODEngine/ANSODHUB.cpp index cace325..7440da4 100644 --- a/modules/ANSODEngine/ANSODHUB.cpp +++ b/modules/ANSODEngine/ANSODHUB.cpp @@ -21,6 +21,7 @@ namespace ANSCENTER } bool ODHUBAPI::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -77,6 +78,7 @@ namespace ANSCENTER } bool ODHUBAPI::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -147,6 +149,7 @@ namespace ANSCENTER } bool ODHUBAPI::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); @@ -218,7 +221,7 @@ namespace ANSCENTER } std::vector ODHUBAPI::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("ODHUBAPI::RunInference")) return {}; std::vector result; try { diff --git a/modules/ANSODEngine/ANSONNXCL.cpp b/modules/ANSODEngine/ANSONNXCL.cpp index b6fcbfa..1fa72cf 100644 --- a/modules/ANSODEngine/ANSONNXCL.cpp +++ b/modules/ANSODEngine/ANSONNXCL.cpp @@ -870,6 +870,7 @@ namespace ANSCENTER } bool ANSONNXCL::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _modelLoadValid = false; bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); @@ -931,6 +932,7 @@ namespace ANSCENTER } bool ANSONNXCL::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -986,6 +988,7 @@ namespace ANSCENTER } bool ANSONNXCL::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -1056,25 +1059,7 @@ namespace ANSCENTER Destroy(); } std::vector ANSONNXCL::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - this->_logger.LogFatal("ANSONNXCL::RunInference", "Cannot load the TensorRT model. Please check if it is exist", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_licenseValid) { - this->_logger.LogFatal("ANSONNXCL::RunInference", "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_isInitialized) { - this->_logger.LogFatal("ANSONNXCL::RunInference", "Model is not initialized", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } + if (!PreInferenceCheck("ANSONNXCL::RunInference")) return {}; try { std::vector result; if (input.empty()) return result; diff --git a/modules/ANSODEngine/ANSONNXOBB.cpp b/modules/ANSODEngine/ANSONNXOBB.cpp index aec7807..9b79e05 100644 --- a/modules/ANSODEngine/ANSONNXOBB.cpp +++ b/modules/ANSODEngine/ANSONNXOBB.cpp @@ -1372,6 +1372,7 @@ namespace ANSCENTER { } bool ANSONNXOBB::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _modelLoadValid = false; bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); @@ -1433,6 +1434,7 @@ namespace ANSCENTER { } bool ANSONNXOBB::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -1488,6 +1490,7 @@ namespace ANSCENTER { } bool ANSONNXOBB::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -1551,25 +1554,7 @@ namespace ANSCENTER { } } std::vector ANSONNXOBB::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - this->_logger.LogFatal("ANSONNXOBB::RunInference", "Cannot load the TensorRT model. Please check if it is exist", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_licenseValid) { - this->_logger.LogFatal("ANSONNXOBB::RunInference", "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_isInitialized) { - this->_logger.LogFatal("ANSONNXOBB::RunInference", "Model is not initialized", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } + if (!PreInferenceCheck("ANSONNXOBB::RunInference")) return {}; try { std::vector result; if (input.empty()) return result; diff --git a/modules/ANSODEngine/ANSONNXPOSE.cpp b/modules/ANSODEngine/ANSONNXPOSE.cpp index 4ee11c1..5f2cbf3 100644 --- a/modules/ANSODEngine/ANSONNXPOSE.cpp +++ b/modules/ANSODEngine/ANSONNXPOSE.cpp @@ -1262,6 +1262,7 @@ namespace ANSCENTER { } bool ANSONNXPOSE::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _modelLoadValid = false; bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); @@ -1324,6 +1325,7 @@ namespace ANSCENTER { } bool ANSONNXPOSE::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -1380,6 +1382,7 @@ namespace ANSCENTER { } bool ANSONNXPOSE::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -1444,25 +1447,7 @@ namespace ANSCENTER { } } std::vector ANSONNXPOSE::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - this->_logger.LogFatal("ANSONNXPOSE::RunInference", "Cannot load the TensorRT model. Please check if it is exist", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_licenseValid) { - this->_logger.LogFatal("ANSONNXPOSE::RunInference", "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_isInitialized) { - this->_logger.LogFatal("ANSONNXPOSE::RunInference", "Model is not initialized", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } + if (!PreInferenceCheck("ANSONNXPOSE::RunInference")) return {}; try { std::vector result; if (input.empty()) return result; diff --git a/modules/ANSODEngine/ANSONNXSAM3.cpp b/modules/ANSODEngine/ANSONNXSAM3.cpp index 73b6adc..6f5eda4 100644 --- a/modules/ANSODEngine/ANSONNXSAM3.cpp +++ b/modules/ANSODEngine/ANSONNXSAM3.cpp @@ -37,6 +37,7 @@ namespace ANSCENTER std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); @@ -84,6 +85,7 @@ namespace ANSCENTER const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -131,6 +133,7 @@ namespace ANSCENTER const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); @@ -281,29 +284,7 @@ namespace ANSCENTER std::vector ANSONNXSAM3::RunInference(const cv::Mat& input, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("ANSONNXSAM3::RunInference", "Model not loaded", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("ANSONNXSAM3::RunInference", "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("ANSONNXSAM3::RunInference", "Model not initialized", __FILE__, __LINE__); - return {}; - } - if (!m_promptSet) { - _logger.LogError("ANSONNXSAM3::RunInference", - "No prompt set. Call SetPrompt() first.", __FILE__, __LINE__); - return {}; - } - if (input.empty() || input.cols < 10 || input.rows < 10) { - return {}; - } - } + if (!PreInferenceCheck("ANSONNXSAM3::RunInference")) return {}; try { // Run ONNX engine inference diff --git a/modules/ANSODEngine/ANSONNXSEG.cpp b/modules/ANSODEngine/ANSONNXSEG.cpp index 4ff889a..d1fce36 100644 --- a/modules/ANSODEngine/ANSONNXSEG.cpp +++ b/modules/ANSODEngine/ANSONNXSEG.cpp @@ -1153,6 +1153,7 @@ namespace ANSCENTER { } bool ANSONNXSEG::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _modelLoadValid = false; bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); @@ -1215,6 +1216,7 @@ namespace ANSCENTER { } bool ANSONNXSEG::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -1271,6 +1273,7 @@ namespace ANSCENTER { } bool ANSONNXSEG::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -1335,25 +1338,7 @@ namespace ANSCENTER { } } std::vector ANSONNXSEG::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - this->_logger.LogFatal("ANSONNXSEG::RunInference", "Cannot load the TensorRT model. Please check if it is exist", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_licenseValid) { - this->_logger.LogFatal("ANSONNXSEG::RunInference", "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } - if (!_isInitialized) { - this->_logger.LogFatal("ANSONNXSEG::RunInference", "Model is not initialized", __FILE__, __LINE__); - std::vector result; - result.clear(); - return result; - } + if (!PreInferenceCheck("ANSONNXSEG::RunInference")) return {}; try { std::vector result; if (input.empty()) return result; diff --git a/modules/ANSODEngine/ANSONNXYOLO.cpp b/modules/ANSODEngine/ANSONNXYOLO.cpp index b50d4df..8cdfda6 100644 --- a/modules/ANSODEngine/ANSONNXYOLO.cpp +++ b/modules/ANSODEngine/ANSONNXYOLO.cpp @@ -1734,6 +1734,7 @@ namespace ANSCENTER { std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _modelLoadValid = false; bool result = ANSODBase::Initialize(licenseKey, modelConfig, @@ -1810,6 +1811,7 @@ namespace ANSCENTER { const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -1873,6 +1875,7 @@ namespace ANSCENTER { std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, @@ -1948,8 +1951,10 @@ namespace ANSCENTER { std::vector ANSONNXYOLO::RunInference(const cv::Mat& inputImgBGR, const std::string& camera_id) { + if (_modelLoading.load()) return {}; { - std::lock_guard lock(_mutex); + auto lock = TryLockWithTimeout("ANSONNXYOLO::RunInference"); + if (!lock.owns_lock()) return {}; if (!_modelLoadValid) { _logger.LogError("ANSONNXYOLO::RunInference", "Model not loaded", __FILE__, __LINE__); return {}; @@ -1976,29 +1981,55 @@ namespace ANSCENTER { const std::string& camera_id) { try { - ANS_DBG("ONNXYOLO", "DetectObjects: cam=%s acquiring mutex...", camera_id.c_str()); - std::lock_guard lock(_mutex); - ANS_DBG("ONNXYOLO", "DetectObjects: mutex acquired, cam=%s", camera_id.c_str()); - if (!m_ortEngine) { - _logger.LogError("ANSONNXYOLO::DetectObjects", "ORT engine is null", __FILE__, __LINE__); - ANS_DBG("ONNXYOLO", "DetectObjects: ORT engine is null!"); + // Fail-fast if a model load/init is in progress on another thread + if (_modelLoading.load()) { + ANS_DBG("ONNXYOLO", "DetectObjects: skipped — model loading in progress, cam=%s", camera_id.c_str()); return {}; } + // ── Snapshot config under a brief lock ────────────────── + // Only hold _mutex long enough to validate state and copy + // the parameters needed for inference. The actual ORT + // detect() call runs outside the lock so that concurrent + // Initialize/LoadModel calls are not blocked for the full + // duration of inference. + float probThresh, nmsThresh; + int numKps; + std::vector classes; + bool trackerEnabled, stabilizationEnabled; + { + ANS_DBG("ONNXYOLO", "DetectObjects: cam=%s acquiring mutex...", camera_id.c_str()); + auto lk = TryLockWithTimeout("ANSONNXYOLO::DetectObjects"); + if (!lk.owns_lock()) return {}; // timed out + ANS_DBG("ONNXYOLO", "DetectObjects: mutex acquired, cam=%s", camera_id.c_str()); + + if (!m_ortEngine) { + _logger.LogError("ANSONNXYOLO::DetectObjects", "ORT engine is null", __FILE__, __LINE__); + ANS_DBG("ONNXYOLO", "DetectObjects: ORT engine is null!"); + return {}; + } + + // Snapshot parameters while locked + probThresh = PROBABILITY_THRESHOLD; + nmsThresh = NMS_THRESHOLD; + numKps = NUM_KPS; + classes = _classes; + trackerEnabled = _trackerEnabled; + stabilizationEnabled = _stabilizationEnabled; + } + // ── _mutex released — heavy work below runs lock-free ─── + // --- NV12 fast path: try to get full-res BGR from GPU NV12 frame --- cv::Mat inferenceImage = inputImage; float bgrScaleX = 1.0f, bgrScaleY = 1.0f; { auto* gpuData = tl_currentGpuFrame(); if (gpuData && gpuData->width > 0 && gpuData->height > 0) { - // Full-res NV12 available — convert to BGR on CPU for ORT - // (ORT preprocessing is CPU-based, so we need a cv::Mat) if (gpuData->cpuYPlane && gpuData->cpuUvPlane && gpuData->cpuYLinesize >= gpuData->width && gpuData->cpuUvLinesize >= gpuData->width) { const int fw = gpuData->width; const int fh = gpuData->height; - // NV12 requires even dimensions if ((fw % 2) == 0 && (fh % 2) == 0) { try { cv::Mat yPlane(fh, fw, CV_8UC1, @@ -2017,10 +2048,12 @@ namespace ANSCENTER { } } } - auto results = m_ortEngine->detect(inferenceImage, _classes, - PROBABILITY_THRESHOLD, - NMS_THRESHOLD, - NUM_KPS); + + // Run ORT inference — no mutex held, this is the expensive call + auto results = m_ortEngine->detect(inferenceImage, classes, + probThresh, + nmsThresh, + numKps); // --- Rescale coordinates from full-res back to display-res --- if (bgrScaleX != 1.0f || bgrScaleY != 1.0f) { @@ -2044,21 +2077,16 @@ namespace ANSCENTER { for (auto& obj : results) obj.cameraId = camera_id; - // Skip tracking for classification models - if (_trackerEnabled && !m_ortEngine->lastWasClassification) { + // Tracking/stabilization (ApplyTracking has its own lock) + if (trackerEnabled && !m_ortEngine->lastWasClassification) { results = ApplyTracking(results, camera_id); - if (_stabilizationEnabled) results = StabilizeDetections(results, camera_id); + if (stabilizationEnabled) results = StabilizeDetections(results, camera_id); } return results; } catch (const std::exception& e) { const std::string msg = e.what(); - // ── DML device-removal detection ────────────────────────── - // HRESULT 887A0005 = DXGI_ERROR_DEVICE_REMOVED ("The GPU - // device instance has been suspended"). Once the D3D12 - // device is gone the ORT session is permanently broken. - // Log once, attempt CPU fallback, suppress further flood. if (msg.find("887A0005") != std::string::npos) { if (!_dmlDeviceLost) { _dmlDeviceLost = true; @@ -2067,6 +2095,7 @@ namespace ANSCENTER { __FILE__, __LINE__); ANS_DBG("ONNXYOLO", "DML device lost — recreating session on CPU"); try { + std::lock_guard lk(_mutex); m_ortEngine.reset(); if (InitOrtEngine(ANSCENTER::EngineType::CPU)) { _logger.LogInfo("ANSONNXYOLO::DetectObjects", @@ -2084,7 +2113,6 @@ namespace ANSCENTER { __FILE__, __LINE__); } } - // Suppress flood — already logged above return {}; } @@ -2101,8 +2129,10 @@ namespace ANSCENTER { std::vector> ANSONNXYOLO::RunInferencesBatch( const std::vector& inputs, const std::string& camera_id) { + if (_modelLoading.load()) return {}; { - std::lock_guard lock(_mutex); + auto lock = TryLockWithTimeout("ANSONNXYOLO::RunInferencesBatch"); + if (!lock.owns_lock()) return {}; if (!_modelLoadValid) { _logger.LogFatal("ANSONNXYOLO::RunInferencesBatch", "Cannot load ONNX model", __FILE__, __LINE__); @@ -2154,16 +2184,33 @@ namespace ANSCENTER { const std::vector& inputImages, const std::string& camera_id) { try { - std::lock_guard lock(_mutex); - if (!m_ortEngine) { - _logger.LogError("ANSONNXYOLO::DetectObjectsBatch", - "ORT engine is null", __FILE__, __LINE__); - return {}; + if (_modelLoading.load()) return {}; + + // Snapshot config under brief lock + float probThresh, nmsThresh; + int numKps; + std::vector classes; + bool trackerEnabled, stabilizationEnabled; + { + auto lk = TryLockWithTimeout("ANSONNXYOLO::DetectObjectsBatch"); + if (!lk.owns_lock()) return {}; + + if (!m_ortEngine) { + _logger.LogError("ANSONNXYOLO::DetectObjectsBatch", + "ORT engine is null", __FILE__, __LINE__); + return {}; + } + probThresh = PROBABILITY_THRESHOLD; + nmsThresh = NMS_THRESHOLD; + numKps = NUM_KPS; + classes = _classes; + trackerEnabled = _trackerEnabled; + stabilizationEnabled = _stabilizationEnabled; } + // Heavy work outside lock auto batchResults = m_ortEngine->detectBatch( - inputImages, _classes, - PROBABILITY_THRESHOLD, NMS_THRESHOLD, NUM_KPS); + inputImages, classes, probThresh, nmsThresh, numKps); const bool isClassification = m_ortEngine->lastBatchWasClassification; @@ -2171,10 +2218,9 @@ namespace ANSCENTER { for (auto& obj : results) obj.cameraId = camera_id; - // Skip tracking for classification models - if (_trackerEnabled && !isClassification) { + if (trackerEnabled && !isClassification) { results = ApplyTracking(results, camera_id); - if (_stabilizationEnabled) results = StabilizeDetections(results, camera_id); + if (stabilizationEnabled) results = StabilizeDetections(results, camera_id); } } @@ -2189,6 +2235,7 @@ namespace ANSCENTER { "DirectML GPU device lost (887A0005) — attempting CPU fallback", __FILE__, __LINE__); try { + std::lock_guard lk(_mutex); m_ortEngine.reset(); if (!InitOrtEngine(ANSCENTER::EngineType::CPU)) _logger.LogFatal("ANSONNXYOLO::DetectObjectsBatch", diff --git a/modules/ANSODEngine/ANSOPENVINOCL.cpp b/modules/ANSODEngine/ANSOPENVINOCL.cpp index cb16e71..3fb3214 100644 --- a/modules/ANSODEngine/ANSOPENVINOCL.cpp +++ b/modules/ANSODEngine/ANSOPENVINOCL.cpp @@ -31,6 +31,7 @@ namespace ANSCENTER } bool OPENVINOCL::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -78,6 +79,7 @@ namespace ANSCENTER } bool OPENVINOCL::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -173,6 +175,7 @@ namespace ANSCENTER } bool OPENVINOCL::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { std::string openVINOVersion = ov::get_openvino_version().buildNumber; //this->_logger.LogDebug("OPENVINOCL::Initialize. OpenVINO version", openVINOVersion, __FILE__, __LINE__); @@ -242,20 +245,7 @@ namespace ANSCENTER } std::vector OPENVINOCL::RunInference(const cv::Mat& input,const std::string& camera_id) { - std::lock_guard lock(_mutex); - - // Early validation - if (!_licenseValid) { - _logger.LogError("OPENVINOCL::RunInference", "Invalid License", - __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - _logger.LogError("OPENVINOCL::RunInference", "Model is not initialized", - __FILE__, __LINE__); - return {}; - } + if (!PreInferenceCheck("OPENVINOCL::RunInference")) return {}; if (input.empty() || input.cols < 20 || input.rows < 20) { _logger.LogError("OPENVINOCL::RunInference", "Input image is invalid", diff --git a/modules/ANSODEngine/ANSOPENVINOOD.cpp b/modules/ANSODEngine/ANSOPENVINOOD.cpp index b005a38..4da131a 100644 --- a/modules/ANSODEngine/ANSOPENVINOOD.cpp +++ b/modules/ANSODEngine/ANSOPENVINOOD.cpp @@ -31,6 +31,7 @@ namespace ANSCENTER } bool OPENVINOOD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -79,6 +80,7 @@ namespace ANSCENTER } bool OPENVINOOD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -180,6 +182,7 @@ namespace ANSCENTER } bool OPENVINOOD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -248,20 +251,7 @@ namespace ANSCENTER } std::vector OPENVINOOD::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - - // Early validation - if (!_licenseValid) { - _logger.LogError("OPENVINOOD::RunInference", "Invalid License", - __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - _logger.LogError("OPENVINOOD::RunInference", "Model is not initialized", - __FILE__, __LINE__); - return {}; - } + if (!PreInferenceCheck("OPENVINOOD::RunInference")) return {}; if (input.empty() || input.cols < 10 || input.rows < 10) { _logger.LogError("OPENVINOOD::RunInference", "Invalid input image", diff --git a/modules/ANSODEngine/ANSOVSEG.cpp b/modules/ANSODEngine/ANSOVSEG.cpp index 6b03522..3b796e1 100644 --- a/modules/ANSODEngine/ANSOVSEG.cpp +++ b/modules/ANSODEngine/ANSOVSEG.cpp @@ -29,6 +29,7 @@ namespace ANSCENTER { } bool ANSOVSEG::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -77,6 +78,7 @@ namespace ANSCENTER { } bool ANSOVSEG::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -143,6 +145,7 @@ namespace ANSCENTER { } bool ANSOVSEG::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { std::string openVINOVersion = ov::get_openvino_version().buildNumber; this->_logger.LogDebug("ANSOVSEG::Initialize. OpenVINO version", openVINOVersion, __FILE__, __LINE__); @@ -211,7 +214,7 @@ namespace ANSCENTER { } std::vector ANSOVSEG::RunInference(const cv::Mat& input,const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("ANSOVSEG::RunInference")) return {}; try { // Validation diff --git a/modules/ANSODEngine/ANSPOSE.cpp b/modules/ANSODEngine/ANSPOSE.cpp index 60bc2ca..05a3c5a 100644 --- a/modules/ANSODEngine/ANSPOSE.cpp +++ b/modules/ANSODEngine/ANSPOSE.cpp @@ -33,6 +33,7 @@ namespace ANSCENTER { bool ANSPOSE::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -76,6 +77,7 @@ namespace ANSCENTER { } bool ANSPOSE::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -137,6 +139,7 @@ namespace ANSCENTER { bool ANSPOSE::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { std::string openVINOVersion = ov::get_openvino_version().buildNumber; this->_logger.LogDebug("ANSPOSE::Initialize. OpenVINO version", openVINOVersion, __FILE__, __LINE__); @@ -199,7 +202,7 @@ namespace ANSCENTER { std::vector ANSPOSE::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("ANSPOSE::RunInference")) return {}; try { // Validation diff --git a/modules/ANSODEngine/ANSRTYOLO.cpp b/modules/ANSODEngine/ANSRTYOLO.cpp index a8efec9..7ccb552 100644 --- a/modules/ANSODEngine/ANSRTYOLO.cpp +++ b/modules/ANSODEngine/ANSRTYOLO.cpp @@ -58,6 +58,7 @@ namespace ANSCENTER { bool ANSRTYOLO::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _isFixedBatch = false; bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); @@ -148,6 +149,7 @@ namespace ANSCENTER { std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _isFixedBatch = false; bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, @@ -250,6 +252,7 @@ namespace ANSCENTER { const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; @@ -351,23 +354,7 @@ namespace ANSCENTER { std::vector ANSRTYOLO::RunInference(const cv::Mat& inputImgBGR, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("ANSRTYOLO::RunInference", "Cannot load TensorRT model", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("ANSRTYOLO::RunInference", "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("ANSRTYOLO::RunInference", "Model not initialized", __FILE__, __LINE__); - return {}; - } - if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) - return {}; - } + if (!PreInferenceCheck("ANSRTYOLO::RunInference")) return {}; try { return DetectObjects(inputImgBGR, camera_id); } catch (const std::exception& e) { _logger.LogFatal("ANSRTYOLO::RunInference", e.what(), __FILE__, __LINE__); @@ -377,25 +364,7 @@ namespace ANSCENTER { std::vector> ANSRTYOLO::RunInferencesBatch( const std::vector& inputs, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogFatal("ANSRTYOLO::RunInferencesBatch", "Cannot load the TensorRT model", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogFatal("ANSRTYOLO::RunInferencesBatch", "Runtime license is not valid or expired", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogFatal("ANSRTYOLO::RunInferencesBatch", "Initialisation is not valid", __FILE__, __LINE__); - return {}; - } - if (inputs.empty()) { - _logger.LogFatal("ANSRTYOLO::RunInferencesBatch", "Input images vector is empty", __FILE__, __LINE__); - return {}; - } - } + if (!PreInferenceCheck("ANSRTYOLO::RunInferencesBatch")) return {}; try { if (_isFixedBatch) return ANSODBase::RunInferencesBatch(inputs, camera_id); else return DetectObjectsBatch(inputs, camera_id); diff --git a/modules/ANSODEngine/ANSSAM.cpp b/modules/ANSODEngine/ANSSAM.cpp index e24e1de..e69a2cb 100644 --- a/modules/ANSODEngine/ANSSAM.cpp +++ b/modules/ANSODEngine/ANSSAM.cpp @@ -35,6 +35,7 @@ namespace ANSCENTER { bool ANSSAM::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -67,6 +68,7 @@ namespace ANSCENTER { } bool ANSSAM::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -108,6 +110,7 @@ namespace ANSCENTER { { try { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); _modelConfig = modelConfig; bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -146,7 +149,7 @@ namespace ANSCENTER { } std::vector ANSSAM::RunInference(const cv::Mat& input,const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("ANSSAM::RunInference")) return {}; try { if (!_licenseValid || !_isInitialized || input.empty() || diff --git a/modules/ANSODEngine/ANSSAM3.cpp b/modules/ANSODEngine/ANSSAM3.cpp index 51a3722..1a75980 100644 --- a/modules/ANSODEngine/ANSSAM3.cpp +++ b/modules/ANSODEngine/ANSSAM3.cpp @@ -687,6 +687,7 @@ namespace ANSCENTER std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -746,6 +747,7 @@ namespace ANSCENTER bool ANSSAM3::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -803,6 +805,7 @@ namespace ANSCENTER const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -1010,15 +1013,7 @@ namespace ANSCENTER std::vector ANSSAM3::RunInference(const cv::Mat& input, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid || !_isInitialized) return {}; - if (!m_promptSet) { - _logger.LogError("ANSSAM3::RunInference", "No prompt set", __FILE__, __LINE__); - return {}; - } - if (input.empty() || input.cols < 10 || input.rows < 10) return {}; - } + if (!PreInferenceCheck("ANSSAM3::RunInference")) return {}; try { return Detect(input, camera_id); } diff --git a/modules/ANSODEngine/ANSTENSORRTCL.cpp b/modules/ANSODEngine/ANSTENSORRTCL.cpp index af3532d..7c725cf 100644 --- a/modules/ANSODEngine/ANSTENSORRTCL.cpp +++ b/modules/ANSODEngine/ANSTENSORRTCL.cpp @@ -58,6 +58,7 @@ namespace ANSCENTER } bool TENSORRTCL::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -152,6 +153,7 @@ namespace ANSCENTER } bool TENSORRTCL::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -257,6 +259,7 @@ namespace ANSCENTER const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -360,33 +363,7 @@ namespace ANSCENTER std::vector TENSORRTCL::RunInference(const cv::Mat& inputImgBGR,const std::string& camera_id) { // Validate state under brief lock - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("TENSORRTCL::RunInference", - "Cannot load the TensorRT model. Please check if it exists", - __FILE__, __LINE__); - return {}; - } - - if (!_licenseValid) { - _logger.LogError("TENSORRTCL::RunInference", - "Runtime license is not valid or expired. Please contact ANSCENTER", - __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - _logger.LogError("TENSORRTCL::RunInference", - "Model is not initialized", - __FILE__, __LINE__); - return {}; - } - - if (inputImgBGR.empty() || inputImgBGR.cols < 5 || inputImgBGR.rows < 5) { - return {}; - } - } + if (!PreInferenceCheck("TENSORRTCL::RunInference")) return {}; try { return DetectObjects(inputImgBGR, camera_id); } @@ -397,28 +374,7 @@ namespace ANSCENTER } std::vector> TENSORRTCL::RunInferencesBatch(const std::vector& inputs, const std::string& camera_id) { // Validate state under brief lock - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - this->_logger.LogFatal("TENSORRTCL::RunInferencesBatch", - "Cannot load the TensorRT model. Please check if it exists", __FILE__, __LINE__); - return {}; - } - - if (!_licenseValid) { - this->_logger.LogFatal("TENSORRTCL::RunInferencesBatch", - "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - this->_logger.LogFatal("TENSORRTCL::RunInferencesBatch", - "Engine not initialized", __FILE__, __LINE__); - return {}; - } - - if (inputs.empty()) return {}; - } + if (!PreInferenceCheck("TENSORRTCL::RunInferencesBatch")) return {}; try { return DetectObjectsBatch(inputs, camera_id); } diff --git a/modules/ANSODEngine/ANSTENSORRTPOSE.cpp b/modules/ANSODEngine/ANSTENSORRTPOSE.cpp index f6c1a2c..1aab86f 100644 --- a/modules/ANSODEngine/ANSTENSORRTPOSE.cpp +++ b/modules/ANSODEngine/ANSTENSORRTPOSE.cpp @@ -55,6 +55,7 @@ namespace ANSCENTER } bool ANSTENSORRTPOSE::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -144,6 +145,7 @@ namespace ANSCENTER } bool ANSTENSORRTPOSE::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); @@ -245,6 +247,7 @@ namespace ANSCENTER } bool ANSTENSORRTPOSE::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; @@ -347,35 +350,7 @@ namespace ANSCENTER } std::vector ANSTENSORRTPOSE::RunInference(const cv::Mat& inputImgBGR,const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - - // Validation checks - if (!_modelLoadValid) { - _logger.LogError("ANSTENSORRTPOSE::RunInference", - "Cannot load the TensorRT model. Please check if it exists", - __FILE__, __LINE__); - return {}; - } - - if (!_licenseValid) { - _logger.LogError("ANSTENSORRTPOSE::RunInference", - "Runtime license is not valid or expired. Please contact ANSCENTER", - __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - _logger.LogError("ANSTENSORRTPOSE::RunInference", - "Model is not initialized", - __FILE__, __LINE__); - return {}; - } - - if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) { - return {}; - } - } + if (!PreInferenceCheck("ANSTENSORRTPOSE::RunInference")) return {}; try { return DetectObjects(inputImgBGR, camera_id); } @@ -983,25 +958,7 @@ namespace ANSCENTER std::vector> ANSTENSORRTPOSE::RunInferencesBatch( const std::vector& inputs, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("ANSTENSORRTPOSE::RunInferencesBatch", - "Model not loaded", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("ANSTENSORRTPOSE::RunInferencesBatch", - "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("ANSTENSORRTPOSE::RunInferencesBatch", - "Engine not initialized", __FILE__, __LINE__); - return {}; - } - if (inputs.empty()) return {}; - } + if (!PreInferenceCheck("ANSTENSORRTPOSE::RunInferencesBatch")) return {}; try { return DetectObjectsBatch(inputs, camera_id); } diff --git a/modules/ANSODEngine/ANSTENSORRTSEG.cpp b/modules/ANSODEngine/ANSTENSORRTSEG.cpp index 91eaeb9..dd4b3a6 100644 --- a/modules/ANSODEngine/ANSTENSORRTSEG.cpp +++ b/modules/ANSODEngine/ANSTENSORRTSEG.cpp @@ -55,6 +55,7 @@ namespace ANSCENTER } bool TENSORRTSEG::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -144,6 +145,7 @@ namespace ANSCENTER } bool TENSORRTSEG::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); @@ -245,6 +247,7 @@ namespace ANSCENTER } bool TENSORRTSEG::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; @@ -347,27 +350,7 @@ namespace ANSCENTER } std::vector TENSORRTSEG::RunInference(const cv::Mat& inputImgBGR,const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("TENSORRTSEG::RunInference", - "Cannot load TensorRT model", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("TENSORRTSEG::RunInference", - "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("TENSORRTSEG::RunInference", - "Model not initialized", __FILE__, __LINE__); - return {}; - } - if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) { - return {}; - } - } + if (!PreInferenceCheck("TENSORRTSEG::RunInference")) return {}; try { return DetectObjects(inputImgBGR, camera_id); } @@ -1134,25 +1117,7 @@ namespace ANSCENTER std::vector> TENSORRTSEG::RunInferencesBatch( const std::vector& inputs, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("TENSORRTSEG::RunInferencesBatch", - "Model not loaded", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("TENSORRTSEG::RunInferencesBatch", - "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("TENSORRTSEG::RunInferencesBatch", - "Engine not initialized", __FILE__, __LINE__); - return {}; - } - if (inputs.empty()) return {}; - } + if (!PreInferenceCheck("TENSORRTSEG::RunInferencesBatch")) return {}; try { return DetectObjectsBatch(inputs, camera_id); } diff --git a/modules/ANSODEngine/ANSTENSORTRTOD.cpp b/modules/ANSODEngine/ANSTENSORTRTOD.cpp index 9306a35..ae248e3 100644 --- a/modules/ANSODEngine/ANSTENSORTRTOD.cpp +++ b/modules/ANSODEngine/ANSTENSORTRTOD.cpp @@ -58,6 +58,7 @@ namespace ANSCENTER } bool TENSORRTOD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _isFixedBatch = false; bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); @@ -151,6 +152,7 @@ namespace ANSCENTER } bool TENSORRTOD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { _isFixedBatch = false; @@ -256,6 +258,7 @@ namespace ANSCENTER } bool TENSORRTOD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; @@ -364,31 +367,7 @@ namespace ANSCENTER { // Validate state under a brief lock — do NOT hold across DetectObjects so that // the Engine pool can run concurrent inferences on different GPU slots. - { - std::lock_guard lock(_mutex); - - if (!_modelLoadValid) { - _logger.LogError("TENSORRTOD::RunInference", - "Cannot load TensorRT model", __FILE__, __LINE__); - return {}; - } - - if (!_licenseValid) { - _logger.LogError("TENSORRTOD::RunInference", - "Invalid license", __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - _logger.LogError("TENSORRTOD::RunInference", - "Model not initialized", __FILE__, __LINE__); - return {}; - } - - if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) { - return {}; - } - } // mutex released here — DetectObjects manages its own locking per phase + if (!PreInferenceCheck("TENSORRTOD::RunInference")) return {}; try { return DetectObjects(inputImgBGR, camera_id); @@ -401,34 +380,7 @@ namespace ANSCENTER std::vector> TENSORRTOD::RunInferencesBatch(const std::vector& inputs, const std::string& camera_id) { // Validate state under a brief lock — do NOT hold across DetectObjectsBatch so that // the Engine pool can serve concurrent batch requests on different GPU slots. - { - std::lock_guard lock(_mutex); - // Validate model, license, and initialization - if (!_modelLoadValid) { - this->_logger.LogFatal("TENSORRTOD::RunInferenceBatch", - "Cannot load the TensorRT model. Please check if it exists", __FILE__, __LINE__); - return {}; - } - - if (!_licenseValid) { - this->_logger.LogFatal("TENSORRTOD::RunInferenceBatch", - "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - this->_logger.LogFatal("TENSORRTOD::RunInferenceBatch", - "Initialisation is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - return {}; - } - - // Validate inputs - if (inputs.empty()) { - this->_logger.LogFatal("TENSORRTOD::RunInferenceBatch", - "Input images vector is empty", __FILE__, __LINE__); - return {}; - } - } // mutex released here — DetectObjectsBatch manages its own GPU dispatch + if (!PreInferenceCheck("TENSORRTOD::RunInferencesBatch")) return {}; try { if (_isFixedBatch) return ANSODBase::RunInferencesBatch(inputs, camera_id); diff --git a/modules/ANSODEngine/ANSYOLO12OD.cpp b/modules/ANSODEngine/ANSYOLO12OD.cpp index 982011f..ab9e03b 100644 --- a/modules/ANSODEngine/ANSYOLO12OD.cpp +++ b/modules/ANSODEngine/ANSYOLO12OD.cpp @@ -23,6 +23,7 @@ namespace ANSCENTER { } bool YOLO12OD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -81,6 +82,7 @@ namespace ANSCENTER { } bool YOLO12OD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName,std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className,modelFolder, labelMap); if (!result) return false; @@ -153,6 +155,7 @@ namespace ANSCENTER { } bool YOLO12OD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -223,7 +226,7 @@ namespace ANSCENTER { } std::vector YOLO12OD::RunInference(const cv::Mat& input,const std::string& camera_id) { - std::lock_guard lock(_mutex); + if (!PreInferenceCheck("YOLO12OD::RunInference")) return {}; try { // Validation if (!_licenseValid) { diff --git a/modules/ANSODEngine/ANSYOLOOD.cpp b/modules/ANSODEngine/ANSYOLOOD.cpp index 86ba3cd..df12098 100644 --- a/modules/ANSODEngine/ANSYOLOOD.cpp +++ b/modules/ANSODEngine/ANSYOLOOD.cpp @@ -938,6 +938,7 @@ namespace ANSCENTER } bool YOLOOD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -1030,6 +1031,7 @@ namespace ANSCENTER } bool YOLOOD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap); if (!result) return false; @@ -1152,6 +1154,7 @@ namespace ANSCENTER } bool YOLOOD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap); if (!result) return false; @@ -1266,18 +1269,7 @@ namespace ANSCENTER return RunInference(input, "CustomCam"); } std::vector YOLOOD::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - - // Early validation - before try block - if (!_licenseValid) { - this->_logger.LogError("YOLOOD::RunInference", "Invalid License", __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - this->_logger.LogError("YOLOOD::RunInference", "Model is not initialized", __FILE__, __LINE__); - return {}; - } + if (!PreInferenceCheck("YOLOOD::RunInference")) return {}; if (input.empty() || input.cols < 10 || input.rows < 10) { return {}; diff --git a/modules/ANSODEngine/ANSYOLOV10OVOD.cpp b/modules/ANSODEngine/ANSYOLOV10OVOD.cpp index 1801ab1..ec06b80 100644 --- a/modules/ANSODEngine/ANSYOLOV10OVOD.cpp +++ b/modules/ANSODEngine/ANSYOLOV10OVOD.cpp @@ -33,6 +33,7 @@ namespace ANSCENTER bool ANSOYOLOV10OVOD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -88,6 +89,7 @@ namespace ANSCENTER } bool ANSOYOLOV10OVOD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName,std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig,modelName, className,modelFolder, labelMap); if (!result) return false; @@ -157,6 +159,7 @@ namespace ANSCENTER bool ANSOYOLOV10OVOD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { std::string openVINOVersion = ov::get_openvino_version().buildNumber; std::cout << "OpenVINO version: " << openVINOVersion << std::endl; @@ -222,18 +225,7 @@ namespace ANSCENTER return RunInference(input, "OpenVINO10Cam"); } std::vector ANSOYOLOV10OVOD::RunInference(const cv::Mat& input, const std::string& camera_id) { - std::lock_guard lock(_mutex); - - // Validate license and initialization status - if (!_licenseValid) { - _logger.LogError("ANSOYOLOV10OVOD::RunInference", "Invalid License", __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - _logger.LogError("ANSOYOLOV10OVOD::RunInference", "Model is not initialized", __FILE__, __LINE__); - return {}; - } + if (!PreInferenceCheck("ANSOYOLOV10OVOD::RunInference")) return {}; // Validate input if (input.empty()) { diff --git a/modules/ANSODEngine/ANSYOLOV10RTOD.cpp b/modules/ANSODEngine/ANSYOLOV10RTOD.cpp index d16c184..b474e97 100644 --- a/modules/ANSODEngine/ANSYOLOV10RTOD.cpp +++ b/modules/ANSODEngine/ANSYOLOV10RTOD.cpp @@ -56,6 +56,7 @@ namespace ANSCENTER } bool ANSYOLOV10RTOD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -154,6 +155,7 @@ namespace ANSCENTER } bool ANSYOLOV10RTOD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig,modelName, className, modelFolder, labelMap); if (!result) return false; @@ -260,6 +262,7 @@ namespace ANSCENTER bool ANSYOLOV10RTOD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; @@ -362,27 +365,7 @@ namespace ANSCENTER std::vector ANSYOLOV10RTOD::RunInference(const cv::Mat& inputImgBGR, const std::string& camera_id) { // Validate under brief lock - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - this->_logger.LogFatal("ANSYOLOV10RTOD::RunInference", - "Cannot load the TensorRT model. Please check if it is exist", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - this->_logger.LogFatal("ANSYOLOV10RTOD::RunInference", - "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - this->_logger.LogFatal("ANSYOLOV10RTOD::RunInference", - "Initialisation is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - return {}; - } - if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) { - return {}; - } - } + if (!PreInferenceCheck("ANSYOLOV10RTOD::RunInference")) return {}; try { return DetectObjects(inputImgBGR, camera_id); } @@ -1000,25 +983,7 @@ namespace ANSCENTER std::vector> ANSYOLOV10RTOD::RunInferencesBatch( const std::vector& inputs, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("ANSYOLOV10RTOD::RunInferencesBatch", - "Model not loaded", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("ANSYOLOV10RTOD::RunInferencesBatch", - "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("ANSYOLOV10RTOD::RunInferencesBatch", - "Engine not initialized", __FILE__, __LINE__); - return {}; - } - if (inputs.empty()) return {}; - } + if (!PreInferenceCheck("ANSYOLOV10RTOD::RunInferencesBatch")) return {}; try { return DetectObjectsBatch(inputs, camera_id); } diff --git a/modules/ANSODEngine/ANSYOLOV12RTOD.cpp b/modules/ANSODEngine/ANSYOLOV12RTOD.cpp index e8103c1..012f864 100644 --- a/modules/ANSODEngine/ANSYOLOV12RTOD.cpp +++ b/modules/ANSODEngine/ANSYOLOV12RTOD.cpp @@ -55,6 +55,7 @@ namespace ANSCENTER } bool ANSYOLOV12RTOD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword); if (!result) return false; @@ -147,6 +148,7 @@ namespace ANSCENTER } bool ANSYOLOV12RTOD::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName,std::string className, const std::string& modelFolder, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig,modelName, className,modelFolder, labelMap); if (!result) return false; @@ -248,6 +250,7 @@ namespace ANSCENTER } bool ANSYOLOV12RTOD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) { std::lock_guard lock(_mutex); + ModelLoadingGuard mlg(_modelLoading); try { const bool engineAlreadyLoaded = _modelLoadValid && _isInitialized && m_trtEngine != nullptr; _modelLoadValid = false; @@ -343,35 +346,11 @@ namespace ANSCENTER return RunInference(inputImgBGR, "TensorRT12Cam"); } std::vector ANSYOLOV12RTOD::RunInference(const cv::Mat& inputImgBGR, const std::string& camera_id) { - std::lock_guard lock(_mutex); - - // Validate model, license, and initialization - if (!_modelLoadValid) { - this->_logger.LogFatal("ANSYOLOV12RTOD::RunInference", - "Cannot load the TensorRT model. Please check if it is exist", __FILE__, __LINE__); + if (!PreInferenceCheck("ANSYOLOV12RTOD::RunInference")) return {}; + if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) return {}; - } - - if (!_licenseValid) { - this->_logger.LogFatal("ANSYOLOV12RTOD::RunInference", - "Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__); - return {}; - } - - if (!_isInitialized) { - this->_logger.LogFatal("ANSYOLOV12RTOD::RunInference", - "Model is not initialized", __FILE__, __LINE__); - return {}; - } - - // Validate input - if (inputImgBGR.empty() || inputImgBGR.cols < 10 || inputImgBGR.rows < 10) { - return {}; - } - try { return DetectObjects(inputImgBGR, camera_id); - } catch (const std::exception& e) { this->_logger.LogFatal("ANSYOLOV12RTOD::RunInference", e.what(), __FILE__, __LINE__); @@ -919,22 +898,7 @@ namespace ANSCENTER std::vector> ANSYOLOV12RTOD::RunInferencesBatch( const std::vector& inputs, const std::string& camera_id) { - { - std::lock_guard lock(_mutex); - if (!_modelLoadValid) { - _logger.LogError("ANSYOLOV12RTOD::RunInferencesBatch", "Model not loaded", __FILE__, __LINE__); - return {}; - } - if (!_licenseValid) { - _logger.LogError("ANSYOLOV12RTOD::RunInferencesBatch", "Invalid license", __FILE__, __LINE__); - return {}; - } - if (!_isInitialized) { - _logger.LogError("ANSYOLOV12RTOD::RunInferencesBatch", "Engine not initialized", __FILE__, __LINE__); - return {}; - } - if (inputs.empty()) return {}; - } + if (!PreInferenceCheck("ANSYOLOV12RTOD::RunInferencesBatch")) return {}; try { return DetectObjectsBatch(inputs, camera_id); }