Files
ANSCORE/modules/ANSLPR/dllmain.cpp

1092 lines
36 KiB
C++

// 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 <unordered_set>
#include <unordered_map>
#include <condition_variable>
#include <cstdint>
// 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<ANSCENTER::ANSALPR*, int>& ALPRHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSALPR*, int> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<cv::Rect> 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<cv::Rect> 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<cv::Rect> 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<int>(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<int>(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<int>(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<int>(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<int>(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<ANSCENTER::Object> 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<int>(std::round(newWidth * static_cast<double>(originalHeight) / originalWidth));
ratio = static_cast<float>(newWidth) / originalWidth;
for (auto& obj : outputs) {
obj.box.x = std::max(0, std::min(static_cast<int>(obj.box.x * ratio), newWidth - 1));
obj.box.y = std::max(0, std::min(static_cast<int>(obj.box.y * ratio), newHeight - 1));
obj.box.width = std::max(1, std::min(static_cast<int>(obj.box.width * ratio), newWidth - obj.box.x));
obj.box.height = std::max(1, std::min(static_cast<int>(obj.box.height * ratio), newHeight - obj.box.y));
}
}
else {
for (auto& obj : outputs) {
obj.box.x = std::max(0, std::min(static_cast<int>(obj.box.x), originalWidth - 1));
obj.box.y = std::max(0, std::min(static_cast<int>(obj.box.y), originalHeight - 1));
obj.box.width = std::max(1, std::min(static_cast<int>(obj.box.width), originalWidth - obj.box.x));
obj.box.height = std::max(1, std::min(static_cast<int>(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<uchar> 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<int>(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<int>(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<ANSCENTER::Object> outputs = engine->RunInference(localImage, cameraId);
bool getJpeg = (getJpegString == 1);
std::string stImage;
if ((jpegImageSize > 0) && (jpegImageSize < maxImageSize)) {
int newWidth = jpegImageSize;
int newHeight = static_cast<int>(std::round(newWidth * static_cast<double>(originalHeight) / originalWidth));
float ratio = static_cast<float>(newWidth) / originalWidth;
for (auto& obj : outputs) {
obj.box.x = std::max(0, std::min(static_cast<int>(obj.box.x * ratio), newWidth - 1));
obj.box.y = std::max(0, std::min(static_cast<int>(obj.box.y * ratio), newHeight - 1));
obj.box.width = std::max(1, std::min(static_cast<int>(obj.box.width * ratio), newWidth - obj.box.x));
obj.box.height = std::max(1, std::min(static_cast<int>(obj.box.height * ratio), newHeight - obj.box.y));
}
}
else {
for (auto& obj : outputs) {
obj.box.x = std::max(0, std::min(static_cast<int>(obj.box.x), originalWidth - 1));
obj.box.y = std::max(0, std::min(static_cast<int>(obj.box.y), originalHeight - 1));
obj.box.width = std::max(1, std::min(static_cast<int>(obj.box.width), originalWidth - obj.box.x));
obj.box.height = std::max(1, std::min(static_cast<int>(obj.box.height), originalHeight - obj.box.y));
}
}
if (getJpeg) {
cv::Mat processedImage;
if ((jpegImageSize > 0) && (jpegImageSize < maxImageSize)) {
int newWidth = jpegImageSize;
int newHeight = static_cast<int>(std::round(newWidth * static_cast<double>(originalHeight) / originalWidth));
cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA);
}
else {
processedImage = localImage; // shallow copy (header only)
}
std::vector<uchar> 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<ANSCENTER::Object> objectDetectionResults;
std::vector<cv::Rect> bBox = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes);
const int originalWidth = localImage.cols;
const int originalHeight = localImage.rows;
const double scaleFactor = (maxImageSize > 0) ? static_cast<double>(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<int>(rect.x * scaleFactor);
scaledRect.y = static_cast<int>(rect.y * scaleFactor);
scaledRect.width = static_cast<int>(rect.width * scaleFactor);
scaledRect.height = static_cast<int>(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<ANSCENTER::Object> 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<int>(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<std::string> 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<std::string> 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<int>(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;
}
}
// ============================================================================
// V2 API — accepts uint64_t handle by value (eliminates LabVIEW buffer reuse bug)
// ============================================================================
#define ALPR_V2_HANDLE_SETUP(handleVal) \
ANSCENTER::ANSALPR* _v2Direct = reinterpret_cast<ANSCENTER::ANSALPR*>(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<int>(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<int>(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<int>(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<int>(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<int>(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<ANSCENTER::ANSALPR*>(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<ANSCENTER::Object> 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<int>(std::round(newWidth * static_cast<double>(originalHeight) / originalWidth));
ratio = static_cast<float>(newWidth) / originalWidth;
for (auto& obj : outputs) {
obj.box.x = std::max(0, std::min(static_cast<int>(obj.box.x * ratio), newWidth - 1));
obj.box.y = std::max(0, std::min(static_cast<int>(obj.box.y * ratio), newHeight - 1));
obj.box.width = std::max(1, std::min(static_cast<int>(obj.box.width * ratio), newWidth - obj.box.x));
obj.box.height = std::max(1, std::min(static_cast<int>(obj.box.height * ratio), newHeight - obj.box.y));
}
}
else {
for (auto& obj : outputs) {
obj.box.x = std::max(0, std::min(static_cast<int>(obj.box.x), originalWidth - 1));
obj.box.y = std::max(0, std::min(static_cast<int>(obj.box.y), originalHeight - 1));
obj.box.width = std::max(1, std::min(static_cast<int>(obj.box.width), originalWidth - obj.box.x));
obj.box.height = std::max(1, std::min(static_cast<int>(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<uchar> 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<int>(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<int>(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<ANSCENTER::ANSALPR*>(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<ANSCENTER::Object> objectDetectionResults;
std::vector<cv::Rect> bBox = ANSCENTER::ANSALPR::GetBoundingBoxes(strBboxes);
const int originalWidth = localImage.cols;
const int originalHeight = localImage.rows;
const double scaleFactor = (maxImageSize > 0) ? static_cast<double>(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<int>(rect.x * scaleFactor);
scaledRect.y = static_cast<int>(rect.y * scaleFactor);
scaledRect.width = static_cast<int>(rect.width * scaleFactor);
scaledRect.height = static_cast<int>(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<ANSCENTER::Object> 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<int>(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;
}
}