Files
ANSCORE/modules/ANSOCR/dllmain.cpp

1115 lines
38 KiB
C++

// 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 <json.hpp>
#include "NV12PreprocessHelper.h"
#include <unordered_map>
#include <condition_variable>
#include <cstdint>
// Handle registry with refcount — prevents use-after-free when
// ReleaseANSOCRHandle is called while inference is still running.
static std::unordered_map<ANSCENTER::ANSOCRBase*, int>& OCRHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSOCRBase*, int> 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<std::mutex> lk(OCRHandleRegistryMutex());
OCRHandleRegistry()[h] = 1; // refcount = 1
}
static ANSCENTER::ANSOCRBase* AcquireOCRHandle(ANSCENTER::ANSOCRBase* h) {
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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:
case DLL_PROCESS_DETACH:
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<ANSCENTER::OCRObject>& 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<ANSCENTER::OCRObject> 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<ANSCENTER::OCRObject> 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<ANSCENTER::OCRObject> 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<ANSCENTER::OCRObject> 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<ANSCENTER::OCRMode>(ocrMode));
return 0;
}
extern "C" ANSOCR_API int SetANSOCRALPRCountry(ANSCENTER::ANSOCRBase** Handle, int country) {
if (!Handle || !*Handle) return -1;
(*Handle)->SetALPRCountry(static_cast<ANSCENTER::ALPRCountry>(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<ANSCENTER::ALPRCountry>(j.value("country", 99));
fmt.numRows = j.value("num_rows", 2);
fmt.rowSplitThreshold = j.value("row_split_threshold", 0.3f);
static const std::map<std::string, ANSCENTER::ALPRCharClass> 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<std::string>();
}
}
fmt.zones.push_back(zone);
}
(*Handle)->SetALPRFormat(fmt);
return 0;
} catch (...) {
return -2;
}
}
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<ANSCENTER::OCRObject> 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<cv::Rect> bBoxes = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes);
std::vector<ANSCENTER::OCRObject> 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<cv::Rect> bBoxes = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes);
std::vector<ANSCENTER::OCRObject> 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<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" 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<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" 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<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" 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<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" 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<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" 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<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" 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<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" 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<ANSCENTER::OCRObject> 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 = localImage;
if (resizeNeeded) {
cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA);
}
std::vector<uchar> 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<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 (...) { 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<ANSCENTER::OCRObject> objectDetectionResults;
std::vector<cv::Rect> bBox = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes);
const int originalWidth = localImage.cols;
const int originalHeight = localImage.rows;
const double scaleFactor = (maxImageSize > 0) ? static_cast<double>(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<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::OCRObject> 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<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 (...) { 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<ANSCENTER::ANSOCRBase*>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<ANSCENTER::ANSOCRBase*>(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<ANSCENTER::OCRObject> 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 = localImage;
if (resizeNeeded) {
cv::resize(localImage, processedImage, cv::Size(newWidth, newHeight), 0, 0, cv::INTER_AREA);
}
std::vector<uchar> 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<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 (...) { 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<ANSCENTER::ANSOCRBase*>(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<ANSCENTER::OCRObject> objectDetectionResults;
std::vector<cv::Rect> bBox = ANSCENTER::ANSOCRUtility::GetBoundingBoxes(strBboxes);
const int originalWidth = localImage.cols;
const int originalHeight = localImage.rows;
const double scaleFactor = (maxImageSize > 0) ? static_cast<double>(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<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::OCRObject> 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<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 (...) { 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<uint64_t>(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<ANSCENTER::ANSOCRBase*>(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;
}
}