Support BMP and JPG conversion
This commit is contained in:
@@ -5304,4 +5304,282 @@ extern "C" __declspec(dllexport) int ANSCV_ImageToArray(cv::Mat** imageIn, LVArr
|
||||
ANS_DBG("ANSCV", "ImageToArray: UNKNOWN EXCEPTION");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ── cv::Mat -> 1D U8 flat array (for .NET MemoryStream / Picture control) ───
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToFlatArray(
|
||||
cv::Mat** imageIn, LVArray1D_U8Hdl arrayOut, int* width, int* height, int* channels) {
|
||||
// Outputs raw BGR pixel data as a flat 1D U8 array (row-major, no padding)
|
||||
// width/height/channels describe the image layout
|
||||
try {
|
||||
if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !arrayOut || !width || !height || !channels) {
|
||||
ANS_DBG("ANSCV", "ImageToFlatArray: invalid input");
|
||||
return -2;
|
||||
}
|
||||
|
||||
const cv::Mat& mat = **imageIn;
|
||||
*width = mat.cols;
|
||||
*height = mat.rows;
|
||||
*channels = mat.channels();
|
||||
|
||||
ANS_DBG("ANSCV", "ImageToFlatArray: Mat=%p (%dx%d ch=%d type=%d)",
|
||||
(void*)*imageIn, mat.cols, mat.rows, mat.channels(), mat.type());
|
||||
|
||||
// Ensure continuous memory layout
|
||||
cv::Mat continuous = mat.isContinuous() ? mat : mat.clone();
|
||||
|
||||
int totalBytes = continuous.rows * continuous.cols * continuous.channels();
|
||||
|
||||
MgErr err = NumericArrayResize(uB, 1, reinterpret_cast<UHandle*>(&arrayOut), totalBytes);
|
||||
if (err != noErr) {
|
||||
ANS_DBG("ANSCV", "ImageToFlatArray: NumericArrayResize failed - err=%d", err);
|
||||
return -4;
|
||||
}
|
||||
(*arrayOut)->dimSizes[0] = totalBytes;
|
||||
memcpy((*arrayOut)->elt, continuous.data, totalBytes);
|
||||
|
||||
ANS_DBG("ANSCV", "ImageToFlatArray: SUCCESS - %d bytes (%dx%dx%d)",
|
||||
totalBytes, *width, *height, *channels);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ANS_DBG("ANSCV", "ImageToFlatArray: EXCEPTION - %s", e.what());
|
||||
return -3;
|
||||
}
|
||||
catch (...) {
|
||||
ANS_DBG("ANSCV", "ImageToFlatArray: UNKNOWN EXCEPTION");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 1D U8 raw pixel array -> JPEG string ───
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_FlatArrayToJpeg(
|
||||
LVArray1D_U8Hdl arrayIn, int width, int height, int channels, int quality, LStrHandle outputImage) {
|
||||
try {
|
||||
if (!arrayIn || !outputImage || width <= 0 || height <= 0 || channels <= 0) {
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: invalid input - w=%d h=%d ch=%d", width, height, channels);
|
||||
return -2;
|
||||
}
|
||||
|
||||
int expectedSize = width * height * channels;
|
||||
int actualSize = (*arrayIn)->dimSizes[0];
|
||||
if (actualSize < expectedSize) {
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: array too small - expected=%d actual=%d", expectedSize, actualSize);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (quality <= 0 || quality > 100) quality = 95;
|
||||
|
||||
// Wrap raw pixel data as cv::Mat (no copy)
|
||||
int cvType = (channels == 1) ? CV_8UC1 : (channels == 3) ? CV_8UC3 : CV_8UC4;
|
||||
cv::Mat mat(height, width, cvType, (*arrayIn)->elt);
|
||||
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: %dx%d ch=%d quality=%d", width, height, channels, quality);
|
||||
|
||||
// Encode to JPEG
|
||||
std::vector<unsigned char> buf;
|
||||
std::vector<int> params = {cv::IMWRITE_JPEG_QUALITY, quality};
|
||||
if (!cv::imencode(".jpg", mat, buf, params)) {
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: imencode failed");
|
||||
return -4;
|
||||
}
|
||||
|
||||
int size = static_cast<int>(buf.size());
|
||||
MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar));
|
||||
if (error != noErr) {
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: DSSetHandleSize failed - err=%d", error);
|
||||
return -4;
|
||||
}
|
||||
(*outputImage)->cnt = size;
|
||||
memcpy((*outputImage)->str, buf.data(), size);
|
||||
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: SUCCESS - %d bytes JPEG", size);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: EXCEPTION - %s", e.what());
|
||||
return -3;
|
||||
}
|
||||
catch (...) {
|
||||
ANS_DBG("ANSCV", "FlatArrayToJpeg: UNKNOWN EXCEPTION");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ── cv::Mat -> BMP binary (no compression, zero-cost encode for .NET) ───
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct BmpFileHeader {
|
||||
uint16_t type{0x4D42}; // "BM"
|
||||
uint32_t fileSize{0};
|
||||
uint16_t reserved1{0};
|
||||
uint16_t reserved2{0};
|
||||
uint32_t offsetData{0};
|
||||
};
|
||||
struct BmpInfoHeader {
|
||||
uint32_t size{40};
|
||||
int32_t width{0};
|
||||
int32_t height{0}; // negative = top-down (no flip needed)
|
||||
uint16_t planes{1};
|
||||
uint16_t bitCount{0};
|
||||
uint32_t compression{0};
|
||||
uint32_t sizeImage{0};
|
||||
int32_t xPPM{0};
|
||||
int32_t yPPM{0};
|
||||
uint32_t colorsUsed{0};
|
||||
uint32_t colorsImportant{0};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToBmp(cv::Mat** imageIn, int maxWidth, int& newWidth, int& newHeight, LStrHandle outputImage) {
|
||||
try {
|
||||
if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !outputImage) {
|
||||
ANS_DBG("ANSCV", "ImageToBmp: invalid input");
|
||||
return -2;
|
||||
}
|
||||
|
||||
cv::Mat mat = **imageIn;
|
||||
|
||||
ANS_DBG("ANSCV", "ImageToBmp: Mat=%p (%dx%d type=%d) maxWidth=%d",
|
||||
(void*)*imageIn, mat.cols, mat.rows, mat.type(), maxWidth);
|
||||
|
||||
// Resize if maxWidth > 0 and image is wider
|
||||
if (maxWidth > 0 && mat.cols > maxWidth) {
|
||||
double scale = static_cast<double>(maxWidth) / mat.cols;
|
||||
int targetHeight = static_cast<int>(mat.rows * scale);
|
||||
cv::resize(mat, mat, cv::Size(maxWidth, targetHeight), 0, 0, cv::INTER_AREA);
|
||||
ANS_DBG("ANSCV", "ImageToBmp: resized to %dx%d", mat.cols, mat.rows);
|
||||
}
|
||||
|
||||
int width = mat.cols;
|
||||
int height = mat.rows;
|
||||
newWidth = width;
|
||||
newHeight = height;
|
||||
|
||||
// Convert to BGR 24-bit
|
||||
cv::Mat bgr;
|
||||
switch (mat.type()) {
|
||||
case CV_8UC3:
|
||||
bgr = mat;
|
||||
break;
|
||||
case CV_8UC4:
|
||||
cv::cvtColor(mat, bgr, cv::COLOR_BGRA2BGR);
|
||||
break;
|
||||
case CV_8UC1:
|
||||
cv::cvtColor(mat, bgr, cv::COLOR_GRAY2BGR);
|
||||
break;
|
||||
default:
|
||||
ANS_DBG("ANSCV", "ImageToBmp: unsupported type %d", mat.type());
|
||||
return -5;
|
||||
}
|
||||
|
||||
// BMP rows must be aligned to 4 bytes
|
||||
int rowBytes = width * 3;
|
||||
int stride = (rowBytes + 3) & ~3; // round up to 4-byte boundary
|
||||
int padding = stride - rowBytes;
|
||||
int imageSize = stride * height;
|
||||
|
||||
// Build BMP file in memory
|
||||
BmpFileHeader fileHeader;
|
||||
BmpInfoHeader infoHeader;
|
||||
int headerSize = sizeof(BmpFileHeader) + sizeof(BmpInfoHeader);
|
||||
|
||||
fileHeader.offsetData = headerSize;
|
||||
fileHeader.fileSize = headerSize + imageSize;
|
||||
infoHeader.width = width;
|
||||
infoHeader.height = -height; // negative = top-down, no flip needed
|
||||
infoHeader.bitCount = 24;
|
||||
infoHeader.sizeImage = imageSize;
|
||||
|
||||
int totalSize = headerSize + imageSize;
|
||||
MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + totalSize);
|
||||
if (error != noErr) {
|
||||
ANS_DBG("ANSCV", "ImageToBmp: DSSetHandleSize failed - err=%d", error);
|
||||
return -4;
|
||||
}
|
||||
(*outputImage)->cnt = totalSize;
|
||||
|
||||
unsigned char* dst = (*outputImage)->str;
|
||||
|
||||
// Write headers
|
||||
memcpy(dst, &fileHeader, sizeof(BmpFileHeader));
|
||||
dst += sizeof(BmpFileHeader);
|
||||
memcpy(dst, &infoHeader, sizeof(BmpInfoHeader));
|
||||
dst += sizeof(BmpInfoHeader);
|
||||
|
||||
// Write pixel rows with padding
|
||||
unsigned char pad[4] = {0, 0, 0, 0};
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(dst, bgr.ptr(y), rowBytes);
|
||||
dst += rowBytes;
|
||||
if (padding > 0) {
|
||||
memcpy(dst, pad, padding);
|
||||
dst += padding;
|
||||
}
|
||||
}
|
||||
|
||||
ANS_DBG("ANSCV", "ImageToBmp: SUCCESS - %d bytes BMP (%dx%d)", totalSize, width, height);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ANS_DBG("ANSCV", "ImageToBmp: EXCEPTION - %s", e.what());
|
||||
return -3;
|
||||
}
|
||||
catch (...) {
|
||||
ANS_DBG("ANSCV", "ImageToBmp: UNKNOWN EXCEPTION");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ── BMP string -> JPEG string ───
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_BmpToJpeg(LStrHandle bmpInput, int quality, LStrHandle jpegOutput) {
|
||||
try {
|
||||
if (!bmpInput || !jpegOutput || (*bmpInput)->cnt <= 0) {
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: invalid input");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (quality <= 0 || quality > 100) quality = 85;
|
||||
|
||||
// Decode BMP from memory
|
||||
int bmpSize = (*bmpInput)->cnt;
|
||||
std::vector<unsigned char> bmpData((*bmpInput)->str, (*bmpInput)->str + bmpSize);
|
||||
cv::Mat mat = cv::imdecode(bmpData, cv::IMREAD_COLOR);
|
||||
if (mat.empty()) {
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: imdecode failed - %d bytes input", bmpSize);
|
||||
return -4;
|
||||
}
|
||||
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: decoded %dx%d, encoding JPEG q=%d", mat.cols, mat.rows, quality);
|
||||
|
||||
// Encode to JPEG using TurboJPEG if available, else cv::imencode
|
||||
std::string jpegStr = ANSCENTER::CompressJpegToString(mat, quality);
|
||||
if (jpegStr.empty()) {
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: JPEG encode failed");
|
||||
return -4;
|
||||
}
|
||||
|
||||
int size = static_cast<int>(jpegStr.size());
|
||||
MgErr error = DSSetHandleSize(jpegOutput, sizeof(int32) + size * sizeof(uChar));
|
||||
if (error != noErr) {
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: DSSetHandleSize failed - err=%d", error);
|
||||
return -4;
|
||||
}
|
||||
(*jpegOutput)->cnt = size;
|
||||
memcpy((*jpegOutput)->str, jpegStr.data(), size);
|
||||
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: SUCCESS - %d bytes BMP -> %d bytes JPEG", bmpSize, size);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: EXCEPTION - %s", e.what());
|
||||
return -3;
|
||||
}
|
||||
catch (...) {
|
||||
ANS_DBG("ANSCV", "BmpToJpeg: UNKNOWN EXCEPTION");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -178,5 +178,15 @@ 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);
|
||||
// cv::Mat -> 1D U8 array (raw BGR pixels, row-major) + width, height, channels
|
||||
typedef struct { int32 dimSizes[1]; uInt8 elt[1]; } LVArray1D_U8;
|
||||
typedef LVArray1D_U8** LVArray1D_U8Hdl;
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToFlatArray(cv::Mat** imageIn, LVArray1D_U8Hdl arrayOut, int* width, int* height, int* channels);
|
||||
// 1D U8 raw pixel array -> JPEG string
|
||||
extern "C" __declspec(dllexport) int ANSCV_FlatArrayToJpeg(LVArray1D_U8Hdl arrayIn, int width, int height, int channels, int quality, LStrHandle outputImage);
|
||||
// cv::Mat -> BMP binary string (no compression, fastest possible, for .NET Image.FromStream)
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToBmp(cv::Mat** imageIn, int maxWidth, int& newWidth, int& newHeight, LStrHandle outputImage);
|
||||
// BMP string -> JPEG string (for storage/network)
|
||||
extern "C" __declspec(dllexport) int ANSCV_BmpToJpeg(LStrHandle bmpInput, int quality, LStrHandle jpegOutput);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user