// dllmain.cpp : Defines the entry point for the DLL application. #pragma once #include "pch.h" #include "ANSLPR.h" #include "ANSLPR_CPU.h" #include "ANSLPR_OD.h" #include "ANSLibsLoader.h" #include "ANSGpuFrameRegistry.h" // gpu_frame_lookup(cv::Mat*) #include #include #include #include // Write a message to Windows Event Log (Application log, source "ANSLogger"). // Works even when spdlog is not initialized — useful for crash diagnostics. static void WriteWindowsEventLog(const char* message, WORD eventType = EVENTLOG_ERROR_TYPE) { static HANDLE hEventLog = RegisterEventSourceA(NULL, "ANSLogger"); if (hEventLog) { const char* msgs[1] = { message }; ReportEventA(hEventLog, eventType, 0, 0, NULL, 1, 0, msgs, NULL); } OutputDebugStringA(message); OutputDebugStringA("\n"); } // Handle registry with refcount — prevents use-after-free when // ReleaseANSALPRHandle is called while inference is still running. // refcount: starts at 1 on Register. AcquireALPRHandle increments, // ReleaseALPRHandleRef decrements. Object is destroyed when refcount hits 0. static std::unordered_map& ALPRHandleRegistry() { static std::unordered_map s; return s; } static std::mutex& ALPRHandleRegistryMutex() { static std::mutex m; return m; } static std::condition_variable& ALPRHandleRegistryCV() { static std::condition_variable cv; return cv; } static void RegisterALPRHandle(ANSCENTER::ANSALPR* h) { std::lock_guard lk(ALPRHandleRegistryMutex()); ALPRHandleRegistry()[h] = 1; // refcount = 1 } // Acquire a handle for use (increment refcount). Returns the handle // if valid, nullptr if already released. static ANSCENTER::ANSALPR* AcquireALPRHandle(ANSCENTER::ANSALPR* h) { std::lock_guard lk(ALPRHandleRegistryMutex()); auto it = ALPRHandleRegistry().find(h); if (it == ALPRHandleRegistry().end()) return nullptr; it->second++; return h; } // Release a use of the handle (decrement refcount). // Returns true if this was the last reference (caller should destroy). static bool ReleaseALPRHandleRef(ANSCENTER::ANSALPR* h) { std::lock_guard lk(ALPRHandleRegistryMutex()); auto it = ALPRHandleRegistry().find(h); if (it == ALPRHandleRegistry().end()) return false; it->second--; if (it->second <= 0) { ALPRHandleRegistry().erase(it); ALPRHandleRegistryCV().notify_all(); return true; // Caller should destroy } return false; } // Unregister and wait for all in-flight uses to finish. // Decrements the creation refcount and blocks until refcount hits 0. // Returns true if caller should destroy the object. static bool UnregisterALPRHandle(ANSCENTER::ANSALPR* h) { std::unique_lock lk(ALPRHandleRegistryMutex()); auto it = ALPRHandleRegistry().find(h); if (it == ALPRHandleRegistry().end()) return false; it->second--; // Remove creation ref // Wait for in-flight inferences to finish (30s timeout as safety net) bool ok = ALPRHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() { auto it2 = ALPRHandleRegistry().find(h); return it2 == ALPRHandleRegistry().end() || it2->second <= 0; }); if (!ok) { OutputDebugStringA("WARNING: UnregisterALPRHandle timed out waiting for in-flight inference\n"); } ALPRHandleRegistry().erase(h); return true; // Safe to destroy now } // RAII guard — ensures ReleaseALPRHandleRef is always called, preventing // refcount leaks that would cause UnregisterALPRHandle to deadlock. class ALPRHandleGuard { ANSCENTER::ANSALPR* engine; public: explicit ALPRHandleGuard(ANSCENTER::ANSALPR* e) : engine(e) {} ~ALPRHandleGuard() { if (engine) ReleaseALPRHandleRef(engine); } ANSCENTER::ANSALPR* get() const { return engine; } explicit operator bool() const { return engine != nullptr; } ALPRHandleGuard(const ALPRHandleGuard&) = delete; ALPRHandleGuard& operator=(const ALPRHandleGuard&) = 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: case DLL_PROCESS_DETACH: break; } return TRUE; } static int CreateANSALPRHandle_Impl(ANSCENTER::ANSALPR** Handle, const char* licenseKey, const char* modelZipFilePath, const char* modelZipPassword, int engineType, double detectorThreshold, double ocrThreshold, double colourThreshold) { try { // Ensure all shared DLLs (OpenCV, OpenVINO, TRT, ORT) are pre-loaded ANSCENTER::ANSLibsLoader::Initialize(); // Release existing handle if called twice (prevents leak from LabVIEW) if (*Handle) { if (UnregisterALPRHandle(*Handle)) { (*Handle)->Destroy(); delete *Handle; } *Handle = nullptr; } if (engineType == 0) { (*Handle) = new ANSCENTER::ANSALPR_CPU();// built-in paddle OCR } else if (engineType == 1) { (*Handle) = new ANSCENTER::ANSALPR_OD(); } else { return 0; } if (*Handle == nullptr) return 0; else { RegisterALPRHandle(*Handle); int result = (*Handle)->Initialize(licenseKey, modelZipFilePath, modelZipPassword, detectorThreshold, ocrThreshold,colourThreshold); return result; } } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int CreateANSALPRHandle(ANSCENTER::ANSALPR * *Handle, const char* licenseKey, const char* modelZipFilePath, const char* modelZipPassword, int engineType, double detectorThreshold, double ocrThreshold, double colourThreshold) { __try { return CreateANSALPRHandle_Impl(Handle, licenseKey, modelZipFilePath, modelZipPassword, engineType, detectorThreshold, ocrThreshold, colourThreshold); } __except (EXCEPTION_EXECUTE_HANDLER) { char buf[256]; snprintf(buf, sizeof(buf), "ANSLPR CreateANSALPRHandle: SEH exception 0x%08X caught during initialization", GetExceptionCode()); WriteWindowsEventLog(buf); if (Handle) *Handle = nullptr; return 0; } } static int LoadANSALPREngineHandle_Impl(ANSCENTER::ANSALPR** Handle) { try { if (*Handle != nullptr) { int result = (*Handle)->LoadEngine(); return result; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int LoadANSALPREngineHandle(ANSCENTER::ANSALPR** Handle) { __try { return LoadANSALPREngineHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { char buf[256]; snprintf(buf, sizeof(buf), "ANSLPR LoadANSALPREngineHandle: SEH exception 0x%08X caught during engine load", GetExceptionCode()); WriteWindowsEventLog(buf); return 0; } } extern "C" ANSLPR_API std::string ANSALPR_RunInference(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_string, unsigned int bufferLength) { if (!Handle || !*Handle) return ""; ALPRHandleGuard guard(AcquireALPRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, jpeg_string), cv::IMREAD_UNCHANGED); if (frame.empty()) return ""; std::string lprResult; bool result = engine->Inference(frame, lprResult); frame.release(); return lprResult; } catch (...) { return ""; } } extern "C" ANSLPR_API std::string ANSALPR_RunInferenceWithCamID(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId) { if (!Handle || !*Handle) return ""; ALPRHandleGuard guard(AcquireALPRHandle(*Handle)); if (!guard) return ""; auto* engine = guard.get(); try { cv::Mat frame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, jpeg_string), cv::IMREAD_UNCHANGED); if (frame.empty()) return ""; std::string lprResult; bool result = engine->Inference(frame, lprResult, cameraId); frame.release(); return lprResult; } catch (...) { return ""; } } extern "C" ANSLPR_API std::string ANSALPR_RunInferenceInCroppedImages(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes) { if (!Handle || !*Handle) return ""; ALPRHandleGuard guard(AcquireALPRHandle(*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::string lprResult; std::vector Bboxes = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes); bool result = engine->Inference(frame, Bboxes, lprResult); frame.release(); Bboxes.clear(); return lprResult; } catch (...) { return ""; } } extern "C" ANSLPR_API std::string ANSALPR_RunInferenceInCroppedImagesWithCamID(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId) { if (!Handle || !*Handle) return ""; ALPRHandleGuard guard(AcquireALPRHandle(*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::string lprResult; std::vector Bboxes = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes); bool result = engine->Inference(frame, Bboxes, lprResult, cameraId); frame.release(); Bboxes.clear(); return lprResult; } catch (...) { return ""; } } extern "C" ANSLPR_API std::string ANSALPR_RunInferenceBinary(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height) { if (!Handle || !*Handle || !jpeg_bytes || width == 0 || height == 0) return ""; ALPRHandleGuard guard(AcquireALPRHandle(*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::string lprResult; bool result = engine->Inference(frame, lprResult); frame.release(); return lprResult; } catch (...) { return ""; } } extern "C" ANSLPR_API std::string ANSALPR_RunInferenceBinaryInCroppedImages(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, const char* strBboxes) { if (!Handle || !*Handle) return ""; ALPRHandleGuard guard(AcquireALPRHandle(*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::string lprResult; std::vector Bboxes = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes); bool result = engine->Inference(frame, Bboxes, lprResult); frame.release(); return lprResult; } catch (...) { return ""; } } static int ReleaseANSALPRHandle_Impl(ANSCENTER::ANSALPR** Handle) { try { if (!Handle || !*Handle) return 1; if (!UnregisterALPRHandle(*Handle)) { *Handle = nullptr; return 1; // Not in registry — already freed } (*Handle)->Destroy(); delete *Handle; *Handle = nullptr; return 1; } catch (...) { if (Handle) *Handle = nullptr; return 0; } } extern "C" ANSLPR_API int ReleaseANSALPRHandle(ANSCENTER::ANSALPR** Handle) { __try { return ReleaseANSALPRHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { return 0; } } //// For LabVIEW API extern "C" ANSLPR_API int ANSALPR_RunInference_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult) { try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceWithCamID_LV(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId, LStrHandle detectionResult) { try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceBinary_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, LStrHandle detectionResult) { try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceInCroppedImages_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, LStrHandle detectionResult) { try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceInCroppedImagesWithCamID_LV(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId, LStrHandle detectionResult) { try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceComplete_LV( ANSCENTER::ANSALPR** 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; ALPRHandleGuard guard(AcquireALPRHandle(*Handle)); if (!guard) return -3; auto* engine = guard.get(); try { const cv::Mat& localImage = **cvImage; // No clone — RunInference takes const ref // Set thread-local NV12 frame data for fast-path inference // Cleared after first RunInference to prevent NV12 mismatch on cropped sub-images (OCR, etc.) tl_currentGpuFrame() = ANSGpuFrameRegistry::instance().lookup(*cvImage); int originalWidth = localImage.cols; int originalHeight = localImage.rows; if (originalWidth == 0 || originalHeight == 0) { return -2; } 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)); } } // Convert to JPEG only if requested (avoid expensive encode when not needed) if (getJpeg) { cv::Mat processedImage; if (resizeNeeded) { cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA); } else { processedImage = localImage; // shallow copy (header only, no pixel copy) } std::vector buf; if (cv::imencode(".jpg", processedImage, buf, { cv::IMWRITE_JPEG_QUALITY, 50 })) { stImage.assign(buf.begin(), buf.end()); } } std::string stDetectionResult = engine->VectorDetectionToJsonString(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 (const std::exception& ex) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int ANSALPR_RunInferenceComplete_CPP(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, std::string& detectionResult, std::string& imageStr) { if (!Handle || !*Handle) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; ALPRHandleGuard guard(AcquireALPRHandle(*Handle)); if (!guard) return -3; auto* engine = guard.get(); try { const cv::Mat& localImage = **cvImage; // No clone — RunInference takes const ref // Set thread-local NV12 frame data for fast-path inference // Cleared after first RunInference to prevent NV12 mismatch on cropped sub-images (OCR, etc.) tl_currentGpuFrame() = ANSGpuFrameRegistry::instance().lookup(*cvImage); int originalWidth = localImage.cols; int originalHeight = localImage.rows; int maxImageSize = originalWidth; std::vector outputs = engine->RunInference(localImage, cameraId); bool getJpeg = (getJpegString == 1); std::string stImage; if ((jpegImageSize > 0) && (jpegImageSize < maxImageSize)) { int newWidth = jpegImageSize; int newHeight = static_cast(std::round(newWidth * static_cast(originalHeight) / originalWidth)); float 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; if ((jpegImageSize > 0) && (jpegImageSize < maxImageSize)) { int newWidth = jpegImageSize; int newHeight = static_cast(std::round(newWidth * static_cast(originalHeight) / originalWidth)); cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA); } else { processedImage = localImage; // shallow copy (header only) } std::vector buf; if (cv::imencode(".jpg", processedImage, buf, { cv::IMWRITE_JPEG_QUALITY, 50 })) { stImage.assign(buf.begin(), buf.end()); } } detectionResult = engine->VectorDetectionToJsonString(outputs); if (detectionResult.empty()) return 0; if (getJpeg) { if (stImage.empty()) return 0; imageStr = stImage; } return 1; } catch (...) { return 0; } } extern "C" ANSLPR_API int ANSALPR_RunInferencesComplete_LV( ANSCENTER::ANSALPR** 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; ALPRHandleGuard guard(AcquireALPRHandle(*Handle)); if (!guard) return -3; auto* engine = guard.get(); try { const cv::Mat& localImage = **cvImage; // No clone — RunInference takes const ref // Set thread-local NV12 frame data for fast-path inference // Cleared after first RunInference to prevent NV12 mismatch on cropped sub-images (OCR, etc.) tl_currentGpuFrame() = ANSGpuFrameRegistry::instance().lookup(*cvImage); std::vector objectDetectionResults; std::vector bBox = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes); const int originalWidth = localImage.cols; const int originalHeight = localImage.rows; const double scaleFactor = (maxImageSize > 0) ? static_cast(originalWidth) / maxImageSize : 1.0; if (bBox.empty()) { objectDetectionResults = engine->RunInference(localImage, cameraId); } tl_currentGpuFrame() = nullptr; // Clear before crop-based inference if (!bBox.empty()) { 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)); } } } std::string stDetectionResult = engine->VectorDetectionToJsonString(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 (const std::exception& ex) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int ANSALPR_SetFormat(ANSCENTER::ANSALPR** Handle, const char* format) { if (!Handle || !*Handle) return -1; if (!format) return -1; try { std::string strFormat(format); (*Handle)->SetPlateFormat(strFormat); return 1; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, const char* formats){// semi separated formats if (!Handle || !*Handle) return -1; if (!formats) return -1; try { std::vector formatList; std::string token; std::istringstream tokenStream(formats); while (getline(tokenStream, token, ';')) { formatList.push_back(token); } (*Handle)->SetPlateFormats(formatList); return 1; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LStrHandle Lstrformats)// semi separated formats { if (!Handle || !*Handle) return -1; if (!Lstrformats) return -1; try { std::vector formatList = (*Handle)->GetPlateFormats(); std::string formats; for (const auto& format : formatList) { formats += format + ";"; } if (!formats.empty()) { formats.pop_back(); // Remove the last semicolon } int size = static_cast(formats.length()); MgErr error = DSSetHandleSize(Lstrformats, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*Lstrformats)->cnt = size; memcpy((*Lstrformats)->str, formats.c_str(), size); return 1; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } // Unicode conversion utilities for LabVIEW wrapper classes extern "C" ANSLPR_API int ANSLPR_ConvertUTF8ToUTF16LE(const char* utf8Str, LStrHandle result, int includeBOM) { try { if (!utf8Str || !result) return -1; int len = (int)strlen(utf8Str); if (len == 0) return 0; const char bom[2] = { '\xFF', '\xFE' }; bool hasUnicodeEscapes = false; for (int i = 0; i + 1 < len; i++) { if (utf8Str[i] == '\\' && utf8Str[i + 1] == 'u') { hasUnicodeEscapes = true; break; } } if (hasUnicodeEscapes) { std::string utf16le; if (includeBOM) utf16le.assign(bom, 2); utf16le.reserve(len * 2 + 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 { 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; } #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 dataSize = wideLen * (int)sizeof(wchar_t); int bomSize = includeBOM ? 2 : 0; int totalSize = bomSize + dataSize; MgErr error = DSSetHandleSize(result, sizeof(int32) + totalSize * sizeof(uChar)); if (error != noErr) return -2; (*result)->cnt = totalSize; if (includeBOM) memcpy((*result)->str, bom, 2); memcpy((*result)->str + bomSize, wideStr.data(), dataSize); return 1; #else return 0; #endif } catch (...) { return -1; } } extern "C" ANSLPR_API int ANSLPR_ConvertUTF16LEToUTF8(const unsigned char* utf16leBytes, int byteLen, LStrHandle result) { try { if (!utf16leBytes || byteLen <= 0 || !result) return -1; bool isUtf16le = (byteLen >= 2 && byteLen % 2 == 0); if (isUtf16le) { bool isAscii = true; for (int i = 1; i < byteLen; i += 2) { if (utf16leBytes[i] != 0x00) { isAscii = false; break; } } if (isAscii) { 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; } } // ============================================================================ // V2 API — accepts uint64_t handle by value (eliminates LabVIEW buffer reuse bug) // ============================================================================ #define ALPR_V2_HANDLE_SETUP(handleVal) \ ANSCENTER::ANSALPR* _v2Direct = reinterpret_cast(handleVal); \ if (_v2Direct == nullptr) return 0; \ ANSCENTER::ANSALPR* _v2Arr[1] = { _v2Direct }; \ ANSCENTER::ANSALPR** Handle = &_v2Arr[0]; extern "C" ANSLPR_API int ANSALPR_RunInference_LV_V2(uint64_t handleVal, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult) { ALPR_V2_HANDLE_SETUP(handleVal); try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceWithCamID_LV_V2(uint64_t handleVal, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId, LStrHandle detectionResult) { ALPR_V2_HANDLE_SETUP(handleVal); try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceBinary_LV_V2(uint64_t handleVal, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, LStrHandle detectionResult) { ALPR_V2_HANDLE_SETUP(handleVal); try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceInCroppedImages_LV_V2(uint64_t handleVal, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, LStrHandle detectionResult) { ALPR_V2_HANDLE_SETUP(handleVal); try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceInCroppedImagesWithCamID_LV_V2(uint64_t handleVal, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId, LStrHandle detectionResult) { ALPR_V2_HANDLE_SETUP(handleVal); try { std::string st = ANSALPR_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" ANSLPR_API int ANSALPR_RunInferenceComplete_LV_V2( uint64_t handleVal, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, LStrHandle detectionResult, LStrHandle imageStr) { ANSCENTER::ANSALPR* _v2Direct = reinterpret_cast(handleVal); if (_v2Direct == nullptr) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; ALPRHandleGuard guard(AcquireALPRHandle(_v2Direct)); if (!guard) return -3; auto* engine = guard.get(); try { const cv::Mat& localImage = **cvImage; // No clone — RunInference takes const ref // Set thread-local NV12 frame data for fast-path inference // Cleared after first RunInference to prevent NV12 mismatch on cropped sub-images (OCR, etc.) tl_currentGpuFrame() = ANSGpuFrameRegistry::instance().lookup(*cvImage); int originalWidth = localImage.cols; int originalHeight = localImage.rows; if (originalWidth == 0 || originalHeight == 0) { return -2; } 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; if (resizeNeeded) { cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA); } else { processedImage = localImage; // shallow copy (header only) } std::vector buf; if (cv::imencode(".jpg", processedImage, buf, { cv::IMWRITE_JPEG_QUALITY, 50 })) { stImage.assign(buf.begin(), buf.end()); } } std::string stDetectionResult = engine->VectorDetectionToJsonString(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 (const std::exception& ex) { return 0; } catch (...) { return 0; } } extern "C" ANSLPR_API int ANSALPR_RunInferencesComplete_LV_V2( uint64_t handleVal, cv::Mat** cvImage, const char* cameraId, int maxImageSize, const char* strBboxes, LStrHandle detectionResult) { ANSCENTER::ANSALPR* _v2Direct = reinterpret_cast(handleVal); if (_v2Direct == nullptr) return -1; if (!cvImage || !(*cvImage) || (*cvImage)->empty()) return -2; ALPRHandleGuard guard(AcquireALPRHandle(_v2Direct)); if (!guard) return -3; auto* engine = guard.get(); try { const cv::Mat& localImage = **cvImage; // No clone — RunInference takes const ref // Set thread-local NV12 frame data for fast-path inference // Cleared after first RunInference to prevent NV12 mismatch on cropped sub-images (OCR, etc.) tl_currentGpuFrame() = ANSGpuFrameRegistry::instance().lookup(*cvImage); std::vector objectDetectionResults; std::vector bBox = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes); const int originalWidth = localImage.cols; const int originalHeight = localImage.rows; const double scaleFactor = (maxImageSize > 0) ? static_cast(originalWidth) / maxImageSize : 1.0; if (bBox.empty()) { objectDetectionResults = engine->RunInference(localImage, cameraId); } tl_currentGpuFrame() = nullptr; // Clear before crop-based inference if (!bBox.empty()) { 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)); } } } std::string stDetectionResult = engine->VectorDetectionToJsonString(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 (const std::exception& ex) { return 0; } catch (...) { return 0; } }