Add support to IMAQ conversion
This commit is contained in:
@@ -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<Image*>(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<Image**>(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<unsigned char> buf;
|
||||
std::vector<int> 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<int>(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<unsigned char*>(info.imageStart) + y * dstStride,
|
||||
mat.ptr<unsigned char>(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<unsigned char*>(info.imageStart) + y * dstStride * sizeof(unsigned short),
|
||||
mat.ptr<unsigned short>(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<unsigned char*>(info.imageStart) + y * dstStride * sizeof(RGBValue),
|
||||
bgra.ptr<unsigned char>(y), width * sizeof(RGBValue));
|
||||
}
|
||||
// Force alpha to 0 (cvtColor may set it to 255)
|
||||
std::vector<cv::Mat> 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<unsigned char*>(info.imageStart) + y * dstStride * sizeof(RGBValue),
|
||||
mat.ptr<unsigned char>(y), width * sizeof(RGBValue));
|
||||
}
|
||||
bgra = mat.clone();
|
||||
// Force alpha to 0 for IMAQ
|
||||
std::vector<cv::Mat> 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<unsigned char*>(info.imageStart) + y * dstStride * sizeof(float),
|
||||
mat.ptr<float>(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<UHandle*>(&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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user