// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include "ANSOCRBase.h" #include "ANSCpuOCR.h" #include "ANSOnnxOCR.h" #include "ANSRtOCR.h" #include "ANSLibsLoader.h" #include "ANSGpuFrameRegistry.h" #include #include "NV12PreprocessHelper.h" #include "engine/TRTEngineCache.h" #include "engine/EnginePoolManager.h" #include #include #include // Handle registry with refcount — prevents use-after-free when // ReleaseANSOCRHandle is called while inference is still running. static std::unordered_map& OCRHandleRegistry() { static std::unordered_map s; return s; } static std::mutex& OCRHandleRegistryMutex() { static std::mutex m; return m; } static std::condition_variable& OCRHandleRegistryCV() { static std::condition_variable cv; return cv; } static void RegisterOCRHandle(ANSCENTER::ANSOCRBase* h) { std::lock_guard lk(OCRHandleRegistryMutex()); OCRHandleRegistry()[h] = 1; // refcount = 1 } static ANSCENTER::ANSOCRBase* AcquireOCRHandle(ANSCENTER::ANSOCRBase* h) { std::lock_guard lk(OCRHandleRegistryMutex()); auto it = OCRHandleRegistry().find(h); if (it == OCRHandleRegistry().end()) return nullptr; it->second++; return h; } static bool ReleaseOCRHandleRef(ANSCENTER::ANSOCRBase* h) { std::lock_guard lk(OCRHandleRegistryMutex()); auto it = OCRHandleRegistry().find(h); if (it == OCRHandleRegistry().end()) return false; it->second--; if (it->second <= 0) { OCRHandleRegistry().erase(it); OCRHandleRegistryCV().notify_all(); return true; } return false; } static bool UnregisterOCRHandle(ANSCENTER::ANSOCRBase* h) { std::unique_lock lk(OCRHandleRegistryMutex()); auto it = OCRHandleRegistry().find(h); if (it == OCRHandleRegistry().end()) return false; it->second--; bool ok = OCRHandleRegistryCV().wait_for(lk, std::chrono::seconds(5), [&]() { auto it2 = OCRHandleRegistry().find(h); return it2 == OCRHandleRegistry().end() || it2->second <= 0; }); if (!ok) { OutputDebugStringA("WARNING: UnregisterOCRHandle timed out waiting for in-flight inference\n"); } OCRHandleRegistry().erase(h); return true; } // RAII guard — ensures ReleaseOCRHandleRef is always called, preventing // refcount leaks that would cause UnregisterOCRHandle to deadlock. class OCRHandleGuard { ANSCENTER::ANSOCRBase* engine; public: explicit OCRHandleGuard(ANSCENTER::ANSOCRBase* e) : engine(e) {} ~OCRHandleGuard() { if (engine) ReleaseOCRHandleRef(engine); } ANSCENTER::ANSOCRBase* get() const { return engine; } explicit operator bool() const { return engine != nullptr; } OCRHandleGuard(const OCRHandleGuard&) = delete; OCRHandleGuard& operator=(const OCRHandleGuard&) = delete; }; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: // When lpReserved != NULL, the process is terminating via ExitProcess. // The OS has already killed all worker threads (idle timers, CUDA // threads, etc.). Set the global flag so atexit destructors skip // thread joins and CUDA/TRT cleanup that would fail on a dead context. if (lpReserved != nullptr) { g_processExiting().store(true, std::memory_order_relaxed); break; } // Dynamic FreeLibrary — threads are still alive, safe to clean up. try { std::vector leakedHandles; { std::lock_guard lk(OCRHandleRegistryMutex()); for (auto& [h, _] : OCRHandleRegistry()) leakedHandles.push_back(h); OCRHandleRegistry().clear(); } for (auto* h : leakedHandles) { try { h->Destroy(); delete h; } catch (...) {} } try { EnginePoolManager::instance().clearAll(); } catch (...) {} try { TRTEngineCache::instance().clearAll(); } catch (...) {} } catch (...) {} break; } return TRUE; } // Extended version with limitSideLen parameter — new callers should use this. extern "C" ANSOCR_API int CreateANSOCRHandleEx(ANSCENTER::ANSOCRBase** Handle, const char* licenseKey, const char* modelFilePath, const char* modelFileZipPassword, int language, int engineMode, // 0: autodetect, 1 gpu, 2 cpu int gpuId, double detectorDBThreshold, double detectorDBBoxThreshold, double detectorDBUnclipRatio, double classifierThreshold, int useDilation, int limitSideLen) { try { // Ensure all shared DLLs (OpenCV, OpenVINO, TRT, ORT) are pre-loaded ANSCENTER::ANSLibsLoader::Initialize(); ANSCENTER::EngineType engineType = ANSCENTER::ANSLicenseHelper::CheckHardwareInformation(); // Release existing handle if called twice (prevents leak from LabVIEW) if (*Handle) { if (UnregisterOCRHandle(*Handle)) { (*Handle)->Destroy(); delete *Handle; } *Handle = nullptr; } // Validate limitSideLen: must be in (0, 20000], default to 960 if (limitSideLen <= 0 || limitSideLen > 20000) limitSideLen = 960; // Backward compatibility: legacy PaddleOCR model uses CPU OCR engine std::string modelPath(modelFilePath ? modelFilePath : ""); bool isLegacyModel = (modelPath.find("ANS_GenericOCR_v1.0") != std::string::npos); if (isLegacyModel) { (*Handle) = new ANSCENTER::ANSCPUOCR(); } else { switch (engineMode) { case 0:// Auto-detect, always use ONNX for better compatibility, especially on AMD GPUs and high-res images (*Handle) = new ANSCENTER::ANSONNXOCR(); break; case 1:// GPU — use TensorRT engine. limitSideLen = 960; (*Handle) = new ANSCENTER::ANSRTOCR(); break; case 2:// CPU (*Handle) = new ANSCENTER::ANSONNXOCR(); break; default: (*Handle) = new ANSCENTER::ANSONNXOCR(); break; } } if (*Handle == nullptr) return 0; else { RegisterOCRHandle(*Handle); ANSCENTER::OCRModelConfig modelConfig; switch (language) { case 0: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::ENGLISH; break; } case 1: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::CHINESE; break; } case 2: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::FRENCH; break; } case 3: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::GERMANY; break; } case 4: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::JAPANESE; break; } case 5: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::KOREAN; break; } case 6: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::CUSTOM; break; } default: { modelConfig.ocrLanguage = ANSCENTER::OCRLanguage::ENGLISH; break; } } modelConfig.useDetector = true; modelConfig.useRecognizer = true; modelConfig.useCLS = true; modelConfig.useLayout = false; modelConfig.useTable = false; modelConfig.useTensorRT = false; modelConfig.enableMKLDNN = false; modelConfig.useDilation = false; modelConfig.useAngleCLS = false; modelConfig.gpuId = gpuId; modelConfig.detectionDBThreshold = detectorDBThreshold; modelConfig.detectionBoxThreshold = detectorDBBoxThreshold; modelConfig.detectionDBUnclipRatio = detectorDBUnclipRatio; modelConfig.clsThreshold = classifierThreshold; if (useDilation == 1)modelConfig.useDilation = true; modelConfig.limitSideLen = limitSideLen; int result = (*Handle)->Initialize(licenseKey, modelConfig, modelFilePath, modelFileZipPassword, engineMode); return result; } } catch (std::exception& e) { return 0; } catch (...) { return 0; } } // Original signature (without limitSideLen) — kept for backward compatibility // with third-party apps already built against the old DLL. extern "C" ANSOCR_API int CreateANSOCRHandle(ANSCENTER::ANSOCRBase** Handle, const char* licenseKey, const char* modelFilePath, const char* modelFileZipPassword, int language, int engineMode, // 0: autodetect, 1 gpu, 2 cpu int gpuId, double detectorDBThreshold, double detectorDBBoxThreshold, double detectorDBUnclipRatio, double classifierThreshold, int useDilation) { return CreateANSOCRHandleEx(Handle, licenseKey, modelFilePath, modelFileZipPassword, language, engineMode, gpuId, detectorDBThreshold, detectorDBBoxThreshold, detectorDBUnclipRatio, classifierThreshold, useDilation, 960); } // Helper: serialize OCR results with optional ALPR post-processing static std::string SerializeOCRResults(ANSCENTER::ANSOCRBase* engine, const std::vector& outputs, int imageWidth, int imageHeight, const cv::Mat& originalImage = cv::Mat()) { if (engine->GetOCRMode() == ANSCENTER::OCR_ALPR && !engine->GetALPRFormats().empty()) { auto alprResults = ANSCENTER::ANSOCRUtility::ALPRPostProcessing( outputs, engine->GetALPRFormats(), imageWidth, imageHeight, engine, originalImage); return ANSCENTER::ANSOCRUtility::ALPRResultToJsonString(alprResults); } return ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(outputs); } extern "C" ANSOCR_API std::string RunInference(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength) { if (!Handle || !*Handle) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, jpeg_string), cv::IMREAD_COLOR); if (frame.empty()) return ""; std::vector outputs = engine->RunInference(frame); std::string stResult = SerializeOCRResults(engine, outputs, frame.cols, frame.rows, frame); frame.release(); outputs.clear(); return stResult; } catch (...) { return ""; } } extern "C" ANSOCR_API std::string RunInferenceWithCamID(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, const char* cameraId) { if (!Handle || !*Handle) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, jpeg_string), cv::IMREAD_COLOR); if (frame.empty()) return ""; std::vector outputs = engine->RunInference(frame, cameraId); std::string stResult = SerializeOCRResults(engine, outputs, frame.cols, frame.rows, frame); frame.release(); outputs.clear(); return stResult; } catch (...) { return ""; } } extern "C" ANSOCR_API int RunInferenceCV(ANSCENTER::ANSOCRBase** Handle, const cv::Mat& image, std::string& ocrResult) { if (!Handle || !*Handle) return -1; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return -3; auto* engine = guard.get(); try { if (image.empty()) return 0; std::vector outputs = engine->RunInference(image, "cameraId"); ocrResult = SerializeOCRResults(engine, outputs, image.cols, image.rows, image); return 1; } catch (...) { return -2; } } extern "C" ANSOCR_API std::string RunInferenceBinary(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height) { if (!Handle || !*Handle || !jpeg_bytes || width == 0 || height == 0) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::Mat(height, width, CV_8UC3, jpeg_bytes).clone(); if (frame.empty()) return ""; std::vector outputs = engine->RunInference(frame); std::string stResult = SerializeOCRResults(engine, outputs, width, height, frame); frame.release(); outputs.clear(); return stResult; } catch (...) { return ""; } } static int ReleaseANSOCRHandle_Impl(ANSCENTER::ANSOCRBase** Handle) { try { if (!Handle || !*Handle) return 0; if (!UnregisterOCRHandle(*Handle)) { *Handle = nullptr; return 0; // Not in registry — already freed } (*Handle)->Destroy(); delete *Handle; *Handle = nullptr; return 0; } catch (...) { if (Handle) *Handle = nullptr; return 1; } } extern "C" ANSOCR_API int ReleaseANSOCRHandle(ANSCENTER::ANSOCRBase** Handle) { __try { return ReleaseANSOCRHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { return 1; } } // ── ALPR Configuration API ────────────────────────────────────────── extern "C" ANSOCR_API int SetANSOCRMode(ANSCENTER::ANSOCRBase** Handle, int ocrMode) { if (!Handle || !*Handle) return -1; (*Handle)->SetOCRMode(static_cast(ocrMode)); return 0; } extern "C" ANSOCR_API int SetANSOCRCountry(ANSCENTER::ANSOCRBase** Handle, int country) { if (!Handle || !*Handle) return -1; (*Handle)->SetCountry(static_cast(country)); return 0; } extern "C" ANSOCR_API int SetANSOCRALPRFormat(ANSCENTER::ANSOCRBase** Handle, const char* formatJson) { if (!Handle || !*Handle || !formatJson) return -1; try { nlohmann::json j = nlohmann::json::parse(formatJson); ANSCENTER::ALPRPlateFormat fmt; fmt.name = j.value("name", "CUSTOM"); fmt.country = static_cast(j.value("country", 99)); fmt.numRows = j.value("num_rows", 2); fmt.rowSplitThreshold = j.value("row_split_threshold", 0.3f); static const std::map classMap = { {"digit", ANSCENTER::CHAR_DIGIT}, {"latin_alpha", ANSCENTER::CHAR_LATIN_ALPHA}, {"alphanumeric", ANSCENTER::CHAR_ALPHANUMERIC}, {"hiragana", ANSCENTER::CHAR_HIRAGANA}, {"katakana", ANSCENTER::CHAR_KATAKANA}, {"kanji", ANSCENTER::CHAR_KANJI}, {"cjk_any", ANSCENTER::CHAR_CJK_ANY}, {"any", ANSCENTER::CHAR_ANY} }; for (const auto& zj : j["zones"]) { ANSCENTER::ALPRZone zone; zone.name = zj.value("name", ""); zone.row = zj.value("row", 0); zone.col = zj.value("col", 0); std::string ccStr = zj.value("char_class", "any"); auto it = classMap.find(ccStr); zone.charClass = (it != classMap.end()) ? it->second : ANSCENTER::CHAR_ANY; zone.minLength = zj.value("min_length", 1); zone.maxLength = zj.value("max_length", 10); zone.validationRegex = zj.value("regex", ""); if (zj.contains("corrections")) { for (auto& [key, val] : zj["corrections"].items()) { zone.corrections[key] = val.get(); } } fmt.zones.push_back(zone); } (*Handle)->SetALPRFormat(fmt); return 0; } catch (...) { return -2; } } // Unicode conversion utilities for LabVIEW wrapper classes // Converts input string to UTF-16LE. Handles both: // - JSON Unicode escapes (\uXXXX) from ensure_ascii=true output // - Raw UTF-8 encoded strings // Pure ASCII input is passed through directly (no conversion overhead). extern "C" ANSOCR_API int ANSOCR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result) { try { if (!utf8Str || !result) return -1; int len = (int)strlen(utf8Str); if (len == 0) return 0; // Check if input contains \uXXXX escapes or non-ASCII bytes bool hasUnicodeEscapes = false; bool hasNonAscii = false; for (int i = 0; i < len; i++) { if ((unsigned char)utf8Str[i] >= 0x80) hasNonAscii = true; if (i + 1 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') hasUnicodeEscapes = true; } // Pure ASCII with no escapes — pass through directly if (!hasNonAscii && !hasUnicodeEscapes) { MgErr error = DSSetHandleSize(result, sizeof(int32) + len * sizeof(uChar)); if (error != noErr) return -2; (*result)->cnt = len; memcpy((*result)->str, utf8Str, len); return 1; } // If contains \uXXXX escapes, decode them to UTF-16LE directly if (hasUnicodeEscapes) { std::string utf16le; utf16le.reserve(len * 2); for (int i = 0; i < len; ) { if (i + 5 < len && utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { char hex[5] = { utf8Str[i + 2], utf8Str[i + 3], utf8Str[i + 4], utf8Str[i + 5], 0 }; uint16_t cp = (uint16_t)strtoul(hex, nullptr, 16); utf16le += static_cast(cp & 0xFF); utf16le += static_cast((cp >> 8) & 0xFF); i += 6; } else { // ASCII or raw UTF-8 byte — convert as single char utf16le += utf8Str[i]; utf16le += '\0'; i++; } } int size = (int)utf16le.size(); MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return -2; (*result)->cnt = size; memcpy((*result)->str, utf16le.data(), size); return 1; } // Raw UTF-8 — convert via Windows API #ifdef _WIN32 int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, nullptr, 0); if (wideLen <= 0) return 0; std::wstring wideStr(wideLen, 0); MultiByteToWideChar(CP_UTF8, 0, utf8Str, len, &wideStr[0], wideLen); int size = wideLen * (int)sizeof(wchar_t); MgErr error = DSSetHandleSize(result, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return -2; (*result)->cnt = size; memcpy((*result)->str, wideStr.data(), size); return 1; #else return 0; #endif } catch (...) { return -1; } } extern "C" ANSOCR_API int ANSOCR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { try { if (!utf16leBytes || byteLen <= 0 || !result) return -1; // Check if input is already pure ASCII (no high bytes, or not valid UTF-16LE) // If all bytes < 0x80 and byteLen has no null bytes in odd positions pattern, // treat as already-UTF8 ASCII and pass through bool isAlreadyAscii = true; bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); if (isUtf16le) { // Check if all high bytes (odd indices) are 0x00 — means pure ASCII in UTF-16LE for (int i = 1; i < byteLen; i += 2) { if (utf16leBytes[i] != 0x00) { isAlreadyAscii = false; break; } } if (isAlreadyAscii) { // Extract just the low bytes (ASCII characters) int asciiLen = byteLen / 2; MgErr error = DSSetHandleSize(result, sizeof(int32) + asciiLen * sizeof(uChar)); if (error != noErr) return -2; (*result)->cnt = asciiLen; for (int i = 0; i < asciiLen; i++) { (*result)->str[i] = utf16leBytes[i * 2]; } return 1; } } #ifdef _WIN32 int wideLen = byteLen / (int)sizeof(wchar_t); const wchar_t* wideStr = reinterpret_cast(utf16leBytes); int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); if (utf8Len <= 0) return 0; std::string utf8Str(utf8Len, 0); WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); MgErr error = DSSetHandleSize(result, sizeof(int32) + utf8Len * sizeof(uChar)); if (error != noErr) return -2; (*result)->cnt = utf8Len; memcpy((*result)->str, utf8Str.data(), utf8Len); return 1; #else return 0; #endif } catch (...) { return -1; } } extern "C" ANSOCR_API std::string RunInferenceImagePath(ANSCENTER::ANSOCRBase** Handle, const char* imageFilePath) { if (!Handle || !*Handle) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { std::string stImageFileName(imageFilePath); cv::Mat frame = cv::imread(stImageFileName, cv::ImreadModes::IMREAD_COLOR); if (frame.empty()) return ""; std::vector outputs = engine->RunInference(frame); std::string stResult = SerializeOCRResults(engine, outputs, frame.cols, frame.rows, frame); frame.release(); outputs.clear(); return stResult; } catch (...) { return ""; } } extern "C" ANSOCR_API std::string RunInferenceInCroppedImages(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, const char* strBboxes) { if (!Handle || !*Handle) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, jpeg_string), cv::IMREAD_COLOR); if (frame.empty()) return ""; std::vector bBoxes = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes); std::vector outputs = engine->RunInference(frame, bBoxes); std::string stResult = ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(outputs); frame.release(); outputs.clear(); return stResult; } catch (...) { return ""; } } extern "C" ANSOCR_API std::string RunInferenceInCroppedImagesWithCamID(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, const char* strBboxes, const char* cameraId) { if (!Handle || !*Handle) return ""; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, jpeg_string), cv::IMREAD_COLOR); if (frame.empty()) return ""; std::vector bBoxes = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes); std::vector outputs = engine->RunInference(frame, bBoxes, cameraId); std::string stResult = ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(outputs); frame.release(); outputs.clear(); return stResult; } catch (...) { return ""; } } //// For LabVIEW API extern "C" ANSOCR_API int RunInference_LV(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, LStrHandle detectionResult) { try { std::string st = RunInference(Handle, jpeg_string, bufferLength); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInference_LVWithCamID(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, const char* cameraId, LStrHandle detectionResult) { try { std::string st = RunInferenceWithCamID(Handle, jpeg_string, bufferLength, cameraId); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceBinary_LV(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, LStrHandle detectionResult) { try { std::string st = RunInferenceBinary(Handle, jpeg_bytes, width, height); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceImagePath_LV(ANSCENTER::ANSOCRBase** Handle, const char* imageFilePath, LStrHandle detectionResult) { try { std::string st = RunInferenceImagePath(Handle, imageFilePath); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int ANSOCRUnitTest(const char* modelFilePath, const char* imageFilePath, LStrHandle detectionResult) { try { ANSCENTER::ANSOCRBase* infHandle; std::string licenseKey = ""; int language = 0;// English int engine = 0; int createResult = CreateANSOCRHandle(&infHandle, licenseKey.c_str(), modelFilePath, "", language, engine); std::string st = RunInferenceImagePath(&infHandle, imageFilePath); ReleaseANSOCRHandle(&infHandle); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceInCroppedImages_LV(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, const char* strBboxes, LStrHandle detectionResult) { try { std::string st = RunInferenceInCroppedImages(Handle, jpeg_string, bufferLength, strBboxes); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceInCroppedImages_LVWithCamID(ANSCENTER::ANSOCRBase** Handle, unsigned char* jpeg_string, int32 bufferLength, const char* strBboxes, const char* cameraId, LStrHandle detectionResult) { try { std::string st = RunInferenceInCroppedImagesWithCamID(Handle, jpeg_string, bufferLength, strBboxes, cameraId); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceComplete_LV( ANSCENTER::ANSOCRBase** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, LStrHandle detectionResult, LStrHandle imageStr) { if (!Handle || !*Handle) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return -3; auto* engine = guard.get(); try { // Lookup NV12 frame BEFORE cloning (clone creates new cv::Mat*) GpuFrameData* gpuFrame = ANSGpuFrameRegistry::instance().lookup(*cvImage); cv::Mat localImage = (**cvImage).clone(); int originalWidth = localImage.cols; int originalHeight = localImage.rows; if (originalWidth == 0 || originalHeight == 0) return -2; tl_currentGpuFrame() = gpuFrame; std::vector outputs = engine->RunInference(localImage, cameraId); tl_currentGpuFrame() = nullptr; bool getJpeg = (getJpegString == 1); std::string stImage; int maxImageSize = originalWidth; bool resizeNeeded = (jpegImageSize > 0) && (jpegImageSize < maxImageSize); float ratio = 1.0f; int newWidth = originalWidth; int newHeight = originalHeight; if (resizeNeeded) { newWidth = jpegImageSize; newHeight = static_cast(std::round(newWidth * static_cast(originalHeight) / originalWidth)); ratio = static_cast(newWidth) / originalWidth; for (auto& obj : outputs) { obj.box.x = std::max(0, std::min(static_cast(obj.box.x * ratio), newWidth - 1)); obj.box.y = std::max(0, std::min(static_cast(obj.box.y * ratio), newHeight - 1)); obj.box.width = std::max(1, std::min(static_cast(obj.box.width * ratio), newWidth - obj.box.x)); obj.box.height = std::max(1, std::min(static_cast(obj.box.height * ratio), newHeight - obj.box.y)); } } else { for (auto& obj : outputs) { obj.box.x = std::max(0, std::min(static_cast(obj.box.x), originalWidth - 1)); obj.box.y = std::max(0, std::min(static_cast(obj.box.y), originalHeight - 1)); obj.box.width = std::max(1, std::min(static_cast(obj.box.width), originalWidth - obj.box.x)); obj.box.height = std::max(1, std::min(static_cast(obj.box.height), originalHeight - obj.box.y)); } } if (getJpeg) { cv::Mat processedImage = localImage; if (resizeNeeded) { cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA); } std::vector buf; if (cv::imencode(".jpg", processedImage, buf, { cv::IMWRITE_JPEG_QUALITY, 50 })) { stImage.assign(buf.begin(), buf.end()); } } std::string stDetectionResult = ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(outputs); if (stDetectionResult.empty()) return 0; int size = static_cast(stDetectionResult.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, stDetectionResult.c_str(), size); if (getJpeg) { if (stImage.empty()) return 0; size = static_cast(stImage.length()); error = DSSetHandleSize(imageStr, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*imageStr)->cnt = size; memcpy((*imageStr)->str, stImage.c_str(), size); } return 1; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferencesComplete_LV(ANSCENTER::ANSOCRBase** Handle, cv::Mat** cvImage, const char* cameraId, int maxImageSize, const char* strBboxes, LStrHandle detectionResult) { if (!Handle || !*Handle) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; OCRHandleGuard guard(AcquireOCRHandle(*Handle)); if (!guard) return -3; auto* engine = guard.get(); try { // Lookup NV12 frame BEFORE cloning (clone creates new cv::Mat*) GpuFrameData* gpuFrame = ANSGpuFrameRegistry::instance().lookup(*cvImage); cv::Mat localImage = (**cvImage).clone(); std::vector objectDetectionResults; std::vector bBox = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes); const int originalWidth = localImage.cols; const int originalHeight = localImage.rows; const double scaleFactor = (maxImageSize > 0) ? static_cast(originalWidth) / maxImageSize : 1.0; tl_currentGpuFrame() = gpuFrame; if (bBox.empty()) { objectDetectionResults = engine->RunInference(localImage, cameraId); } else { for (const auto& rect : bBox) { cv::Rect scaledRect; scaledRect.x = static_cast(rect.x * scaleFactor); scaledRect.y = static_cast(rect.y * scaleFactor); scaledRect.width = static_cast(rect.width * scaleFactor); scaledRect.height = static_cast(rect.height * scaleFactor); scaledRect &= cv::Rect(0, 0, originalWidth, originalHeight); if (scaledRect.width <= 0 || scaledRect.height <= 0) continue; const cv::Mat croppedImage = localImage(scaledRect); std::vector croppedDetectionResults = engine->RunInference(croppedImage, cameraId); for (auto& obj : croppedDetectionResults) { obj.box.x = (obj.box.x + scaledRect.x) / scaleFactor; obj.box.y = (obj.box.y + scaledRect.y) / scaleFactor; obj.box.width /= scaleFactor; obj.box.height /= scaleFactor; objectDetectionResults.push_back(std::move(obj)); } } } tl_currentGpuFrame() = nullptr; std::string stDetectionResult = ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(objectDetectionResults); if (stDetectionResult.empty()) return 0; const int size = static_cast(stDetectionResult.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, stDetectionResult.c_str(), size); return 1; } catch (...) { return 0; } } // ============================================================================ // V2 LabVIEW API — Accept handle as uint64_t by value. // Eliminates Handle** pointer-to-pointer instability when LabVIEW calls // concurrently from multiple tasks. // LabVIEW CLFN: set Handle parameter to Numeric / Unsigned Pointer-sized Integer / Pass: Value // ============================================================================ // Helper: cast uint64_t handle to ANSOCRBase** for delegation to existing functions #define OCR_V2_HANDLE_SETUP(handleVal) \ ANSCENTER::ANSOCRBase* _v2Direct = reinterpret_cast(handleVal); \ if (_v2Direct == nullptr) return 0; \ ANSCENTER::ANSOCRBase* _v2Arr[1] = { _v2Direct }; \ ANSCENTER::ANSOCRBase** Handle = &_v2Arr[0]; extern "C" ANSOCR_API int RunInference_LV_V2(uint64_t handleVal, unsigned char* jpeg_string, int32 bufferLength, LStrHandle detectionResult) { try { OCR_V2_HANDLE_SETUP(handleVal); std::string st = RunInference(Handle, jpeg_string, bufferLength); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInference_LVWithCamID_V2(uint64_t handleVal, unsigned char* jpeg_string, int32 bufferLength, const char* cameraId, LStrHandle detectionResult) { try { OCR_V2_HANDLE_SETUP(handleVal); std::string st = RunInferenceWithCamID(Handle, jpeg_string, bufferLength, cameraId); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceBinary_LV_V2(uint64_t handleVal, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, LStrHandle detectionResult) { try { OCR_V2_HANDLE_SETUP(handleVal); std::string st = RunInferenceBinary(Handle, jpeg_bytes, width, height); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceImagePath_LV_V2(uint64_t handleVal, const char* imageFilePath, LStrHandle detectionResult) { try { OCR_V2_HANDLE_SETUP(handleVal); std::string st = RunInferenceImagePath(Handle, imageFilePath); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceInCroppedImages_LV_V2(uint64_t handleVal, unsigned char* jpeg_string, int32 bufferLength, const char* strBboxes, LStrHandle detectionResult) { try { OCR_V2_HANDLE_SETUP(handleVal); std::string st = RunInferenceInCroppedImages(Handle, jpeg_string, bufferLength, strBboxes); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceInCroppedImages_LVWithCamID_V2(uint64_t handleVal, unsigned char* jpeg_string, int32 bufferLength, const char* strBboxes, const char* cameraId, LStrHandle detectionResult) { try { OCR_V2_HANDLE_SETUP(handleVal); std::string st = RunInferenceInCroppedImagesWithCamID(Handle, jpeg_string, bufferLength, strBboxes, cameraId); if (st.empty()) return 0; int size = static_cast(st.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferenceComplete_LV_V2( uint64_t handleVal, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, LStrHandle detectionResult, LStrHandle imageStr) { ANSCENTER::ANSOCRBase* directHandle = reinterpret_cast(handleVal); if (directHandle == nullptr) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; OCRHandleGuard guard(AcquireOCRHandle(directHandle)); if (!guard) return -3; auto* engine = guard.get(); try { // Lookup NV12 frame BEFORE cloning (clone creates new cv::Mat*) GpuFrameData* gpuFrame = ANSGpuFrameRegistry::instance().lookup(*cvImage); cv::Mat localImage = (**cvImage).clone(); int originalWidth = localImage.cols; int originalHeight = localImage.rows; if (originalWidth == 0 || originalHeight == 0) return -2; tl_currentGpuFrame() = gpuFrame; std::vector outputs = engine->RunInference(localImage, cameraId); tl_currentGpuFrame() = nullptr; bool getJpeg = (getJpegString == 1); std::string stImage; int maxImageSize = originalWidth; bool resizeNeeded = (jpegImageSize > 0) && (jpegImageSize < maxImageSize); float ratio = 1.0f; int newWidth = originalWidth; int newHeight = originalHeight; if (resizeNeeded) { newWidth = jpegImageSize; newHeight = static_cast(std::round(newWidth * static_cast(originalHeight) / originalWidth)); ratio = static_cast(newWidth) / originalWidth; for (auto& obj : outputs) { obj.box.x = std::max(0, std::min(static_cast(obj.box.x * ratio), newWidth - 1)); obj.box.y = std::max(0, std::min(static_cast(obj.box.y * ratio), newHeight - 1)); obj.box.width = std::max(1, std::min(static_cast(obj.box.width * ratio), newWidth - obj.box.x)); obj.box.height = std::max(1, std::min(static_cast(obj.box.height * ratio), newHeight - obj.box.y)); } } else { for (auto& obj : outputs) { obj.box.x = std::max(0, std::min(static_cast(obj.box.x), originalWidth - 1)); obj.box.y = std::max(0, std::min(static_cast(obj.box.y), originalHeight - 1)); obj.box.width = std::max(1, std::min(static_cast(obj.box.width), originalWidth - obj.box.x)); obj.box.height = std::max(1, std::min(static_cast(obj.box.height), originalHeight - obj.box.y)); } } if (getJpeg) { cv::Mat processedImage = localImage; if (resizeNeeded) { cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA); } std::vector buf; if (cv::imencode(".jpg", processedImage, buf, { cv::IMWRITE_JPEG_QUALITY, 50 })) { stImage.assign(buf.begin(), buf.end()); } } std::string stDetectionResult = ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(outputs); if (stDetectionResult.empty()) return 0; int size = static_cast(stDetectionResult.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, stDetectionResult.c_str(), size); if (getJpeg) { if (stImage.empty()) return 0; size = static_cast(stImage.length()); error = DSSetHandleSize(imageStr, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*imageStr)->cnt = size; memcpy((*imageStr)->str, stImage.c_str(), size); } return 1; } catch (...) { return 0; } } extern "C" ANSOCR_API int RunInferencesComplete_LV_V2(uint64_t handleVal, cv::Mat** cvImage, const char* cameraId, int maxImageSize, const char* strBboxes, LStrHandle detectionResult) { ANSCENTER::ANSOCRBase* directHandle = reinterpret_cast(handleVal); if (directHandle == nullptr) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; OCRHandleGuard guard(AcquireOCRHandle(directHandle)); if (!guard) return -3; auto* engine = guard.get(); try { // Lookup NV12 frame BEFORE cloning (clone creates new cv::Mat*) GpuFrameData* gpuFrame = ANSGpuFrameRegistry::instance().lookup(*cvImage); cv::Mat localImage = (**cvImage).clone(); std::vector objectDetectionResults; std::vector bBox = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes); const int originalWidth = localImage.cols; const int originalHeight = localImage.rows; const double scaleFactor = (maxImageSize > 0) ? static_cast(originalWidth) / maxImageSize : 1.0; tl_currentGpuFrame() = gpuFrame; if (bBox.empty()) { objectDetectionResults = engine->RunInference(localImage, cameraId); } else { for (const auto& rect : bBox) { cv::Rect scaledRect; scaledRect.x = static_cast(rect.x * scaleFactor); scaledRect.y = static_cast(rect.y * scaleFactor); scaledRect.width = static_cast(rect.width * scaleFactor); scaledRect.height = static_cast(rect.height * scaleFactor); scaledRect &= cv::Rect(0, 0, originalWidth, originalHeight); if (scaledRect.width <= 0 || scaledRect.height <= 0) continue; const cv::Mat croppedImage = localImage(scaledRect); std::vector croppedDetectionResults = engine->RunInference(croppedImage, cameraId); for (auto& obj : croppedDetectionResults) { obj.box.x = (obj.box.x + scaledRect.x) / scaleFactor; obj.box.y = (obj.box.y + scaledRect.y) / scaleFactor; obj.box.width /= scaleFactor; obj.box.height /= scaleFactor; objectDetectionResults.push_back(std::move(obj)); } } } tl_currentGpuFrame() = nullptr; std::string stDetectionResult = ANSCENTER::ANSOCRUtility::OCRDetectionToJsonString(objectDetectionResults); if (stDetectionResult.empty()) return 0; const int size = static_cast(stDetectionResult.length()); MgErr error = DSSetHandleSize(detectionResult, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*detectionResult)->cnt = size; memcpy((*detectionResult)->str, stDetectionResult.c_str(), size); return 1; } catch (...) { return 0; } } // ============================================================================ // V2 Create / Release — handle returned/passed as uint64_t by value. // ============================================================================ extern "C" ANSOCR_API uint64_t CreateANSOCRHandleEx_V2(const char* licenseKey, const char* modelFilePath, const char* modelFileZipPassword, int language, int engineMode, int gpuId, double detectorDBThreshold, double detectorDBBoxThreshold, double detectorDBUnclipRatio, double classifierThreshold, int useDilation, int limitSideLen) { try { ANSCENTER::ANSOCRBase* handle = nullptr; int result = CreateANSOCRHandleEx(&handle, licenseKey, modelFilePath, modelFileZipPassword, language, engineMode, gpuId, detectorDBThreshold, detectorDBBoxThreshold, detectorDBUnclipRatio, classifierThreshold, useDilation, limitSideLen); if (result == 0 || handle == nullptr) return 0; return reinterpret_cast(handle); } catch (...) { return 0; } } extern "C" ANSOCR_API uint64_t CreateANSOCRHandle_V2(const char* licenseKey, const char* modelFilePath, const char* modelFileZipPassword, int language, int engineMode, int gpuId, double detectorDBThreshold, double detectorDBBoxThreshold, double detectorDBUnclipRatio, double classifierThreshold, int useDilation) { return CreateANSOCRHandleEx_V2(licenseKey, modelFilePath, modelFileZipPassword, language, engineMode, gpuId, detectorDBThreshold, detectorDBBoxThreshold, detectorDBUnclipRatio, classifierThreshold, useDilation, 960); } extern "C" ANSOCR_API int ReleaseANSOCRHandle_V2(uint64_t handleVal) { try { ANSCENTER::ANSOCRBase* directHandle = reinterpret_cast(handleVal); if (directHandle == nullptr) return 0; if (!UnregisterOCRHandle(directHandle)) { return 0; // Not in registry — already freed } directHandle->Destroy(); delete directHandle; return 0; } catch (...) { return 1; } }