diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5a03e25..d13814a 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -110,7 +110,18 @@ "Bash(grep -l \"ANS_DBG\" modules/ANSLPR/*.cpp modules/ANSCV/*.cpp modules/ANSODEngine/*.cpp)", "Bash(grep -h \"ANS_DBG\\(\\\\\"\" modules/ANSLPR/*.cpp modules/ANSCV/ANSRTSP.cpp modules/ANSODEngine/ANSONNXYOLO.cpp modules/ANSODEngine/ANSRTYOLO.cpp modules/ANSODEngine/NV12PreprocessHelper.cpp)", "Bash(grep -v \"DNError\\\\|ViewerConfigPath\\\\|Failed to get\\\\|RecursiveDirectory\\\\|qt.qpa\\\\|DispBroker\\\\|SyncInvokeTable\\\\|Created new AppDomain\\\\|Destroying AppDomain\\\\|Trace Start\\\\|ExpandNode\\\\|PublisherMetadata\\\\|at System\\\\.\\\\|at NationalInstruments\\\\|at Mscorlib\\\\|Parameter name\\\\|^[[:space:]]*$\\\\|ArgumentException\\\\|Wrong type\\\\|Concerning target\\\\|Unable to get\\\\|RenderEventToBuffer\\\\|Getting next\\\\|Fetching Next\\\\|Image.Dispose\\\\|Graphics.Dispose\\\\|Image.FromStream\\\\|Inner Exception\\\\|FontFamily\\\\|InitHash\\\\|get_Item\\\\|MethodHandle.InvokeMethod\\\\|RuntimeMethodInfo\\\\|TargetInvocationException\\\\|Hashtable\\\\|LookupControl\\\\|RemoveControl\\\\|CloseInstance\\\\|FreeInstance\\\\|CrossDomainServer\" \"C:/Users/nghia/Downloads/AVNET-8845HS1.log\")", - "Bash(grep -E \"\\\\.\\(h|cpp\\)$\")" + "Bash(grep -E \"\\\\.\\(h|cpp\\)$\")", + "WebSearch", + "WebFetch(domain:forums.ni.com)", + "WebFetch(domain:www.ni.com)", + "WebFetch(domain:lavag.org)", + "Read(//c/ProgramData/**)", + "Read(//c/Users/nghia/**)", + "Bash(dir /s /b \"C:\\\\Program Files\\\\National Instruments\\\\*NIImage*\" \"C:\\\\Program Files\\\\National Instruments\\\\*OpenCV*\")", + "Bash(dir /s /b \"C:\\\\Program Files \\(x86\\)\\\\National Instruments\\\\Vision\\\\*OpenCV*\")", + "Bash(dir /s /b \"C:\\\\Program Files \\(x86\\)\\\\National Instruments\\\\Vision\\\\Help\\\\*\")", + "Bash(findstr /i \"opencv\")", + "WebFetch(domain:documentation.help)" ] } } diff --git a/modules/ANSCV/ANSOpenCV.cpp b/modules/ANSCV/ANSOpenCV.cpp index 7f56497..7eb0c75 100644 --- a/modules/ANSCV/ANSOpenCV.cpp +++ b/modules/ANSCV/ANSOpenCV.cpp @@ -5108,127 +5108,200 @@ extern "C" __declspec(dllexport) int ANSCV_AddSpeckleNoise_V2(uint64_t handleVal // ── IMAQ <-> cv::Mat conversion ────────────────────────────────── -extern "C" __declspec(dllexport) int ANSCV_IMAQ2Image(Image* imaqImage, cv::Mat** imageOut) { +extern "C" __declspec(dllexport) int ANSCV_IMAQ2Image(void* imaqHandle, cv::Mat** imageOut) { try { - if (!imaqImage || !imageOut) return -2; + if (!imaqHandle || !imageOut) { + ANS_DBG("ANSCV", "IMAQ2Image: null pointer - imaqHandle=%p imageOut=%p", imaqHandle, (void*)imageOut); + return -2; + } - ImageInfo info; - if (!imaqGetImageInfo(imaqImage, &info)) return -4; - if (!info.imageStart || info.xRes <= 0 || info.yRes <= 0) return -2; + // Try as Image* first (direct pointer) + Image* imaqImage = static_cast(imaqHandle); + ImageInfo info = {}; + if (!imaqGetImageInfo(imaqImage, &info)) { + // Try as Image** (pointer-to-pointer, LabVIEW handle indirection) + ANS_DBG("ANSCV", "IMAQ2Image: Image* failed (err=%d), trying Image**", imaqGetLastError()); + Image** imaqImagePtr = static_cast(imaqHandle); + imaqImage = *imaqImagePtr; + if (!imaqImage || !imaqGetImageInfo(imaqImage, &info)) { + int errCode = imaqGetLastError(); + const char* errFunc = imaqGetLastErrorFunc(); + ANS_DBG("ANSCV", "IMAQ2Image: FAILED both Image* and Image** - err=%d func=%s", + errCode, errFunc ? errFunc : "unknown"); + return -4; + } + ANS_DBG("ANSCV", "IMAQ2Image: resolved as Image** OK"); + } else { + ANS_DBG("ANSCV", "IMAQ2Image: resolved as Image* OK"); + } + + if (!info.imageStart || info.xRes <= 0 || info.yRes <= 0) { + ANS_DBG("ANSCV", "IMAQ2Image: invalid image - start=%p xRes=%d yRes=%d", + info.imageStart, info.xRes, info.yRes); + return -2; + } int width = info.xRes; int height = info.yRes; - int stride = info.pixelsPerLine; // pixels per row (may include padding) + int stride = info.pixelsPerLine; + + ANS_DBG("ANSCV", "IMAQ2Image: %dx%d stride=%d type=%d", width, height, stride, info.imageType); cv::Mat result; switch (info.imageType) { case IMAQ_IMAGE_U8: { - // 8-bit grayscale: stride is in pixels = bytes cv::Mat wrapper(height, width, CV_8UC1, info.imageStart, stride * sizeof(unsigned char)); result = wrapper.clone(); break; } case IMAQ_IMAGE_U16: { - // 16-bit grayscale cv::Mat wrapper(height, width, CV_16UC1, info.imageStart, stride * sizeof(unsigned short)); result = wrapper.clone(); break; } case IMAQ_IMAGE_RGB: { - // IMAQ RGB is 32-bit RGBX (RGBValue: B,G,R,alpha) — same layout as BGRA cv::Mat bgra(height, width, CV_8UC4, info.imageStart, stride * sizeof(RGBValue)); cv::cvtColor(bgra, result, cv::COLOR_BGRA2BGR); break; } case IMAQ_IMAGE_SGL: { - // 32-bit float grayscale cv::Mat wrapper(height, width, CV_32FC1, info.imageStart, stride * sizeof(float)); result = wrapper.clone(); break; } default: - return -5; // Unsupported image type + ANS_DBG("ANSCV", "IMAQ2Image: unsupported IMAQ type %d", info.imageType); + return -5; } *imageOut = anscv_mat_new(result); + ANS_DBG("ANSCV", "IMAQ2Image: SUCCESS - Mat=%p %dx%d type=%d", + (void*)*imageOut, result.cols, result.rows, result.type()); return 1; } catch (const std::exception& e) { - std::cerr << "Error in ANSCV_IMAQ2Image: " << e.what() << std::endl; + ANS_DBG("ANSCV", "IMAQ2Image: EXCEPTION - %s", e.what()); return -3; } - catch (...) { return -1; } + catch (...) { + ANS_DBG("ANSCV", "IMAQ2Image: UNKNOWN EXCEPTION"); + return -1; + } } -extern "C" __declspec(dllexport) int ANSCV_Image2IMAQ(cv::Mat** imageIn, Image* imaqImage) { +extern "C" __declspec(dllexport) int ANSCV_Image2IMAQ(cv::Mat** imageIn, LStrHandle outputImage) { try { - if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !imaqImage) return -2; + if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !outputImage) { + ANS_DBG("ANSCV", "Image2IMAQ: null input - imageIn=%p outputImage=%p", + (void*)imageIn, (void*)outputImage); + return -2; + } const cv::Mat& mat = **imageIn; - int width = mat.cols; - int height = mat.rows; + ANS_DBG("ANSCV", "Image2IMAQ: Mat=%p (%dx%d type=%d)", + (void*)*imageIn, mat.cols, mat.rows, mat.type()); - // Set the IMAQ image size (allocates pixel buffer) - if (!imaqSetImageSize(imaqImage, width, height)) return -4; + // Encode as lossless PNG + std::vector buf; + std::vector params = {cv::IMWRITE_PNG_COMPRESSION, 1}; + if (!cv::imencode(".png", mat, buf, params)) { + ANS_DBG("ANSCV", "Image2IMAQ: imencode PNG failed"); + return -4; + } - ImageInfo info; - if (!imaqGetImageInfo(imaqImage, &info)) return -4; - if (!info.imageStart) return -4; + int size = static_cast(buf.size()); + MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); + if (error != noErr) { + ANS_DBG("ANSCV", "Image2IMAQ: DSSetHandleSize failed - err=%d", error); + return -4; + } + (*outputImage)->cnt = size; + memcpy((*outputImage)->str, buf.data(), size); - int dstStride = info.pixelsPerLine; + ANS_DBG("ANSCV", "Image2IMAQ: SUCCESS - %d bytes PNG (%dx%d)", size, mat.cols, mat.rows); + return 1; + } + catch (const std::exception& e) { + ANS_DBG("ANSCV", "Image2IMAQ: EXCEPTION - %s", e.what()); + return -3; + } + catch (...) { + ANS_DBG("ANSCV", "Image2IMAQ: UNKNOWN EXCEPTION"); + return -1; + } +} +// ── cv::Mat -> LabVIEW 2D U32 array for IMAQ ArrayToColorImage VI ─── + +extern "C" __declspec(dllexport) int ANSCV_ImageToArray(cv::Mat** imageIn, LVArray2D_U32Hdl arrayOut) { + // Outputs 2D U32 array (height x width), each U32 = packed XRGB + // Wire to IMAQ ArrayToColorImage "Image Pixels (U32)" input + try { + if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !arrayOut) { + ANS_DBG("ANSCV", "ImageToArray: invalid input"); + return -2; + } + + const cv::Mat& mat = **imageIn; + int rows = mat.rows; + int cols = mat.cols; + ANS_DBG("ANSCV", "ImageToArray: Mat=%p (%dx%d type=%d)", + (void*)*imageIn, cols, rows, mat.type()); + + // Convert to BGRA with alpha=0 (IMAQ convention) + cv::Mat bgra; switch (mat.type()) { - case CV_8UC1: { - // Grayscale -> IMAQ_IMAGE_U8 - for (int y = 0; y < height; y++) { - memcpy(static_cast(info.imageStart) + y * dstStride, - mat.ptr(y), width * sizeof(unsigned char)); - } - break; - } - case CV_16UC1: { - // 16-bit grayscale -> IMAQ_IMAGE_U16 - for (int y = 0; y < height; y++) { - memcpy(static_cast(info.imageStart) + y * dstStride * sizeof(unsigned short), - mat.ptr(y), width * sizeof(unsigned short)); - } - break; - } case CV_8UC3: { - // BGR -> IMAQ_IMAGE_RGB (BGRA with alpha=0) - cv::Mat bgra; cv::cvtColor(mat, bgra, cv::COLOR_BGR2BGRA); - for (int y = 0; y < height; y++) { - memcpy(static_cast(info.imageStart) + y * dstStride * sizeof(RGBValue), - bgra.ptr(y), width * sizeof(RGBValue)); - } + // Force alpha to 0 (cvtColor may set it to 255) + std::vector ch; + cv::split(bgra, ch); + ch[3].setTo(0); + cv::merge(ch, bgra); break; } case CV_8UC4: { - // BGRA -> IMAQ_IMAGE_RGB directly - for (int y = 0; y < height; y++) { - memcpy(static_cast(info.imageStart) + y * dstStride * sizeof(RGBValue), - mat.ptr(y), width * sizeof(RGBValue)); - } + bgra = mat.clone(); + // Force alpha to 0 for IMAQ + std::vector ch; + cv::split(bgra, ch); + ch[3].setTo(0); + cv::merge(ch, bgra); break; } - case CV_32FC1: { - // Float -> IMAQ_IMAGE_SGL - for (int y = 0; y < height; y++) { - memcpy(static_cast(info.imageStart) + y * dstStride * sizeof(float), - mat.ptr(y), width * sizeof(float)); - } + case CV_8UC1: + cv::cvtColor(mat, bgra, cv::COLOR_GRAY2BGRA); + // cvtColor sets alpha to 0, which is correct for IMAQ break; - } default: - return -5; // Unsupported cv::Mat type + ANS_DBG("ANSCV", "ImageToArray: unsupported type %d", mat.type()); + return -5; } + int totalPixels = rows * cols; + + // Resize LabVIEW 2D U32 array + MgErr err = NumericArrayResize(uL, 2, reinterpret_cast(&arrayOut), totalPixels); + if (err != noErr) { + ANS_DBG("ANSCV", "ImageToArray: NumericArrayResize failed - err=%d", err); + return -4; + } + (*arrayOut)->dimSizes[0] = rows; + (*arrayOut)->dimSizes[1] = cols; + + // IMAQ RGBValue layout is {B, G, R, alpha} which is the same memory layout as BGRA + // Copy directly — each 4 bytes of BGRA = one U32 in the array + memcpy((*arrayOut)->elt, bgra.data, totalPixels * sizeof(uInt32)); + + ANS_DBG("ANSCV", "ImageToArray: SUCCESS - %dx%d (%d pixels)", cols, rows, totalPixels); return 1; } catch (const std::exception& e) { - std::cerr << "Error in ANSCV_Image2IMAQ: " << e.what() << std::endl; + ANS_DBG("ANSCV", "ImageToArray: EXCEPTION - %s", e.what()); return -3; } - catch (...) { return -1; } + catch (...) { + ANS_DBG("ANSCV", "ImageToArray: UNKNOWN EXCEPTION"); + return -1; + } } \ No newline at end of file diff --git a/modules/ANSCV/ANSOpenCV.h b/modules/ANSCV/ANSOpenCV.h index 006c399..953419c 100644 --- a/modules/ANSCV/ANSOpenCV.h +++ b/modules/ANSCV/ANSOpenCV.h @@ -167,8 +167,16 @@ extern "C" __declspec(dllexport) int ANSCV_ImagePatternMatchs_S(cv::Mat** image extern "C" __declspec(dllexport) int ANSCV_ImagesToMP4_S(const char* imageFolder, const char* outputVideoPath, int targetDurationSec); -// IMAQ <-> cv::Mat conversion functions -extern "C" __declspec(dllexport) int ANSCV_IMAQ2Image(Image* imaqImage, cv::Mat** imageOut); -extern "C" __declspec(dllexport) int ANSCV_Image2IMAQ(cv::Mat** imageIn, Image* imaqImage); +// IMAQ -> cv::Mat conversion (NI Vision Image*, auto-detects indirection level) +extern "C" __declspec(dllexport) int ANSCV_IMAQ2Image(void* imaqHandle, cv::Mat** imageOut); +// cv::Mat -> IMAQ: outputs lossless PNG to LStrHandle, use IMAQ ReadFromString in LabVIEW +extern "C" __declspec(dllexport) int ANSCV_Image2IMAQ(cv::Mat** imageIn, LStrHandle outputImage); + +// cv::Mat -> LabVIEW 2D U32 array for IMAQ ArrayToColorImage VI "Image Pixels (U32)" +typedef struct { int32 dimSizes[2]; uInt32 elt[1]; } LVArray2D_U32; +typedef LVArray2D_U32** LVArray2D_U32Hdl; +extern "C" __declspec(dllexport) int ANSCV_ImageToArray(cv::Mat** imageIn, LVArray2D_U32Hdl arrayOut); +// cv::Mat -> IMAQ via LStrHandle (lossless PNG) +extern "C" __declspec(dllexport) int ANSCV_Image2IMAQ(cv::Mat** imageIn, LStrHandle outputImage); #endif \ No newline at end of file