Refactor project structure
This commit is contained in:
757
modules/ANSODEngine/ANSCUSTOMPY.cpp
Normal file
757
modules/ANSODEngine/ANSCUSTOMPY.cpp
Normal file
@@ -0,0 +1,757 @@
|
||||
#include "ANSCUSTOMPY.h"
|
||||
#include <filesystem>
|
||||
#include <json.hpp>
|
||||
using json = nlohmann::json;
|
||||
namespace fs = std::filesystem;
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace {
|
||||
using ThreadLocalModelMap = std::unordered_map<int, std::shared_ptr<ANSCENTER::ModelWrapper>>;
|
||||
ThreadLocalModelMap& GetThreadLocalState() {
|
||||
thread_local ThreadLocalModelMap threadState;
|
||||
return threadState;
|
||||
}
|
||||
}
|
||||
std::string CustomParametersToJson(const ANSCENTER::Params& params) {
|
||||
json root;
|
||||
// ROI_Config
|
||||
for (const auto& cfg : params.ROI_Config) {
|
||||
json j;
|
||||
j["Rectangle"] = cfg.Rectangle;
|
||||
j["Polygon"] = cfg.Polygon;
|
||||
j["Line"] = cfg.Line;
|
||||
j["MinItems"] = cfg.MinItems;
|
||||
j["MaxItems"] = cfg.MaxItems;
|
||||
j["Name"] = cfg.Name;
|
||||
j["ROI-Match"] = cfg.ROIMatch;
|
||||
root["ROI_Config"].push_back(j);
|
||||
}
|
||||
|
||||
// ROI_Options
|
||||
for (const auto& opt : params.ROI_Options) {
|
||||
root["ROI_Options"].push_back(opt);
|
||||
}
|
||||
|
||||
// Parameters
|
||||
for (const auto& param : params.Parameters) {
|
||||
json j;
|
||||
j["Name"] = param.Name;
|
||||
j["DataType"] = param.DataType;
|
||||
j["NoOfdecimals"] = param.NoOfDecimals;
|
||||
j["MaxValue"] = param.MaxValue;
|
||||
j["MinValue"] = param.MinValue;
|
||||
j["StartValue"] = param.StartValue;
|
||||
j["ListItems"] = param.ListItems;
|
||||
j["DefaultValue"] = param.DefaultValue;
|
||||
j["Value"] = param.Value;
|
||||
root["Parameters"].push_back(j);
|
||||
}
|
||||
|
||||
// ROI_Values
|
||||
for (const auto& rv : params.ROI_Values) {
|
||||
json j;
|
||||
j["ROI-Match"] = rv.ROIMatch;
|
||||
|
||||
for (const auto& pt : rv.ROIPoints) {
|
||||
j["ROIPoints"].push_back({ {"x", pt.x}, {"y", pt.y} });
|
||||
}
|
||||
|
||||
j["Option"] = rv.Option;
|
||||
j["Name"] = rv.Name;
|
||||
j["OriginalImageSize"] = rv.OriginalImageSize;
|
||||
root["ROI_Values"].push_back(j);
|
||||
}
|
||||
|
||||
// Convert to compact JSON string (no whitespace)
|
||||
std::string jsonString = root.dump(); // dump() without indent = compact
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
namespace ANSCENTER
|
||||
{
|
||||
std::once_flag PythonRuntime::initFlag;
|
||||
std::unique_ptr<PythonRuntime> PythonRuntime::instance = nullptr;
|
||||
std::atomic<bool> PythonRuntime::_isInitialized{ false };
|
||||
std::atomic<bool> PythonRuntime::shuttingDown{ false };
|
||||
std::mutex PythonRuntime::_instanceMutex;
|
||||
|
||||
std::mutex ANSCUSTOMPY::_globalInstancesMutex;
|
||||
std::unordered_set<ANSCUSTOMPY*> ANSCUSTOMPY::_globalInstances;
|
||||
std::atomic<int> ANSCUSTOMPY::globalInstanceCounter{ 1 };
|
||||
|
||||
// Declare GetPythonRuntime as a friend function of PythonRuntime
|
||||
PythonRuntime& GetPythonRuntime(const std::wstring& pythonHome);
|
||||
|
||||
PythonRuntime& GetPythonRuntime(const std::wstring& pythonHome) {
|
||||
std::call_once(PythonRuntime::initFlag, [&]() {
|
||||
std::lock_guard<std::mutex> lock(PythonRuntime::_instanceMutex);
|
||||
PythonRuntime::instance = std::unique_ptr<PythonRuntime>(new PythonRuntime(pythonHome));
|
||||
});
|
||||
return *PythonRuntime::instance;
|
||||
}
|
||||
|
||||
PythonRuntime::PythonRuntime(const std::wstring& home) : pythonHome(home) {
|
||||
InitializeInterpreter();
|
||||
}
|
||||
|
||||
bool PythonRuntime::IsInitialized() {
|
||||
return _isInitialized.load(std::memory_order_acquire) && Py_IsInitialized();
|
||||
}
|
||||
|
||||
void PythonRuntime::InitializeInterpreter() {
|
||||
if (!pythonHome.empty()) {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
Py_SetPythonHome(pythonHome.c_str());
|
||||
#pragma warning(pop)
|
||||
}
|
||||
py::initialize_interpreter();
|
||||
_isInitialized.store(true, std::memory_order_release);
|
||||
PatchPythonStreamsSafe();
|
||||
}
|
||||
|
||||
void PythonRuntime::PatchPythonStreamsSafe() {
|
||||
try {
|
||||
py::exec(R"(
|
||||
import sys, io
|
||||
class DummyStream(io.StringIO):
|
||||
def write(self, txt): pass
|
||||
def flush(self): pass
|
||||
if sys.stdout is None or not hasattr(sys.stdout, "write"):
|
||||
sys.stdout = DummyStream()
|
||||
if sys.stderr is None or not hasattr(sys.stderr, "write"):
|
||||
sys.stderr = DummyStream()
|
||||
)");
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
std::cerr << "[PatchPythonStreamsSafe] Python error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PythonRuntime::AddToSysPath(const std::string& path) {
|
||||
if (!IsInitialized()) {
|
||||
std::cerr << "[PythonRuntime::AddToSysPath] Interpreter not initialized!\n";
|
||||
return;
|
||||
}
|
||||
py::gil_scoped_acquire gil;
|
||||
py::module_ sys = py::module_::import("sys");
|
||||
sys.attr("path").attr("insert")(0, path);
|
||||
}
|
||||
|
||||
void PythonRuntime::Shutdown() {
|
||||
std::lock_guard<std::mutex> lock(_instanceMutex);
|
||||
if (shuttingDown.exchange(true)) return;
|
||||
if (_isInitialized.load(std::memory_order_acquire)) {
|
||||
try {
|
||||
py::gil_scoped_acquire gil;
|
||||
py::module::import("gc").attr("collect")();
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "[PythonRuntime::Shutdown] GC failed (ignored)." << std::endl;
|
||||
}
|
||||
try {
|
||||
py::finalize_interpreter();
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "[PythonRuntime::Shutdown] finalize_interpreter failed." << std::endl;
|
||||
}
|
||||
_isInitialized.store(false, std::memory_order_release);
|
||||
}
|
||||
instance.reset();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// /// Initializes the ANSCUSTOMPY model with the given parameters.
|
||||
/// </summary>
|
||||
ANSCUSTOMPY::ANSCUSTOMPY() {
|
||||
_instanceId = globalInstanceCounter.fetch_add(1, std::memory_order_relaxed);
|
||||
_destroyed = false;
|
||||
_isInitialized = false;
|
||||
}
|
||||
ANSCUSTOMPY::~ANSCUSTOMPY() {
|
||||
try {
|
||||
if (PythonRuntime::IsInitialized())
|
||||
Destroy();
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "[~ANSCUSTOMPY] Exception during destruction." << std::endl;
|
||||
}
|
||||
}
|
||||
void ANSCUSTOMPY::RegisterSelf() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_globalInstancesMutex);
|
||||
_globalInstances.insert(this);
|
||||
}
|
||||
auto& threadState = GetThreadLocalState();
|
||||
if (!_wrapper || !_wrapper->modelFactory) return;
|
||||
if (threadState.find(_instanceId) == threadState.end()) {
|
||||
if (_wrapper && _wrapper->modelFactory) {
|
||||
py::gil_scoped_acquire gil;
|
||||
auto newWrapper = std::make_shared<ModelWrapper>();
|
||||
newWrapper->modelFactory = _wrapper->modelFactory;
|
||||
newWrapper->map[0] = std::make_shared<py::object>(_wrapper->modelFactory());
|
||||
threadState[_instanceId] = newWrapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
void ANSCUSTOMPY::UnregisterSelf() {
|
||||
std::lock_guard<std::mutex> lock(_globalInstancesMutex);
|
||||
_globalInstances.erase(this);
|
||||
}
|
||||
void ANSCUSTOMPY::ClearThreadState() {
|
||||
auto& threadState = GetThreadLocalState();
|
||||
auto it = threadState.find(_instanceId);
|
||||
if (it != threadState.end()) {
|
||||
if (PythonRuntime::IsInitialized()) {
|
||||
py::gil_scoped_acquire gil;
|
||||
it->second->map.clear();
|
||||
}
|
||||
else {
|
||||
it->second->map.clear();
|
||||
}
|
||||
threadState.erase(it);
|
||||
}
|
||||
}
|
||||
bool ANSCUSTOMPY::EnsureModelReady(const std::string& methodName) {
|
||||
if (!_isInitialized || _destroyed || !PythonRuntime::IsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
//auto& threadState = GetThreadLocalState();
|
||||
//if (threadState.find(_instanceId) == threadState.end()) {
|
||||
// if (_wrapper && _wrapper->modelFactory) {
|
||||
// py::gil_scoped_acquire gil;
|
||||
// auto newWrapper = std::make_shared<ModelWrapper>();
|
||||
// newWrapper->modelFactory = _wrapper->modelFactory;
|
||||
// newWrapper->map[0] = std::make_shared<py::object>(_wrapper->modelFactory());
|
||||
// threadState[_instanceId] = newWrapper;
|
||||
// }
|
||||
// else {
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
py::object ANSCUSTOMPY::GetThreadLocalModel() {
|
||||
auto& threadState = GetThreadLocalState();
|
||||
auto it = threadState.find(_instanceId);
|
||||
if (_destroyed || it == threadState.end() || !it->second) {
|
||||
return py::none();
|
||||
}
|
||||
auto& modelMap = it->second->map;
|
||||
auto modelIt = modelMap.find(0);
|
||||
if (modelIt == modelMap.end()) {
|
||||
py::gil_scoped_acquire gil;
|
||||
auto obj = it->second->modelFactory();
|
||||
if (obj.is_none()) return py::none();
|
||||
auto wrapped = std::make_shared<py::object>(obj);
|
||||
modelMap[0] = wrapped;
|
||||
return *wrapped;
|
||||
}
|
||||
return *modelIt->second;
|
||||
}
|
||||
bool ANSCUSTOMPY::Destroy() {
|
||||
//std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
if (_destroyed) return true;
|
||||
_destroyed = true;
|
||||
ClearThreadState();
|
||||
UnregisterSelf();
|
||||
return true;
|
||||
}
|
||||
void ANSCUSTOMPY::SafeShutdownAll(bool waitForShutdown) {
|
||||
std::unordered_set<ANSCUSTOMPY*> instancesCopy;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_globalInstancesMutex);
|
||||
instancesCopy = _globalInstances;
|
||||
}
|
||||
for (ANSCUSTOMPY* instance : instancesCopy) {
|
||||
if (instance) {
|
||||
try {
|
||||
instance->ClearThreadState();
|
||||
instance->Destroy();
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "[SafeShutdownAll] Destroy failed." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
auto shutdownFunc = []() {
|
||||
try {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
int retry = 0;
|
||||
while (!ANSCUSTOMPY::_globalInstances.empty() && retry++ < 100) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
if (PythonRuntime::IsInitialized()) {
|
||||
py::gil_scoped_acquire gil;
|
||||
py::module::import("gc").attr("collect")();
|
||||
PythonRuntime::Shutdown();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "[SafeShutdownAll] Shutdown failed." << std::endl;
|
||||
}
|
||||
};
|
||||
if (waitForShutdown) shutdownFunc();
|
||||
else std::thread(shutdownFunc).detach();
|
||||
}
|
||||
|
||||
|
||||
bool ANSCUSTOMPY::InitializePythonModel(const std::string& moduleName, const std::string& fullPath) {
|
||||
if (!PythonRuntime::IsInitialized()) {
|
||||
_logger.LogError("[InitializePythonModel] Python error:\n", "Python interpreter is not initialized.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::gil_scoped_acquire gil;
|
||||
try {
|
||||
py::exec("import logging; logging.getLogger('ultralytics').setLevel(logging.CRITICAL)", py::globals());
|
||||
py::dict locals;
|
||||
std::ostringstream code;
|
||||
code << "import importlib.util\n"
|
||||
<< "spec = importlib.util.spec_from_file_location('" << moduleName << "', r'" << fullPath << "')\n"
|
||||
<< "mod = importlib.util.module_from_spec(spec)\n"
|
||||
<< "spec.loader.exec_module(mod)\n";
|
||||
|
||||
py::exec(code.str(), py::globals(), locals);
|
||||
py::object mod = locals["mod"];
|
||||
py::object modelClass = mod.attr("ANSModel");
|
||||
|
||||
auto& threadState = GetThreadLocalState();
|
||||
auto& wrapper = threadState[_instanceId]; // ensure per-thread-per-instance
|
||||
if (!wrapper) wrapper = std::make_shared<ModelWrapper>();
|
||||
|
||||
wrapper->modelFactory = [modelClass]() {
|
||||
py::gil_scoped_acquire gil;
|
||||
py::object instance = modelClass();
|
||||
return instance;};
|
||||
_wrapper = wrapper; // Store for main thread access too
|
||||
return true;
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("InitializePythonModel", std::string("Python error: ") + e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSCUSTOMPY::Initialize(std::string licenseKey, ModelConfig modelConfig,
|
||||
const std::string& modelZipFilePath, const std::string& modelZipPassword,
|
||||
std::string& labelMap) {
|
||||
//std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
// Step 1: Base model init
|
||||
bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
|
||||
if (!result) return false;
|
||||
|
||||
_modelConfig = modelConfig;
|
||||
_modelConfig.inpHeight = 640;
|
||||
_modelConfig.inpWidth = 640;
|
||||
if (_modelConfig.modelMNSThreshold < 0.2) _modelConfig.modelMNSThreshold = 0.5;
|
||||
if (_modelConfig.modelConfThreshold < 0.2) _modelConfig.modelConfThreshold = 0.5;
|
||||
|
||||
// Step 2: Validate model folder
|
||||
if (_modelFolder.empty()) {
|
||||
_logger.LogError("ANSCUSTOMPY::Initialize", "_modelFolder is not set.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: Set Python Home
|
||||
std::wstring pythonHome;
|
||||
const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
|
||||
const fs::path sharedDll = sharedPython / "python311.dll";
|
||||
|
||||
if (fs::exists(sharedDll)) {
|
||||
pythonHome = sharedPython.wstring();
|
||||
}
|
||||
else {
|
||||
pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
|
||||
}
|
||||
|
||||
// Step 4: Initialize Python and sys.path
|
||||
auto& runtime = GetPythonRuntime(pythonHome);
|
||||
runtime.AddToSysPath(_modelFolder);
|
||||
|
||||
// Step 5: Load Python model class from unique module
|
||||
//_scriptPath = _modelFolder + "/ansinfer.py";
|
||||
_scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
|
||||
_moduleName = "ansinfer_" + std::to_string(_instanceId);
|
||||
|
||||
if (!InitializePythonModel(_moduleName, _scriptPath)) {
|
||||
_logger.LogError("ANSCUSTOMPY::Initialize", "Failed to load Python model.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Load class names
|
||||
_classFilePath = CreateFilePath(_modelFolder, "classes.names");
|
||||
std::ifstream isValidFileName(_classFilePath);
|
||||
if (!isValidFileName) {
|
||||
_logger.LogDebug("ANSCUSTOMPY::LoadModel. Load classes from string", _classFilePath, __FILE__, __LINE__);
|
||||
LoadClassesFromString();
|
||||
}
|
||||
else {
|
||||
_logger.LogDebug("ANSCUSTOMPY::LoadModel. Load classes from file", _classFilePath, __FILE__, __LINE__);
|
||||
LoadClassesFromFile();
|
||||
}
|
||||
|
||||
labelMap.clear();
|
||||
if (!_classes.empty())
|
||||
labelMap = VectorToCommaSeparatedString(_classes);
|
||||
|
||||
_destroyed = false;
|
||||
_isInitialized = true;
|
||||
RegisterSelf();
|
||||
return true;
|
||||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::Initialize", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSCUSTOMPY::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
|
||||
//std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
// Step 0: Assign unique instance ID
|
||||
if (!ANSODBase::LoadModel(modelZipFilePath, modelZipPassword))
|
||||
return false;
|
||||
|
||||
if (_modelFolder.empty()) {
|
||||
_logger.LogError("ANSCUSTOMPY::LoadModel", "_modelFolder is not set.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: Set Python home
|
||||
std::wstring pythonHome;
|
||||
const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
|
||||
const fs::path sharedDll = sharedPython / "python311.dll";
|
||||
|
||||
if (fs::exists(sharedDll)) {
|
||||
pythonHome = sharedPython.wstring();
|
||||
}
|
||||
else {
|
||||
pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
|
||||
}
|
||||
|
||||
// Step 4: Initialize Python and sys.path
|
||||
auto& runtime = GetPythonRuntime(pythonHome);
|
||||
runtime.AddToSysPath(_modelFolder);
|
||||
|
||||
// Step 5: Load Python model class from unique module
|
||||
//_scriptPath = _modelFolder + "/ansinfer.py";
|
||||
_scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
|
||||
_moduleName = "ansinfer_" + std::to_string(_instanceId);
|
||||
|
||||
if (!InitializePythonModel(_moduleName, _scriptPath)) {
|
||||
_logger.LogError("ANSCUSTOMPY::LoadModel", "Failed to load Python model.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
_destroyed = false;
|
||||
_isInitialized = true;
|
||||
RegisterSelf();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::LoadModel", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSCUSTOMPY::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig,
|
||||
std::string modelName, std::string className,
|
||||
const std::string& modelFolder,
|
||||
std::string& labelMap)
|
||||
{
|
||||
//std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
// Step 0: Assign unique instance ID early
|
||||
if (!ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap))
|
||||
return false;
|
||||
_modelConfig = modelConfig;
|
||||
_modelConfig.inpHeight = 640;
|
||||
_modelConfig.inpWidth = 640;
|
||||
if (_modelConfig.modelMNSThreshold < 0.2) _modelConfig.modelMNSThreshold = 0.5;
|
||||
if (_modelConfig.modelConfThreshold < 0.2) _modelConfig.modelConfThreshold = 0.5;
|
||||
|
||||
// Step 2: Validate model folder
|
||||
if (_modelFolder.empty()) {
|
||||
_logger.LogError("ANSCUSTOMPY::Initialize", "_modelFolder is not set.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: Set Python home
|
||||
std::wstring pythonHome;
|
||||
const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
|
||||
const fs::path sharedDll = sharedPython / "python311.dll";
|
||||
|
||||
if (fs::exists(sharedDll)) {
|
||||
pythonHome = sharedPython.wstring();
|
||||
}
|
||||
else {
|
||||
pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
|
||||
}
|
||||
|
||||
// Step 4: Initialize Python and sys.path
|
||||
auto& runtime = GetPythonRuntime(pythonHome);
|
||||
runtime.AddToSysPath(_modelFolder);
|
||||
|
||||
// Step 5: Load Python model class from unique module
|
||||
//_scriptPath = _modelFolder + "/ansinfer.py";
|
||||
_scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
|
||||
_moduleName = "ansinfer_" + std::to_string(_instanceId);
|
||||
|
||||
if (!InitializePythonModel(_moduleName, _scriptPath)) {
|
||||
_logger.LogError("ANSCUSTOMPY::Initialize", "Failed to load Python model.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: Load class labels
|
||||
_classFilePath = CreateFilePath(_modelFolder, "classes.names");
|
||||
std::ifstream isValidFileName(_classFilePath);
|
||||
if (!isValidFileName) {
|
||||
_logger.LogDebug("ANSCUSTOMPY::LoadModel", "Load classes from string", __FILE__, __LINE__);
|
||||
LoadClassesFromString();
|
||||
}
|
||||
else {
|
||||
_logger.LogDebug("ANSCUSTOMPY::LoadModel", "Load classes from file", __FILE__, __LINE__);
|
||||
LoadClassesFromFile();
|
||||
}
|
||||
|
||||
labelMap.clear();
|
||||
if (!_classes.empty())
|
||||
labelMap = VectorToCommaSeparatedString(_classes);
|
||||
_destroyed = false;
|
||||
_isInitialized = true;
|
||||
RegisterSelf();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::LoadModelFromFolder", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<Object> ANSCUSTOMPY::RunInference(const cv::Mat& input) {
|
||||
return RunInference(input, "CustomPyCam");
|
||||
}
|
||||
std::vector<Object> ANSCUSTOMPY::RunInference(const cv::Mat& input, const std::string& camera_id) {
|
||||
std::vector<Object> results;
|
||||
if (!_isInitialized) {
|
||||
_logger.LogError("ANSCUSTOMPY::RunInference", "Model is not initialized.", __FILE__, __LINE__);
|
||||
return results;
|
||||
}
|
||||
if (!_licenseValid) {
|
||||
_logger.LogError("ANSCUSTOMPY::RunInference", "Invalid License", __FILE__, __LINE__);
|
||||
return results;
|
||||
}
|
||||
if (input.empty()) {
|
||||
_logger.LogError("ANSCUSTOMPY::RunInference", "Input image is empty.", __FILE__, __LINE__);
|
||||
return results;
|
||||
}
|
||||
try {
|
||||
std::string jsonStr = RunPythonInferenceFromMat(input);
|
||||
if (!jsonStr.empty()) {
|
||||
results = ANSUtilityHelper::GetDetectionsFromString(jsonStr);
|
||||
}
|
||||
else {
|
||||
_logger.LogWarn("ANSCUSTOMPY::RunInference", "Empty JSON result from Python inference.", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::RunInference", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
std::string ANSCUSTOMPY::RunPythonInferenceFromMat(const cv::Mat& image) {
|
||||
if (!EnsureModelReady("RunPythonInferenceFromMat"))
|
||||
return "";
|
||||
if (image.empty())
|
||||
return "";
|
||||
|
||||
try {
|
||||
py::object model = GetThreadLocalModel();
|
||||
if (model.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::RunPythonInferenceFromMat", "Model is None.", __FILE__, __LINE__);
|
||||
return "";
|
||||
}
|
||||
py::gil_scoped_acquire gil;
|
||||
if (!py::hasattr(model, "run_inference")) {
|
||||
_logger.LogError("ANSCUSTOMPY::RunPythonInferenceFromMat", "Model has no 'run_inference' method.", __FILE__, __LINE__);
|
||||
return "";
|
||||
}
|
||||
py::bytes image_bytes(reinterpret_cast<const char*>(image.data), image.total() * image.elemSize());
|
||||
py::object result = model.attr("run_inference")(image_bytes, image.cols, image.rows, image.channels());
|
||||
return result.cast<std::string>();
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("RunPythonInferenceFromMat", e.what(), __FILE__, __LINE__);
|
||||
return "";
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("RunPythonInferenceFromMat", e.what(), __FILE__, __LINE__);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSCUSTOMPY::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
|
||||
if (!EnsureModelReady("OptimizeModel")) return false;
|
||||
try {
|
||||
py::object model = GetThreadLocalModel();
|
||||
if (model.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Model is None.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::gil_scoped_acquire gil;
|
||||
if (!py::hasattr(model, "model_optimize")) {
|
||||
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Python model has no method 'model_optimize'.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::object result;
|
||||
try {
|
||||
result = model.attr("model_optimize")(fp16);
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("ANSCUSTOMPY::OptimizeModel", std::string("Exception in 'model_optimize': ") + e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Python 'model_optimize' returned None.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!py::isinstance<py::bool_>(result)) {
|
||||
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Unexpected return type from 'model_optimize'. Expected bool.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = result.cast<bool>();
|
||||
if (success) {
|
||||
optimizedModelFolder = fs::path(_scriptPath).parent_path().string();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("ANSCUSTOMPY::OptimizeModel", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::OptimizeModel", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ANSCUSTOMPY::ConfigureParameters(Params& param) {
|
||||
if (!EnsureModelReady("ConfigureParamaters")) return false;
|
||||
|
||||
try {
|
||||
py::object model = GetThreadLocalModel();
|
||||
if (model.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Model is None.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::gil_scoped_acquire gil;
|
||||
if (!py::hasattr(model, "configureParameters")) {
|
||||
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Python model missing 'configureParameters' method.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::object result;
|
||||
try {
|
||||
result = model.attr("configureParameters")();
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", std::string("Exception in 'configureParameters': ") + e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Returned None from Python method.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!py::isinstance<py::str>(result)) {
|
||||
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Expected Python string return from 'configureParameters'.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string jsonStr = result.cast<std::string>();
|
||||
if (jsonStr.empty()) {
|
||||
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Empty JSON string returned from Python.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
param = ANSUtilityHelper::ParseCustomParameters(jsonStr);
|
||||
return true;
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ANSCUSTOMPY::SetParameters(const Params& param) {
|
||||
if (!EnsureModelReady("SetParamaters")) return false;
|
||||
try {
|
||||
std::string jsonStr = CustomParametersToJson(param);
|
||||
if (jsonStr.empty()) {
|
||||
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Serialized parameter string is empty.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::object model = GetThreadLocalModel();
|
||||
if (model.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Model is None.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::gil_scoped_acquire gil;
|
||||
if (!py::hasattr(model, "setParameters")) {
|
||||
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Python model has no method 'setParameters'.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
py::object result;
|
||||
try {
|
||||
result = model.attr("setParameters")(jsonStr);
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("ANSCUSTOMPY::SetParamaters", std::string("Exception in 'setParameters': ") + e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.is_none()) {
|
||||
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Python method returned None.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!py::isinstance<py::bool_>(result)) {
|
||||
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Python method returned non-boolean type.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.cast<bool>();
|
||||
}
|
||||
catch (const py::error_already_set& e) {
|
||||
PyErr_Print();
|
||||
_logger.LogFatal("ANSCUSTOMPY::SetParamaters", std::string("Python error: ") + e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogFatal("ANSCUSTOMPY::SetParamaters", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user