#include "ANSOpenCV.h" #include "ANSMatRegistry.h" #include #include #include #include #include "boost/property_tree/ptree.hpp" #include "boost/property_tree/json_parser.hpp" #include "boost/foreach.hpp" #include "ReadBarcode.h" #include "sys_inc.h" #include "rtsp_cln.h" #include "hqueue.h" #include "http.h" #include "http_parse.h" #include "rtsp_player.h" #include #include #include #include #include #include #include #include #include // SIMD includes #ifdef __AVX2__ #include #elif defined(__SSE2__) #include #endif extern "C" { #include #include #include } std::mutex imageMutex; // Global mutex for thread safety std::timed_mutex timeImageMutex; static bool ansCVLicenceValid = false; // Global once_flag to protect license checking static std::once_flag ansCVLicenseOnceFlag; //template //T GetData(const boost::property_tree::ptree& pt, const std::string& key) //{ // T ret; // if (boost::optional data = pt.get_optional(key)) // { // ret = data.get(); // } // return ret; //} namespace fs = std::filesystem; namespace ANSCENTER { // Global function to verify license only once static void VerifyGlobalLicense(const std::string& licenseKey) { ansCVLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1007, "ANSCV"); if (!ansCVLicenceValid) { // we also support ANSTS license ansCVLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS) } if (!ansCVLicenceValid) { // we also support ANSTS license ansCVLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1008, "ANSTS");//Default productId=1008 (ANSTS) } } TurboJpegCompressor::TurboJpegCompressor() { _handle = tjInitCompress(); if (!_handle) { std::cerr << "Failed to initialize TurboJPEG: " << tjGetErrorStr() << std::endl; std::exit(1); } // Pre-allocate buffer to avoid tjAlloc/tjFree overhead _bufferSize = 2 * 1024 * 1024; // 2MB - should handle most images _buffer = static_cast(tjAlloc(_bufferSize)); if (!_buffer) { std::cerr << "Failed to allocate JPEG buffer" << std::endl; tjDestroy(_handle); std::exit(1); } } TurboJpegCompressor::~TurboJpegCompressor() noexcept { if (_buffer) { tjFree(_buffer); _buffer = nullptr; } if (_handle) { tjDestroy(_handle); _handle = nullptr; } } // Your original logic with minimal optimizations std::string TurboJpegCompressor::compress(const cv::Mat& image, int quality) { if (image.empty()) { std::cerr << "Error: Input image is empty!" << std::endl; return ""; } int pixelFormat; int subsampling; if (image.type() == CV_8UC1) { pixelFormat = TJPF_GRAY; subsampling = TJSAMP_GRAY; } else if (image.type() == CV_8UC3) { pixelFormat = TJPF_BGR; subsampling = TJSAMP_420; } else { std::cerr << "Error: Unsupported image format!" << std::endl; return ""; } // Try with pre-allocated buffer first (fastest path) unsigned long jpegSize = _bufferSize; if (tjCompress2(_handle, image.data, image.cols, 0, image.rows, pixelFormat, &_buffer, &jpegSize, subsampling, quality, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) >= 0) { return std::string(reinterpret_cast(_buffer), jpegSize); } // Fallback to dynamic allocation (your original method) unsigned char* jpegBuf = nullptr; jpegSize = 0; if (tjCompress2(_handle, image.data, image.cols, 0, image.rows, pixelFormat, &jpegBuf, &jpegSize, subsampling, quality, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) < 0) { std::cerr << "JPEG compression failed: " << tjGetErrorStr() << std::endl; return ""; } std::string jpegString(reinterpret_cast(jpegBuf), jpegSize); tjFree(jpegBuf); // If we needed more space, expand our buffer for next time if (jpegSize > _bufferSize) { tjFree(_buffer); _bufferSize = jpegSize * 2; _buffer = static_cast(tjAlloc(_bufferSize)); } return jpegString; } std::string CompressJpegToString(const cv::Mat& image, int quality) { static thread_local TurboJpegCompressor compressor; return compressor.compress(image, quality); } void ANSOPENCV::CheckLicense() { try { // Check once globally std::call_once(ansCVLicenseOnceFlag, [this]() { VerifyGlobalLicense(_licenseKey); }); // Update this instance's local license flag _licenseValid = ansCVLicenceValid; } catch (const std::exception& e) { this->_logger.LogFatal("ANSOPENCV::CheckLicense. Error:", e.what(), __FILE__, __LINE__); } } bool ANSOPENCV::Init(std::string licenseKey) { if (ansCVLicenceValid) { // Global check _licenseValid = true; return true; } { std::lock_guard lock(_mutex); if (_licenseKey.empty()) { _licenseKey = licenseKey; } else if (_licenseKey != licenseKey) { std::cerr << "Warning: Attempt to reset license key with a different value!" << std::endl; } } CheckLicense(); return _licenseValid; } //std::string ANSOPENCV::EncodeJpegString(const cv::Mat& img, int quality) { // std::lock_guard lock(_mutex); // tjhandle _jpegCompressor = nullptr; // unsigned char* jpegBuf = nullptr; // try { // _jpegCompressor = tjInitCompress(); // if (!_jpegCompressor) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString. Failed to initialize TurboJPEG compressor.", tjGetErrorStr(), __FILE__, __LINE__); // return ""; // } // int maxBufferSize = img.cols * img.rows * 3; // jpegBuf = new unsigned char[maxBufferSize]; // Pre-allocated buffer // long unsigned int jpegSize = maxBufferSize; // Size of the JPEG image (output) // int subsamp = TJSAMP_444; // Chroma subsampling: TJSAMP_444, TJSAMP_422, TJSAMP_420, etc. // int pixelFormat = img.channels() == 3 ? TJPF_BGR : TJPF_GRAY; // Pixel format based on channels // // Compress the image into the pre-allocated buffer // int result = tjCompress2(_jpegCompressor, img.data, img.cols, 0, img.rows, pixelFormat, // &jpegBuf, &jpegSize, subsamp, quality, TJFLAG_FASTDCT); // // Handle compression errors // if (result != 0) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString. Compression error:", tjGetErrorStr(), __FILE__, __LINE__); // if (jpegBuf) { // tjFree(jpegBuf); // Free the buffer if allocated // } // tjDestroy(_jpegCompressor); // Destroy the TurboJPEG compressor // return ""; // } // // Create a string from the JPEG buffer // std::string jpegString(reinterpret_cast(jpegBuf), jpegSize); // // Clean up resources // tjFree(jpegBuf); // tjDestroy(_jpegCompressor); // return jpegString; // } // catch (std::exception& e) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString:", e.what(), __FILE__, __LINE__); // // Clean up resources in case of an exception // if (jpegBuf) { // tjFree(jpegBuf); // } // if (_jpegCompressor) { // tjDestroy(_jpegCompressor); // } // } // // Return an empty string in case of failure // return ""; //} //std::string ANSOPENCV::EncodeJpegString(const cv::Mat& img, int quality) { // std::lock_guard lock(_mutex); // tjhandle _jpegCompressor = nullptr; // unsigned char* jpegBuf = nullptr; // try { // // Validate input // if (img.empty()) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString. Empty image.", "", __FILE__, __LINE__); // return ""; // } // // Determine pixel format // int pixelFormat; // if (img.channels() == 1) { // pixelFormat = TJPF_GRAY; // } // else if (img.channels() == 3) { // pixelFormat = TJPF_BGR; // } // else if (img.channels() == 4) { // pixelFormat = TJPF_BGRA; // } // else { // this->_logger.LogError("ANSOPENCV::EncodeJpegString. Unsupported channel count: " + std::to_string(img.channels()), "", __FILE__, __LINE__); // return ""; // } // _jpegCompressor = tjInitCompress(); // if (!_jpegCompressor) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString. Failed to initialize TurboJPEG compressor.", tjGetErrorStr(), __FILE__, __LINE__); // return ""; // } // // Correct buffer size based on actual channels // unsigned long maxBufferSize = tjBufSize(img.cols, img.rows, TJSAMP_444); // jpegBuf = new unsigned char[maxBufferSize]; // unsigned long jpegSize = maxBufferSize; // int subsamp = (pixelFormat == TJPF_GRAY) ? TJSAMP_GRAY : TJSAMP_444; // int pitch = img.step[0]; // int result = tjCompress2(_jpegCompressor, img.data, img.cols, pitch, img.rows, pixelFormat, // &jpegBuf, &jpegSize, subsamp, quality, TJFLAG_FASTDCT); // if (result != 0) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString. Compression error:", tjGetErrorStr(), __FILE__, __LINE__); // tjFree(jpegBuf); // tjDestroy(_jpegCompressor); // return ""; // } // std::string jpegString(reinterpret_cast(jpegBuf), jpegSize); // tjFree(jpegBuf); // tjDestroy(_jpegCompressor); // return jpegString; // } // catch (std::exception& e) { // this->_logger.LogError("ANSOPENCV::EncodeJpegString:", e.what(), __FILE__, __LINE__); // if (jpegBuf) tjFree(jpegBuf); // if (_jpegCompressor) tjDestroy(_jpegCompressor); // } // return ""; //} std::string ANSOPENCV::EncodeJpegString(const cv::Mat& img, int quality) { std::lock_guard lock(_mutex); tjhandle _jpegCompressor = nullptr; unsigned char* jpegBuf = nullptr; try { // Validate input if (img.empty()) { this->_logger.LogError("ANSOPENCV::EncodeJpegString. Empty image.", "", __FILE__, __LINE__); return ""; } // Ensure continuous memory layout cv::Mat continuous_img; if (!img.isContinuous()) { continuous_img = img.clone(); } else { continuous_img = img; } // Determine pixel format int pixelFormat; int subsamp; if (continuous_img.channels() == 1) { pixelFormat = TJPF_GRAY; subsamp = TJSAMP_GRAY; } else if (continuous_img.channels() == 3) { pixelFormat = TJPF_BGR; subsamp = TJSAMP_444; } else if (continuous_img.channels() == 4) { pixelFormat = TJPF_BGRA; subsamp = TJSAMP_444; } else { this->_logger.LogError("ANSOPENCV::EncodeJpegString. Unsupported channel count: " + std::to_string(continuous_img.channels()), "", __FILE__, __LINE__); return ""; } _jpegCompressor = tjInitCompress(); if (!_jpegCompressor) { this->_logger.LogError("ANSOPENCV::EncodeJpegString. Failed to initialize TurboJPEG compressor.", tjGetErrorStr(), __FILE__, __LINE__); return ""; } // Use tjAlloc instead of new[] - this is critical for TurboJPEG unsigned long maxBufferSize = tjBufSize(continuous_img.cols, continuous_img.rows, subsamp); jpegBuf = tjAlloc(maxBufferSize); if (!jpegBuf) { this->_logger.LogError("ANSOPENCV::EncodeJpegString. Failed to allocate JPEG buffer.", "", __FILE__, __LINE__); tjDestroy(_jpegCompressor); return ""; } unsigned long jpegSize = maxBufferSize; // Use pitch = 0 since we ensured continuous memory int result = tjCompress2(_jpegCompressor, continuous_img.data, continuous_img.cols, 0, continuous_img.rows, pixelFormat, &jpegBuf, &jpegSize, subsamp, quality, TJFLAG_FASTDCT); if (result != 0) { this->_logger.LogError("ANSOPENCV::EncodeJpegString. Compression error (format=" + std::to_string(pixelFormat) + ", subsamp=" + std::to_string(subsamp) + ", quality=" + std::to_string(quality) + "):", tjGetErrorStr(), __FILE__, __LINE__); tjFree(jpegBuf); tjDestroy(_jpegCompressor); return ""; } // Create string from compressed buffer std::string jpegString(reinterpret_cast(jpegBuf), jpegSize); // Clean up tjFree(jpegBuf); tjDestroy(_jpegCompressor); return jpegString; } catch (const std::exception& e) { this->_logger.LogError("ANSOPENCV::EncodeJpegString:", e.what(), __FILE__, __LINE__); if (jpegBuf) tjFree(jpegBuf); if (_jpegCompressor) tjDestroy(_jpegCompressor); } catch (...) { this->_logger.LogError("ANSOPENCV::EncodeJpegString: Unknown exception", "", __FILE__, __LINE__); if (jpegBuf) tjFree(jpegBuf); if (_jpegCompressor) tjDestroy(_jpegCompressor); } return ""; } std::string ANSOPENCV::MatToBinaryData(const cv::Mat& image) { std::lock_guard lock(_mutex); // Check if the image is empty or has invalid data if (image.empty() || !image.data || !image.u) { return ""; } try { // Encode the image to a memory buffer return EncodeJpegString(image, 100); } catch (const std::exception& e) { this->_logger.LogFatal("ANSOPENCV::MatToBinaryData. Exception occurred:", e.what(), __FILE__, __LINE__); } catch (...) { this->_logger.LogFatal("ANSWEBCAMPlayer::MatToBinaryData.", "Unknown exception occurred.", __FILE__, __LINE__); } // Return an empty string in case of failure return ""; } void ANSOPENCV::ImageResize(const cv::Mat& inputFrame, int width, int height, cv::Mat& outputFrame) { std::lock_guard lock(_mutex); if (!_licenseValid) { outputFrame = inputFrame; return; } if (inputFrame.empty()) { outputFrame = inputFrame; return; } // Validate dimensions if (width <= 0 || height <= 0) { std::cerr << "Error: Invalid dimensions in ImageResize (" << width << "x" << height << ")" << std::endl; outputFrame = inputFrame; return; } // Check if resize is actually needed if (inputFrame.cols == width && inputFrame.rows == height) { outputFrame = inputFrame; return; } try { // Choose interpolation based on scaling direction int interpolation; const double scaleX = static_cast(width) / inputFrame.cols; const double scaleY = static_cast(height) / inputFrame.rows; const double scale = min(scaleX, scaleY); if (scale < 1.0) { // Downscaling - use INTER_AREA (best quality and fastest) interpolation = cv::INTER_AREA; } else { // Upscaling - use INTER_LINEAR (good balance) interpolation = cv::INTER_LINEAR; } cv::resize(inputFrame, outputFrame, cv::Size(width, height), 0, 0, interpolation); } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ImageResize: " << e.what() << std::endl; outputFrame = inputFrame; } catch (const std::exception& e) { std::cerr << "Exception in ImageResize: " << e.what() << std::endl; outputFrame = inputFrame; } catch (...) { std::cerr << "Unknown exception in ImageResize" << std::endl; outputFrame = inputFrame; } } void ANSOPENCV::ImageResizeWithRatio(const cv::Mat& inputFrame, int width, cv::Mat& outputFrame) { std::lock_guard lock(_mutex); if (!_licenseValid) { outputFrame = inputFrame; // Shallow copy (fast) return; } // Validate inputs outside mutex if (inputFrame.empty() || width <= 0) { outputFrame = inputFrame; return; } // Check if resize is needed if (inputFrame.cols == width) { outputFrame = inputFrame; // Already correct width, no resize needed return; } try { // Calculate new height maintaining aspect ratio double aspectRatio = static_cast(inputFrame.cols) / inputFrame.rows; int height = static_cast(std::round(width / aspectRatio)); // Validate calculated height if (height <= 0) { std::cerr << "Error: Calculated height is invalid in ImageResizeWithRatio! " << "Width: " << width << ", AspectRatio: " << aspectRatio << std::endl; outputFrame = inputFrame; return; } // Choose interpolation based on scaling direction int interpolation; if (width < inputFrame.cols) { // Downscaling: INTER_AREA is best (good quality, fast) interpolation = cv::INTER_AREA; } else { // Upscaling: INTER_LINEAR is good enough and fast interpolation = cv::INTER_LINEAR; } cv::resize(inputFrame, outputFrame, cv::Size(width, height), 0, 0, interpolation); } catch (const cv::Exception& e) { std::cerr << "OpenCV error in ImageResizeWithRatio: " << e.what() << std::endl; outputFrame = inputFrame; // Shallow copy fallback } catch (...) { std::cerr << "Unknown error in ImageResizeWithRatio!" << std::endl; outputFrame = inputFrame; // Shallow copy fallback } } cv::Mat ANSOPENCV::BlurObjects(const cv::Mat& image, const std::vector& objects) { std::lock_guard lock(_mutex); // Check for valid license and empty input if (!_licenseValid || image.empty()) return image; // Create a copy of the original image to apply the blurring cv::Mat outputImage = image.clone(); // Define blur parameters const int blurKernelSize = 45; // Kernel size for blurring cv::Size kernelSize(blurKernelSize, blurKernelSize); // Apply blur to each object ROI for (const auto& obj : objects) { // Ensure the ROI is within the image bounds cv::Rect boundedRect = obj & cv::Rect(0, 0, image.cols, image.rows); if (boundedRect.area() > 0) { // Check if the bounded rect is valid cv::GaussianBlur(outputImage(boundedRect), outputImage(boundedRect), kernelSize, 0); } } return outputImage; } cv::Mat ANSOPENCV::BlurBackground(const cv::Mat& image, const std::vector& objects) { std::lock_guard lock(_mutex); // Check for valid license and empty input if (!_licenseValid || image.empty()) return image; // Define blur parameters const int blurKernelSize = 45; // Kernel size for blurring cv::Size kernelSize(blurKernelSize, blurKernelSize); // Blur the entire image cv::Mat blurredImage; cv::GaussianBlur(image, blurredImage, kernelSize, 0); // Copy each ROI from the original image onto the blurred image for (const auto& obj : objects) { // Validate that the ROI is within image bounds cv::Rect boundedRect = obj & cv::Rect(0, 0, image.cols, image.rows); if (boundedRect.area() > 0) { // Proceed if the rect is valid image(boundedRect).copyTo(blurredImage(boundedRect)); } } return blurredImage; } cv::Mat ANSOPENCV::ToGray(const cv::Mat& image) { std::lock_guard lock(_mutex); // Check for valid license if (!_licenseValid) return image; // Return the original if the image is empty if (image.empty()) return image; // Prepare the grayscale output cv::Mat grayMat; // Convert based on channel count switch (image.channels()) { case 3: cv::cvtColor(image, grayMat, cv::COLOR_BGR2GRAY); break; case 4: cv::cvtColor(image, grayMat, cv::COLOR_BGRA2GRAY); break; case 1: grayMat = image.clone(); // Clone to ensure a copy is returned break; default: // Unsupported number of channels, return the original std::cerr << "Error: Unsupported image format. Expected 1, 3, or 4 channels." << std::endl; return image; } return grayMat; } cv::Mat ANSOPENCV::ImageDenoise(const cv::Mat& image) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; } cv::Mat denoised_image; // Bilateral filter: 10-50x faster than NLMeans const int diameter = 9; // Kernel size const double sigmaColor = 75; // Color space sigma const double sigmaSpace = 75; // Coordinate space sigma cv::bilateralFilter(image, denoised_image, diameter, sigmaColor, sigmaSpace); return denoised_image; } cv::Mat ANSOPENCV::ImageCrop(const cv::Mat& inputImage, const cv::Rect& resizeROI, int originalImageSize) { std::lock_guard lock(_mutex); // License validation if (!_licenseValid) { std::cerr << "Error: License is not valid in ImageCrop." << std::endl; return cv::Mat(); // Return empty Mat explicitly } // Early validation checks if (inputImage.empty()) { std::cerr << "Error: Input image is empty!" << std::endl; return cv::Mat(); } if (resizeROI.width <= 0 || resizeROI.height <= 0) { std::cerr << "Error: Invalid ROI size! Width=" << resizeROI.width << ", Height=" << resizeROI.height << std::endl; return cv::Mat(); } try { const int originalWidth = inputImage.cols; const int originalHeight = inputImage.rows; // Scale ROI if originalImageSize is provided and valid cv::Rect roi = resizeROI; if (originalImageSize > 0 && originalImageSize != originalWidth) { const double scale = static_cast(originalWidth) / originalImageSize; // Use std::round for more accurate scaling roi.x = static_cast(std::round(resizeROI.x * scale)); roi.y = static_cast(std::round(resizeROI.y * scale)); roi.width = static_cast(std::round(resizeROI.width * scale)); roi.height = static_cast(std::round(resizeROI.height * scale)); // Ensure dimensions are still positive after rounding if (roi.width <= 0 || roi.height <= 0) { std::cerr << "Error: Scaled ROI has invalid dimensions!" << std::endl; return cv::Mat(); } } // Pre-calculate image bounds const cv::Rect imageBounds(0, 0, originalWidth, originalHeight); // Check if ROI is within bounds (optimized check) if (roi.x < 0 || roi.y < 0 || roi.x + roi.width > originalWidth || roi.y + roi.height > originalHeight) { std::cerr << "Error: ROI exceeds image boundaries! " << "ROI=[" << roi.x << "," << roi.y << "," << roi.width << "," << roi.height << "], " << "Image=[" << originalWidth << "x" << originalHeight << "]" << std::endl; return cv::Mat(); } // Crop and return (shallow copy - fast) //return inputImage(roi); return inputImage(roi).clone(); } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSOPENCV::ImageCrop: " << e.what() << std::endl; return cv::Mat(); } catch (const std::exception& e) { std::cerr << "Exception in ANSOPENCV::ImageCrop: " << e.what() << std::endl; return cv::Mat(); } } cv::Mat ANSOPENCV::ImageRepair(const cv::Mat& image) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; } try { cv::Mat grayImage; if (image.channels() == 1) { grayImage = image; } else { cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); } // Use more aggressive Canny thresholds to detect fewer edges cv::Mat edges; cv::Canny(grayImage, edges, 80, 200, 3, false); // Higher thresholds = fewer edges cv::Mat mask; cv::bitwise_not(edges, mask); cv::Mat inpaintedImage; const int inpaintRadius = 2; // Smaller radius = faster cv::inpaint(image, mask, inpaintedImage, inpaintRadius, cv::INPAINT_NS); // NS is faster return inpaintedImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ImageRepairFast: " << e.what() << std::endl; return image; } } std::string ANSOPENCV::PatternMatches(cv::Mat& image, cv::Mat& templateImage, double threshold) { std::lock_guard lock(_mutex); std::vector detectedObjects; // Early validation if (!_licenseValid || image.empty() || templateImage.empty()) { return VectorDetectionToJsonString(detectedObjects); } // Validate template size if (templateImage.cols > image.cols || templateImage.rows > image.rows) { std::cerr << "Error: Template is larger than image in PatternMatches!" << std::endl; return VectorDetectionToJsonString(detectedObjects); } try { // Calculate result dimensions const int result_cols = image.cols - templateImage.cols + 1; const int result_rows = image.rows - templateImage.rows + 1; // Perform template matching cv::Mat result; cv::matchTemplate(image, templateImage, result, cv::TM_CCOEFF_NORMED); // TM_CCOEFF_NORMED already returns normalized values in [-1, 1] // So we don't need to normalize again (major performance gain) // Reserve space to avoid repeated allocations detectedObjects.reserve(100); // Reasonable default // Use OpenCV's threshold function instead of manual iteration cv::Mat thresholdedResult; cv::threshold(result, thresholdedResult, threshold, 1.0, cv::THRESH_BINARY); // Find non-zero locations (matches above threshold) std::vector locations; cv::findNonZero(thresholdedResult, locations); // Convert to DetectionObjects const int templateWidth = templateImage.cols; const int templateHeight = templateImage.rows; for (const auto& loc : locations) { DetectionObject detectionObject; detectionObject.classId = 0; detectionObject.className = "Matched Object"; detectionObject.confidence = result.at(loc.y, loc.x); // Actual confidence detectionObject.box = cv::Rect(loc.x, loc.y, templateWidth, templateHeight); detectedObjects.push_back(detectionObject); } // Apply Non-Maximum Suppression if we have matches if (!detectedObjects.empty()) { NonMaximumSuppression(detectedObjects, 0.5); } return VectorDetectionToJsonString(detectedObjects); } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in PatternMatches: " << e.what() << std::endl; return VectorDetectionToJsonString(detectedObjects); } } std::string ANSOPENCV::QRDecoder(const cv::Mat& image) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return ""; } try { // Convert to grayscale efficiently cv::Mat grayImage; if (image.channels() == 1) { grayImage = image; // Already grayscale, no conversion needed } else if (image.channels() == 3) { cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); } else if (image.channels() == 4) { cv::cvtColor(image, grayImage, cv::COLOR_BGRA2GRAY); } else { std::cerr << "Error: Unsupported image format in QRDecoder" << std::endl; return ""; } const int width = grayImage.cols; const int height = grayImage.rows; // Create ZXing image view const ZXing::ImageView barcodeImage(grayImage.data, width, height, ZXing::ImageFormat::Lum); // Configure options (keep Any format for flexibility) const auto options = ZXing::ReaderOptions() .setFormats(ZXing::BarcodeFormat::Any) .setTryHarder(false) // Set false for faster decoding .setTryRotate(false); // Set false if rotation not needed // Decode barcodes const auto barcodes = ZXing::ReadBarcodes(barcodeImage, options); // Early exit if no barcodes found if (barcodes.empty()) { return ""; } // Reserve space for detected objects std::vector detectedObjects; detectedObjects.reserve(barcodes.size()); // Process each barcode for (const auto& b : barcodes) { DetectionObject detectedObject; detectedObject.classId = static_cast(b.format()); detectedObject.className = b.text(); detectedObject.confidence = 1.0; detectedObject.extraInfo = ZXing::ToString(b.format()); // Calculate bounding box from position const ZXing::Position& pos = b.position(); if (pos.size() == 4) { // Use std::minmax_element for cleaner code auto [minX, maxX] = std::minmax_element( pos.begin(), pos.end(), [](const auto& a, const auto& b) { return a.x < b.x; } ); auto [minY, maxY] = std::minmax_element( pos.begin(), pos.end(), [](const auto& a, const auto& b) { return a.y < b.y; } ); const int xmin = minX->x; const int ymin = minY->y; const int bwidth = maxX->x - xmin; const int bheight = maxY->y - ymin; // Validate bounding box if (bwidth > 0 && bheight > 0) { detectedObject.box = cv::Rect(xmin, ymin, bwidth, bheight); } else { std::cerr << "Warning: Invalid bounding box calculated for barcode" << std::endl; } } detectedObjects.push_back(std::move(detectedObject)); } // Apply NMS only if multiple detections if (detectedObjects.size() > 1) { NonMaximumSuppression(detectedObjects, 0.5); } return VectorDetectionToJsonString(detectedObjects); } catch (const std::exception& e) { std::cerr << "Exception in QRDecoder: " << e.what() << std::endl; return ""; } } std::string ANSOPENCV::QRDecoderWithBBox(const cv::Mat& image, int maxImageSize, const std::vector& bBox) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return ""; } // Early fallback if no bounding boxes if (bBox.empty()) { return QRDecoder(image); } try { const int originalWidth = image.cols; const int originalHeight = image.rows; const double scaleFactor = (maxImageSize > 0) ? static_cast(originalWidth) / maxImageSize : 1.0; // Pre-calculate inverse scale factor (avoid repeated division) const double invScaleFactor = 1.0 / scaleFactor; // Pre-calculate image bounds const cv::Rect imageBounds(0, 0, originalWidth, originalHeight); // Convert to grayscale once for all crops (major optimization) cv::Mat grayImage; if (image.channels() == 1) { grayImage = image; } else if (image.channels() == 3) { cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); } else if (image.channels() == 4) { cv::cvtColor(image, grayImage, cv::COLOR_BGRA2GRAY); } else { std::cerr << "Error: Unsupported image format in QRDecoderWithBBox" << std::endl; return ""; } // Reserve space for detected objects std::vector detectedObjects; detectedObjects.reserve(bBox.size() * 2); // Assume ~2 codes per box on average // Configure ZXing options once const auto options = ZXing::ReaderOptions() .setFormats(ZXing::BarcodeFormat::Any) .setTryHarder(false) .setTryRotate(false); // Process each bounding box for (const auto& rect : bBox) { // Scale the bounding box with rounding for better accuracy cv::Rect scaledRect; scaledRect.x = static_cast(std::round(rect.x * scaleFactor)); scaledRect.y = static_cast(std::round(rect.y * scaleFactor)); scaledRect.width = static_cast(std::round(rect.width * scaleFactor)); scaledRect.height = static_cast(std::round(rect.height * scaleFactor)); // Clamp to image bounds scaledRect &= imageBounds; // Skip invalid regions if (scaledRect.width <= 0 || scaledRect.height <= 0) { continue; } // Crop from grayscale image (no need to convert again) const cv::Mat croppedGray = grayImage(scaledRect); // Create ZXing image view const ZXing::ImageView barcodeImage(croppedGray.data, croppedGray.cols, croppedGray.rows, ZXing::ImageFormat::Lum); // Decode barcodes in this region const auto barcodes = ZXing::ReadBarcodes(barcodeImage, options); // Process each detected barcode for (const auto& b : barcodes) { DetectionObject detectedObject; detectedObject.classId = static_cast(b.format()); detectedObject.className = b.text(); detectedObject.confidence = 1.0; detectedObject.extraInfo = ZXing::ToString(b.format()); const ZXing::Position& pos = b.position(); if (pos.size() == 4) { // Calculate bounding box using minmax_element auto [minX, maxX] = std::minmax_element( pos.begin(), pos.end(), [](const auto& a, const auto& b) { return a.x < b.x; } ); auto [minY, maxY] = std::minmax_element( pos.begin(), pos.end(), [](const auto& a, const auto& b) { return a.y < b.y; } ); const int xmin = minX->x; const int ymin = minY->y; const int bwidth = maxX->x - xmin; const int bheight = maxY->y - ymin; // Validate dimensions if (bwidth > 0 && bheight > 0) { // Transform to global coordinates with proper rounding detectedObject.box = cv::Rect( static_cast(std::round((xmin + scaledRect.x) * invScaleFactor)), static_cast(std::round((ymin + scaledRect.y) * invScaleFactor)), static_cast(std::round(bwidth * invScaleFactor)), static_cast(std::round(bheight * invScaleFactor)) ); detectedObjects.push_back(std::move(detectedObject)); } } } } // Apply NMS only if multiple detections if (detectedObjects.size() > 1) { NonMaximumSuppression(detectedObjects, 0.5); } return VectorDetectionToJsonString(detectedObjects); } catch (const std::exception& e) { std::cerr << "Exception in QRDecoderWithBBox: " << e.what() << std::endl; return ""; } } std::string ANSOPENCV::MatToBase64(const cv::Mat& image) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return ""; } try { // Use CompressJpegToString with quality parameter // Note: Despite the function name "MatToBase64", this returns JPEG binary // If you actually need Base64, see the alternatives below std::string jpegString = CompressJpegToString(image, 100); if (jpegString.empty()) { std::cerr << "Error: JPEG compression failed in MatToBase64!" << std::endl; return ""; } return jpegString; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in MatToBase64: " << e.what() << std::endl; return ""; } catch (const std::exception& e) { std::cerr << "Exception in MatToBase64: " << e.what() << std::endl; return ""; } } cv::Mat ANSOPENCV::ImageDarkEnhancement(const cv::Mat& img, double brightnessScaleFactor) { std::lock_guard lock(_mutex); if (!_licenseValid || img.empty()) { return img; // Shallow copy (fast) } // Validate and set default scale factor if (brightnessScaleFactor <= 1.0) { brightnessScaleFactor = 1.5; } try { cv::Mat enhancedImage; // Direct scaling for single-channel images (faster) if (img.channels() == 1) { img.convertTo(enhancedImage, -1, brightnessScaleFactor, 0); return enhancedImage; } // For multi-channel images, use convertTo (much faster than split/merge) img.convertTo(enhancedImage, -1, brightnessScaleFactor, 0); return enhancedImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ImageDarkEnhancement: " << e.what() << std::endl; return img; } } cv::Mat ANSOPENCV::ImageContrastEnhancement(const cv::Mat& src) { std::lock_guard lock(_mutex); double clipLimit = 2.0; if (!_licenseValid || src.empty()) { return src; } try { cv::Mat dst; cv::Ptr clahe = cv::createCLAHE(clipLimit, cv::Size(8, 8)); if (src.channels() == 1) { clahe->apply(src, dst); } else if (src.channels() == 3) { cv::Mat yuv; cv::cvtColor(src, yuv, cv::COLOR_BGR2YUV); std::vector channels; cv::split(yuv, channels); clahe->apply(channels[0], channels[0]); cv::merge(channels, yuv); cv::cvtColor(yuv, dst, cv::COLOR_YUV2BGR); } else if (src.channels() == 4) { cv::Mat bgr; cv::cvtColor(src, bgr, cv::COLOR_BGRA2BGR); cv::Mat yuv; cv::cvtColor(bgr, yuv, cv::COLOR_BGR2YUV); std::vector channels; cv::split(yuv, channels); clahe->apply(channels[0], channels[0]); cv::merge(channels, yuv); cv::cvtColor(yuv, dst, cv::COLOR_YUV2BGR); } else { return src; } return dst; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ImageContrastEnhancementCLAHE: " << e.what() << std::endl; return src; } } cv::Mat ANSOPENCV::ImageWhiteBalance(const cv::Mat& src) { std::lock_guard lock(_mutex); if (!_licenseValid || src.empty()) { return src; // Shallow copy (fast) } // Only works with 3-channel (BGR) images if (src.channels() != 3) { std::cerr << "Warning: ImageWhiteBalance only works with 3-channel images" << std::endl; return src; } try { // Calculate mean for each channel directly (no split needed) cv::Scalar meanValues = cv::mean(src); double avgB = meanValues[0]; double avgG = meanValues[1]; double avgR = meanValues[2]; // Calculate total average double avgTotal = (avgB + avgG + avgR) / 3.0; // Validate averages to prevent division by zero if (avgB < 1.0 || avgG < 1.0 || avgR < 1.0) { std::cerr << "Warning: Very dark image in ImageWhiteBalance, skipping" << std::endl; return src; } // Calculate scale factors double scaleB = avgTotal / avgB; double scaleG = avgTotal / avgG; double scaleR = avgTotal / avgR; // Apply scaling using convertTo with per-channel scales cv::Mat dst; std::vector channels(3); cv::split(src, channels); // Scale each channel in-place (8-bit, no float conversion needed) channels[0].convertTo(channels[0], CV_8U, scaleB); channels[1].convertTo(channels[1], CV_8U, scaleG); channels[2].convertTo(channels[2], CV_8U, scaleR); cv::merge(channels, dst); return dst; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ImageWhiteBalance: " << e.what() << std::endl; return src; } } std::vector ANSOPENCV::GetBoundingBoxes(std::string strBBoxes) { std::lock_guard lock(_mutex); std::vector bBoxes; if (!_licenseValid) return bBoxes; bBoxes.clear(); try { // Parse JSON string using nlohmann::json nlohmann::json j = nlohmann::json::parse(strBBoxes); // Check if "results" exists and is an array if (j.contains("results") && j["results"].is_array()) { // Iterate through the results array for (const auto& result : j["results"]) { // Extract values with type checking if (result.contains("x") && result.contains("y") && result.contains("width") && result.contains("height")) { const auto x = result["x"].get(); const auto y = result["y"].get(); const auto width = result["width"].get(); const auto height = result["height"].get(); cv::Rect rectTemp; rectTemp.x = static_cast(x); rectTemp.y = static_cast(y); rectTemp.width = static_cast(width); rectTemp.height = static_cast(height); bBoxes.push_back(rectTemp); } } } } catch (const nlohmann::json::exception& e) { // Handle JSON parsing errors // You might want to log this error or handle it according to your needs // For now, we'll just return an empty vector bBoxes.clear(); } return bBoxes; } cv::Mat ANSOPENCV::RotateImage(const cv::Mat& image, double angle) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; // Shallow copy (fast) } try { // Normalize angle to [-180, 180] range angle = std::fmod(angle, 360.0); if (angle > 180.0) angle -= 360.0; if (angle < -180.0) angle += 360.0; // Fast path for common angles (exact 90-degree multiples) if (std::abs(angle) < 0.01) { return image; // No rotation needed } // Use faster built-in functions for 90-degree rotations if (std::abs(angle - 90.0) < 0.01) { cv::Mat rotated; cv::rotate(image, rotated, cv::ROTATE_90_CLOCKWISE); return rotated; } if (std::abs(angle + 90.0) < 0.01 || std::abs(angle - 270.0) < 0.01) { cv::Mat rotated; cv::rotate(image, rotated, cv::ROTATE_90_COUNTERCLOCKWISE); return rotated; } if (std::abs(std::abs(angle) - 180.0) < 0.01) { cv::Mat rotated; cv::rotate(image, rotated, cv::ROTATE_180); return rotated; } // General rotation for arbitrary angles const cv::Point2f center(image.cols / 2.0f, image.rows / 2.0f); // Get rotation matrix cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, angle, 1.0); // Calculate bounding box for rotated image const cv::Rect2f bbox = cv::RotatedRect(cv::Point2f(), image.size(), angle).boundingRect2f(); // Adjust transformation matrix to center the rotated image rotationMatrix.at(0, 2) += bbox.width / 2.0 - center.x; rotationMatrix.at(1, 2) += bbox.height / 2.0 - center.y; // Perform rotation with optimized interpolation cv::Mat rotatedImage; cv::warpAffine(image, rotatedImage, rotationMatrix, bbox.size(), cv::INTER_LINEAR, // Faster than INTER_CUBIC, still good quality cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); return rotatedImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in RotateImage: " << e.what() << std::endl; return image; } } cv::Mat ANSOPENCV::FlipImage(const cv::Mat& image, int flipCode) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; // Shallow copy (fast) } // Validate flipCode // flipCode > 0: flip horizontally (mirror left-right) // flipCode = 0: flip vertically (mirror top-bottom) // flipCode < 0: flip both horizontally and vertically (180° rotation) if (flipCode < -1 || flipCode > 1) { std::cerr << "Warning: flipCode should be -1, 0, or 1. Using modulo." << std::endl; // Normalize to valid range if (flipCode > 1) flipCode = 1; if (flipCode < -1) flipCode = -1; } try { cv::Mat flippedImage; cv::flip(image, flippedImage, flipCode); return flippedImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in FlipImage: " << e.what() << std::endl; return image; } } cv::Mat ANSOPENCV::ShiftImage(const cv::Mat& image, int shiftX, int shiftY) { std::lock_guard lock(_mutex); if (!_licenseValid) return image; if (image.empty()) return image; // Calculate new dimensions to accommodate the shift int newWidth = image.cols + std::abs(shiftX); int newHeight = image.rows + std::abs(shiftY); // Adjust translation to account for negative shifts int translateX = (shiftX < 0) ? std::abs(shiftX) : 0; int translateY = (shiftY < 0) ? std::abs(shiftY) : 0; // Create translation matrix with adjusted offsets cv::Mat translationMatrix = (cv::Mat_(2, 3) << 1, 0, translateX + shiftX, 0, 1, translateY + shiftY); // Create output with expanded size cv::Mat shiftedImage; cv::Size newSize(newWidth, newHeight); cv::warpAffine(image, shiftedImage, translationMatrix, newSize, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); return shiftedImage; } cv::Mat ANSOPENCV::AddGaussianNoise(const cv::Mat& image, double mean, double stddev) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; } if (stddev < 0) { stddev = 0; } if (stddev == 0 && mean == 0) { return image; } try { // Convert to float for better precision cv::Mat floatImage; image.convertTo(floatImage, CV_32F); // Generate noise cv::Mat noise(image.size(), CV_32FC(image.channels())); cv::randn(noise, mean, stddev); // Add noise floatImage += noise; // Convert back with saturation cv::Mat noisyImage; floatImage.convertTo(noisyImage, image.type()); return noisyImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in AddGaussianNoiseFast: " << e.what() << std::endl; return image; } } cv::Mat ANSOPENCV::AddSaltAndPepperNoise(const cv::Mat& image, double amount) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; } if (amount <= 0 || amount >= 1) { if (amount <= 0) return image; amount = 0.99; // Clamp to valid range } try { cv::Mat noisyImage = image.clone(); // Generate random mask for entire image (vectorized!) cv::Mat randMat(image.size(), CV_32F); cv::randu(randMat, 0, 1.0); // Salt (white pixels) - top amount/2 percentile double saltThreshold = 1.0 - (amount / 2.0); cv::Mat saltMask = randMat > saltThreshold; // Pepper (black pixels) - bottom amount/2 percentile double pepperThreshold = amount / 2.0; cv::Mat pepperMask = randMat < pepperThreshold; // Apply noise using masks (MUCH faster than loops!) noisyImage.setTo(cv::Scalar::all(255), saltMask); noisyImage.setTo(cv::Scalar::all(0), pepperMask); return noisyImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in AddSaltAndPepperNoiseFast: " << e.what() << std::endl; return image; } } cv::Mat ANSOPENCV::AddSpeckleNoise(const cv::Mat& image, double stddev) { std::lock_guard lock(_mutex); if (!_licenseValid || image.empty()) { return image; // Shallow copy (fast) } // Validate stddev parameter if (stddev < 0) { std::cerr << "Error: stddev must be non-negative in AddSpeckleNoise" << std::endl; return image; } // Early exit if no noise if (stddev == 0) { return image; } try { // Convert to float for proper multiplicative noise calculation cv::Mat floatImage; image.convertTo(floatImage, CV_32F); // Generate Gaussian noise with mean 0 and given stddev cv::Mat noise(image.size(), CV_32FC(image.channels())); cv::randn(noise, 0, stddev); // Add 1.0 to noise so it becomes a multiplicative factor around 1.0 // noise in range [1-3σ, 1+3σ] for ~99.7% of values noise += 1.0; // Apply multiplicative noise: output = input * (1 + noise) cv::Mat noisyFloat; cv::multiply(floatImage, noise, noisyFloat); // Convert back to original type with saturation cv::Mat noisyImage; noisyFloat.convertTo(noisyImage, image.type()); return noisyImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in AddSpeckleNoise: " << e.what() << std::endl; return image; } } void ANSOPENCV::InitCameraNetwork() { network_init(); sys_buf_init(200); rtsp_parse_buf_init(200); http_msg_buf_init(200); } void ANSOPENCV::DeinitCameraNetwork() { http_msg_buf_deinit(); rtsp_parse_buf_deinit(); sys_buf_deinit(); network_deinit(); } cv::Mat ANSOPENCV::resizeImageToFit(const cv::Mat& inputImage, int maxWidth, int maxHeight, int& newWidth, int& newHeight) { if (inputImage.empty()) { std::cerr << "Error: Input image is empty or license invalid!" << std::endl; newWidth = 0; newHeight = 0; return cv::Mat(); } // Validate max dimensions if (maxWidth <= 0 || maxHeight <= 0) { std::cerr << "Error: Invalid max dimensions in resizeImageToFit" << std::endl; newWidth = inputImage.cols; newHeight = inputImage.rows; return inputImage; } const int width = inputImage.cols; const int height = inputImage.rows; // If image already fits, return as-is (no clone needed - shallow copy is fine) if (width <= maxWidth && height <= maxHeight) { newWidth = width; newHeight = height; return inputImage; // Shallow copy (fast, safe for read-only) } try { // Compute optimal scale factor maintaining aspect ratio const double scale = min(static_cast(maxWidth) / width, static_cast(maxHeight) / height); // Use rounding for more accurate dimensions newWidth = static_cast(std::round(width * scale)); newHeight = static_cast(std::round(height * scale)); // Ensure dimensions are at least 1x1 newWidth = max(1, newWidth); newHeight =max(1, newHeight); // Choose interpolation method based on scaling direction const int interpolation = (scale < 1.0) ? cv::INTER_AREA : cv::INTER_LINEAR; // Resize (no need to pre-allocate - cv::resize handles it efficiently) cv::Mat resizedImage; cv::resize(inputImage, resizedImage, cv::Size(newWidth, newHeight), 0, 0, interpolation); return resizedImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in resizeImageToFit: " << e.what() << std::endl; newWidth = width; newHeight = height; return inputImage; } } std::string ANSOPENCV::VectorDetectionToJsonString(const std::vector& dets) { if (dets.empty()) { return R"({"results":[]})"; } try { nlohmann::json root; auto& results = root["results"] = nlohmann::json::array(); for (const auto& det : dets) { results.push_back({ {"class_id", std::to_string(det.classId)}, {"track_id", std::to_string(det.trackId)}, {"class_name", det.className}, {"prob", std::to_string(det.confidence)}, {"x", std::to_string(det.box.x)}, {"y", std::to_string(det.box.y)}, {"width", std::to_string(det.box.width)}, {"height", std::to_string(det.box.height)}, {"mask", ""}, // TODO: convert masks to comma separated string {"extra_info",det.extraInfo} }); } return root.dump(); } catch (const std::exception& e) { // Add your error logging here if needed return R"({"results":[],"error":"Serialization failed"})"; } } double ANSOPENCV::CalculateIoU(const cv::Rect& box1, const cv::Rect& box2) { std::lock_guard lock(_mutex); int x1 = max(box1.x, box2.x); int y1 = max(box1.y, box2.y); int x2 = min(box1.x + box1.width, box2.x + box2.width); int y2 = min(box1.y + box1.height, box2.y + box2.height); int intersectionArea = max(0, x2 - x1) * max(0, y2 - y1); int box1Area = box1.width * box1.height; int box2Area = box2.width * box2.height; double iou = static_cast(intersectionArea) / (box1Area + box2Area - intersectionArea); return iou; } void ANSOPENCV::NonMaximumSuppression(std::vector& detectedObjects, double iouThreshold) { std::lock_guard lock(_mutex); std::sort(detectedObjects.begin(), detectedObjects.end(), [](const DetectionObject& a, const DetectionObject& b) { return a.confidence > b.confidence; }); std::vector finalDetections; while (!detectedObjects.empty()) { finalDetections.push_back(detectedObjects.front()); detectedObjects.erase(detectedObjects.begin()); for (auto it = detectedObjects.begin(); it != detectedObjects.end(); ) { if (CalculateIoU(finalDetections.back().box, it->box) > iouThreshold) { it = detectedObjects.erase(it); } else { ++it; } } } detectedObjects = std::move(finalDetections); } cv::Mat ANSOPENCV::ImageResizeV2(const cv::Mat& inputImage, int resizeWidth, int originalImageSize) { std::lock_guard lock(_mutex); if (!_licenseValid) { std::cerr << "Error: License is not valid in ImageResizeV2." << std::endl; return cv::Mat(); } if (inputImage.empty()) { std::cerr << "Error: Input image is empty!" << std::endl; return cv::Mat(); } if (resizeWidth <= 0) { std::cerr << "Error: Invalid width provided for resizing!" << std::endl; return cv::Mat(); } try { const int originalWidth = inputImage.cols; const int originalHeight = inputImage.rows; // Calculate target width with scaling int targetWidth = resizeWidth; if (originalImageSize > 0 && originalImageSize != originalWidth) { const double scale = static_cast(originalWidth) / originalImageSize; targetWidth = static_cast(std::round(resizeWidth * scale)); } // Skip resize if target is same or larger if (targetWidth >= originalWidth) { return inputImage; // Shallow copy (fast, safe for read-only) } // Calculate target height maintaining aspect ratio const int targetHeight = static_cast(std::round( targetWidth * static_cast(originalHeight) / originalWidth )); // Validate computed dimensions if (targetWidth <= 0 || targetHeight <= 0) { std::cerr << "Error: Computed dimensions invalid (" << targetWidth << "x" << targetHeight << ")" << std::endl; return cv::Mat(); } // Choose interpolation method (INTER_AREA best for downscaling) cv::Mat resizedImage; cv::resize(inputImage, resizedImage, cv::Size(targetWidth, targetHeight), 0, 0, cv::INTER_AREA); return resizedImage; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ImageResizeV2: " << e.what() << std::endl; return cv::Mat(); } catch (const std::exception& e) { std::cerr << "Exception in ImageResizeV2: " << e.what() << std::endl; return cv::Mat(); } } bool ANSOPENCV::resizeImage(cv::Mat& inputImage, int resizeWidth, int originalImageSize) { if (inputImage.empty()) { std::cerr << "Error: Input image is empty!" << std::endl; return false; } if (resizeWidth <= 0) { std::cerr << "Error: Invalid width provided for resizing!" << std::endl; return false; } try { const int originalWidth = inputImage.cols; const int originalHeight = inputImage.rows; // Calculate target width with scaling int targetWidth = resizeWidth; if (originalImageSize > 0 && originalImageSize != originalWidth) { const double scale = static_cast(originalWidth) / originalImageSize; targetWidth = static_cast(std::round(resizeWidth * scale)); } // Skip resize if target is same or larger if (targetWidth >= originalWidth) { return true; // No resize needed, success } // Calculate target height maintaining aspect ratio const int targetHeight = static_cast(std::round( targetWidth * static_cast(originalHeight) / originalWidth )); // Validate computed dimensions if (targetWidth <= 0 || targetHeight <= 0) { std::cerr << "Error: Computed dimensions invalid (" << targetWidth << "x" << targetHeight << ")" << std::endl; return false; } // CRITICAL: Use INTER_AREA for downscaling (much faster and better quality) cv::resize(inputImage, inputImage, cv::Size(targetWidth, targetHeight), 0, 0, cv::INTER_AREA); return true; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in resizeImage: " << e.what() << std::endl; return false; } catch (const std::exception& e) { std::cerr << "Exception in resizeImage: " << e.what() << std::endl; return false; } } bool ANSOPENCV::cropImage(cv::Mat& inputImage, const cv::Rect& resizeROI, int originalImageSize) { if (inputImage.empty()) { std::cerr << "Error: Input image is empty!" << std::endl; return false; } if (resizeROI.width <= 0 || resizeROI.height <= 0) { std::cerr << "Error: Invalid ROI size! Width and height must be greater than zero." << std::endl; return false; } try { const int originalWidth = inputImage.cols; const int originalHeight = inputImage.rows; // Scale ROI if needed cv::Rect roi = resizeROI; if (originalImageSize > 0 && originalImageSize != originalWidth) { const double scale = static_cast(originalWidth) / originalImageSize; // Use rounding for more accurate scaling roi.x = static_cast(std::round(resizeROI.x * scale)); roi.y = static_cast(std::round(resizeROI.y * scale)); roi.width = static_cast(std::round(resizeROI.width * scale)); roi.height = static_cast(std::round(resizeROI.height * scale)); } // Validate and clamp ROI to image bounds roi.x = max(0, roi.x); roi.y = max(0, roi.y); roi.width = min(roi.width, originalWidth - roi.x); roi.height = min(roi.height, originalHeight - roi.y); // Final validation if (roi.width <= 0 || roi.height <= 0) { std::cerr << "Error: ROI exceeds image boundaries! ROI(" << roi.x << "," << roi.y << "," << roi.width << "," << roi.height << ") vs Image(" << originalWidth << "x" << originalHeight << ")" << std::endl; return false; } // CRITICAL BUG FIX: Clone to avoid dangling reference // Without clone(), inputImage references old memory that may be freed inputImage = inputImage(roi).clone(); return true; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in cropImage: " << e.what() << std::endl; return false; } catch (const std::exception& e) { std::cerr << "Exception in cropImage: " << e.what() << std::endl; return false; } } bool ANSOPENCV::ImagesToMP4(const std::string& imageFolder, const std::string& outputVideoPath, int targetDurationSec) { // Use per-output-file mutex instead of global mutex static std::mutex mapMutex; static std::map> fileMutexes; std::shared_ptr fileMutex; { std::lock_guard lock(mapMutex); std::string canonicalPath = std::filesystem::canonical( std::filesystem::path(outputVideoPath).parent_path()).string() + "/" + std::filesystem::path(outputVideoPath).filename().string(); if (fileMutexes.find(canonicalPath) == fileMutexes.end()) { fileMutexes[canonicalPath] = std::make_unique(); } fileMutex = std::shared_ptr( fileMutexes[canonicalPath].get(), [](std::timed_mutex*) {}); } std::unique_lock lock(*fileMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Another thread is writing to " << outputVideoPath << std::endl; return false; // Changed from -6 to false for consistency } cv::VideoWriter videoWriter; try { // Collect all image files efficiently std::vector imageFiles; const std::vector extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp" }; for (const auto& ext : extensions) { std::vector temp; cv::glob(imageFolder + "/" + ext, temp, false); imageFiles.insert(imageFiles.end(), std::make_move_iterator(temp.begin()), std::make_move_iterator(temp.end())); } if (imageFiles.empty()) { std::cerr << "Error: No images found in folder: " << imageFolder << std::endl; return false; } // Sort for consistent ordering std::sort(imageFiles.begin(), imageFiles.end()); // Read first image to determine dimensions cv::Mat firstImage = cv::imread(imageFiles[0], cv::IMREAD_COLOR); if (firstImage.empty()) { std::cerr << "Error: Could not read first image: " << imageFiles[0] << std::endl; return false; } // Target video dimensions (1920x1080) const int targetWidth = 1920; const int targetHeight = 1080; // Calculate scaling to fit within bounds while maintaining aspect ratio const double scaleX = static_cast(targetWidth) / firstImage.cols; const double scaleY = static_cast(targetHeight) / firstImage.rows; const double scale = min(scaleX, scaleY); // Calculate scaled dimensions (ensure even for H.264) int scaledWidth = static_cast(std::round(firstImage.cols * scale)); int scaledHeight = static_cast(std::round(firstImage.rows * scale)); // Make dimensions even (required for H.264) scaledWidth = (scaledWidth / 2) * 2; scaledHeight = (scaledHeight / 2) * 2; // Calculate centering padding const int padLeft = (targetWidth - scaledWidth) / 2; const int padTop = (targetHeight - scaledHeight) / 2; std::cout << "[Thread " << std::this_thread::get_id() << "] " << "Original: " << firstImage.cols << "x" << firstImage.rows << " -> Scaled: " << scaledWidth << "x" << scaledHeight << std::endl; // Release firstImage to free memory firstImage.release(); // Video parameters const int targetFPS = 25; const int videoDurationSec = max(3, targetDurationSec); const int totalFrames = videoDurationSec * targetFPS; // Ensure .mp4 extension std::string mp4OutputPath = outputVideoPath; if (mp4OutputPath.size() < 4 || mp4OutputPath.substr(mp4OutputPath.size() - 4) != ".mp4") { mp4OutputPath += ".mp4"; } // Try codecs in order of preference const std::vector> codecs = { {"x264", cv::VideoWriter::fourcc('x', '2', '6', '4')}, {"H264", cv::VideoWriter::fourcc('H', '2', '6', '4')}, {"MP4V", cv::VideoWriter::fourcc('M', 'P', '4', 'V')}, {"MJPG", cv::VideoWriter::fourcc('M', 'J', 'P', 'G')} }; bool codecFound = false; for (const auto& [name, fourcc] : codecs) { videoWriter.open(mp4OutputPath, fourcc, targetFPS, cv::Size(targetWidth, targetHeight), true); if (videoWriter.isOpened()) { std::cout << "Using codec: " << name << std::endl; codecFound = true; break; } videoWriter.release(); // Release failed attempt } if (!codecFound) { std::cerr << "Error: Could not open video writer with any codec!" << std::endl; return false; } const int numImages = static_cast(imageFiles.size()); // Pre-create black canvas and ROI (reuse for all frames) cv::Mat canvas(targetHeight, targetWidth, CV_8UC3, cv::Scalar(0, 0, 0)); const cv::Rect roi(padLeft, padTop, scaledWidth, scaledHeight); // Pre-allocate matrices for reuse cv::Mat img; cv::Mat resizedImg; if (numImages <= totalFrames) { // Case 1: Few images - each shown for multiple frames const int framesPerImage = totalFrames / numImages; const int remainingFrames = totalFrames % numImages; for (int i = 0; i < numImages; ++i) { img = cv::imread(imageFiles[i], cv::IMREAD_COLOR); if (img.empty()) { std::cerr << "Warning: Could not read: " << imageFiles[i] << std::endl; continue; } // Reset canvas to black canvas.setTo(cv::Scalar(0, 0, 0)); // Resize and place on canvas cv::resize(img, resizedImg, cv::Size(scaledWidth, scaledHeight), 0, 0, cv::INTER_AREA); resizedImg.copyTo(canvas(roi)); img.release(); int framesToWrite = framesPerImage + (i < remainingFrames ? 1 : 0); for (int j = 0; j < framesToWrite; ++j) { videoWriter.write(canvas); } } } else { // Case 2: Many images - sample to fit total frames for (int frame = 0; frame < totalFrames; ++frame) { const double imageIndex = (static_cast(frame) * (numImages - 1)) / (totalFrames - 1); const int imageIdx = static_cast(std::round(imageIndex)); img = cv::imread(imageFiles[imageIdx], cv::IMREAD_COLOR); if (img.empty()) { std::cerr << "Warning: Could not read: " << imageFiles[imageIdx] << std::endl; continue; } canvas.setTo(cv::Scalar(0, 0, 0)); cv::resize(img, resizedImg, cv::Size(scaledWidth, scaledHeight), 0, 0, cv::INTER_AREA); resizedImg.copyTo(canvas(roi)); img.release(); videoWriter.write(canvas); } } // Explicit cleanup canvas.release(); resizedImg.release(); videoWriter.release(); std::cout << "✓ Video created: " << mp4OutputPath << " (" << totalFrames << " frames, " << numImages << " images)" << std::endl; return true; } catch (const cv::Exception& e) { videoWriter.release(); std::cerr << "OpenCV exception: " << e.what() << std::endl; return false; } catch (const std::exception& e) { videoWriter.release(); std::cerr << "Exception: " << e.what() << std::endl; return false; } } //bool ANSOPENCV::ImagesToMP4(const std::string& imageFolder, const std::string& outputVideoPath, int targetDurationSec) { // std::unique_lock lock(timeImageMutex, std::defer_lock); // if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { // std::cerr << "Error: Mutex timeout in ImagesToMP4!" << std::endl; // return -6; // } // cv::VideoWriter videoWriter; // try { // // Collect all image files efficiently // std::vector imageFiles; // const std::vector extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp" }; // for (const auto& ext : extensions) { // std::vector temp; // cv::glob(imageFolder + "/" + ext, temp, false); // imageFiles.insert(imageFiles.end(), temp.begin(), temp.end()); // } // if (imageFiles.empty()) { // std::cerr << "Error: No images found in folder: " << imageFolder << std::endl; // return false; // } // // Sort for consistent ordering // std::sort(imageFiles.begin(), imageFiles.end()); // // Read first image to determine dimensions // cv::Mat firstImage = cv::imread(imageFiles[0]); // if (firstImage.empty()) { // std::cerr << "Error: Could not read first image: " << imageFiles[0] << std::endl; // return false; // } // // Target video dimensions (1920x1080) // const int targetWidth = 1920; // const int targetHeight = 1080; // // Calculate scaling to fit within bounds while maintaining aspect ratio // const double scaleX = static_cast(targetWidth) / firstImage.cols; // const double scaleY = static_cast(targetHeight) / firstImage.rows; // const double scale = min(scaleX, scaleY); // // Calculate scaled dimensions (ensure even for H.264) // int scaledWidth = static_cast(std::round(firstImage.cols * scale)); // int scaledHeight = static_cast(std::round(firstImage.rows * scale)); // // Make dimensions even (required for H.264) // scaledWidth = (scaledWidth / 2) * 2; // scaledHeight = (scaledHeight / 2) * 2; // // Calculate centering padding // const int padLeft = (targetWidth - scaledWidth) / 2; // const int padTop = (targetHeight - scaledHeight) / 2; // std::cout << "Original image size: " << firstImage.cols << "x" << firstImage.rows << std::endl; // std::cout << "Scaled image size: " << scaledWidth << "x" << scaledHeight << std::endl; // std::cout << "Final video size: " << targetWidth << "x" << targetHeight << std::endl; // std::cout << "Padding: left=" << padLeft << ", top=" << padTop << std::endl; // // Release firstImage to free memory // firstImage.release(); // // Video parameters // const int targetFPS = 25; // const int videoDurationSec = max(3, targetDurationSec); // const int totalFrames = videoDurationSec * targetFPS; // // Ensure .mp4 extension // std::string mp4OutputPath = outputVideoPath; // if (mp4OutputPath.size() < 4 || // mp4OutputPath.substr(mp4OutputPath.size() - 4) != ".mp4") { // mp4OutputPath += ".mp4"; // } // // Try codecs in order of preference // const std::vector> codecs = { // {"x264 (best for web)", cv::VideoWriter::fourcc('x', '2', '6', '4')}, // {"H264 (good for web)", cv::VideoWriter::fourcc('H', '2', '6', '4')}, // {"MP4V (web compatible)", cv::VideoWriter::fourcc('M', 'P', '4', 'V')}, // {"MJPG (large files)", cv::VideoWriter::fourcc('M', 'J', 'P', 'G')} // }; // bool codecFound = false; // for (const auto& [name, fourcc] : codecs) { // videoWriter.open(mp4OutputPath, fourcc, targetFPS, // cv::Size(targetWidth, targetHeight), true); // if (videoWriter.isOpened()) { // std::cout << "Using codec: " << name << std::endl; // codecFound = true; // break; // } // } // if (!codecFound) { // videoWriter.release(); // Ensure cleanup // std::cerr << "Error: Could not open video writer with any codec!" << std::endl; // std::cerr << "Install OpenCV with FFMPEG support for H.264 encoding." << std::endl; // return false; // } // const int numImages = static_cast(imageFiles.size()); // // Pre-create black canvas and ROI (reuse for all frames) // cv::Mat canvas = cv::Mat::zeros(targetHeight, targetWidth, CV_8UC3); // const cv::Rect roi(padLeft, padTop, scaledWidth, scaledHeight); // // Pre-allocate matrices for reuse (reduces allocations) // cv::Mat img; // cv::Mat resizedImg; // if (numImages <= totalFrames) { // // Case 1: Few images - each shown for multiple frames // const int framesPerImage = totalFrames / numImages; // const int remainingFrames = totalFrames % numImages; // for (int i = 0; i < numImages; ++i) { // img = cv::imread(imageFiles[i]); // if (img.empty()) { // std::cerr << "Warning: Could not read image: " << imageFiles[i] << std::endl; // continue; // } // // Reset canvas to black // canvas.setTo(cv::Scalar(0, 0, 0)); // // Resize and place on canvas // cv::resize(img, resizedImg, cv::Size(scaledWidth, scaledHeight), // 0, 0, cv::INTER_AREA); // resizedImg.copyTo(canvas(roi)); // // Release img to free memory immediately // img.release(); // // Calculate frames for this image // int framesToWrite = framesPerImage + (i < remainingFrames ? 1 : 0); // // Write frames // for (int j = 0; j < framesToWrite; ++j) { // videoWriter.write(canvas); // } // } // } // else { // // Case 2: Many images - sample to fit total frames // for (int frame = 0; frame < totalFrames; ++frame) { // // Calculate which image to use // const double imageIndex = (static_cast(frame) * (numImages - 1)) / // (totalFrames - 1); // const int imageIdx = static_cast(std::round(imageIndex)); // img = cv::imread(imageFiles[imageIdx]); // if (img.empty()) { // std::cerr << "Warning: Could not read image: " << imageFiles[imageIdx] << std::endl; // continue; // } // // Reset canvas to black // canvas.setTo(cv::Scalar(0, 0, 0)); // // Resize and place on canvas // cv::resize(img, resizedImg, cv::Size(scaledWidth, scaledHeight), // 0, 0, cv::INTER_AREA); // resizedImg.copyTo(canvas(roi)); // // Release img to free memory immediately // img.release(); // videoWriter.write(canvas); // } // } // // Explicit cleanup // canvas.release(); // resizedImg.release(); // videoWriter.release(); // std::cout << "Video created successfully: " << mp4OutputPath << std::endl; // std::cout << "Dimensions: " << targetWidth << "x" << targetHeight << std::endl; // std::cout << "Frame rate: " << targetFPS << " fps" << std::endl; // std::cout << "Duration: " << videoDurationSec << " seconds" << std::endl; // std::cout << "Total frames: " << totalFrames << std::endl; // std::cout << "Images used: " << numImages << std::endl; // return true; // } // catch (const cv::Exception& e) { // if (videoWriter.isOpened()) { // videoWriter.release(); // } // std::cerr << "OpenCV exception in ImagesToMP4: " << e.what() << std::endl; // return false; // } // catch (const std::exception& e) { // if (videoWriter.isOpened()) { // videoWriter.release(); // } // std::cerr << "Exception in ImagesToMP4: " << e.what() << std::endl; // return false; // } //} // bool ANSOPENCV::ImagesToMP4(const std::string& imageFolder, const std::string& outputVideoPath, int targetDurationSec) { // try { // // Collect all image files efficiently // std::vector imageFiles; // const std::vector extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp" }; // // for (const auto& ext : extensions) { // std::vector temp; // cv::glob(imageFolder + "/" + ext, temp, false); // imageFiles.insert(imageFiles.end(), temp.begin(), temp.end()); // } // // if (imageFiles.empty()) { // std::cerr << "Error: No images found in folder: " << imageFolder << std::endl; // return false; // } // // // Sort for consistent ordering // std::sort(imageFiles.begin(), imageFiles.end()); // // // Read first image to determine dimensions // cv::Mat firstImage = cv::imread(imageFiles[0]); // if (firstImage.empty()) { // std::cerr << "Error: Could not read first image: " << imageFiles[0] << std::endl; // return false; // } // // // Target video dimensions (1920x1080) // const int targetWidth = 1920; // const int targetHeight = 1080; // // // Calculate scaling to fit within bounds while maintaining aspect ratio // const double scaleX = static_cast(targetWidth) / firstImage.cols; // const double scaleY = static_cast(targetHeight) / firstImage.rows; // const double scale = min(scaleX, scaleY); // // // Calculate scaled dimensions (ensure even for H.264) // int scaledWidth = static_cast(std::round(firstImage.cols * scale)); // int scaledHeight = static_cast(std::round(firstImage.rows * scale)); // // // Make dimensions even (required for H.264) // scaledWidth = (scaledWidth / 2) * 2; // scaledHeight = (scaledHeight / 2) * 2; // // // Calculate centering padding // const int padLeft = (targetWidth - scaledWidth) / 2; // const int padTop = (targetHeight - scaledHeight) / 2; // // std::cout << "Original image size: " << firstImage.cols << "x" << firstImage.rows << std::endl; // std::cout << "Scaled image size: " << scaledWidth << "x" << scaledHeight << std::endl; // std::cout << "Final video size: " << targetWidth << "x" << targetHeight << std::endl; // std::cout << "Padding: left=" << padLeft << ", top=" << padTop << std::endl; // // // Video parameters // const int targetFPS = 25; // const int videoDurationSec = max(3, targetDurationSec); // const int totalFrames = videoDurationSec * targetFPS; // // // Ensure .mp4 extension // std::string mp4OutputPath = outputVideoPath; // if (mp4OutputPath.size() < 4 || // mp4OutputPath.substr(mp4OutputPath.size() - 4) != ".mp4") { // mp4OutputPath += ".mp4"; // } // // // Try codecs in order of preference // cv::VideoWriter videoWriter; // const std::vector> codecs = { // {"x264 (best for web)", cv::VideoWriter::fourcc('x', '2', '6', '4')}, // {"H264 (good for web)", cv::VideoWriter::fourcc('H', '2', '6', '4')}, // {"MP4V (web compatible)", cv::VideoWriter::fourcc('M', 'P', '4', 'V')}, // {"MJPG (large files)", cv::VideoWriter::fourcc('M', 'J', 'P', 'G')} // }; // // bool codecFound = false; // for (const auto& [name, fourcc] : codecs) { // videoWriter.open(mp4OutputPath, fourcc, targetFPS, // cv::Size(targetWidth, targetHeight), true); // if (videoWriter.isOpened()) { // std::cout << "Using codec: " << name << std::endl; // codecFound = true; // break; // } // } // // if (!codecFound) { // std::cerr << "Error: Could not open video writer with any codec!" << std::endl; // std::cerr << "Install OpenCV with FFMPEG support for H.264 encoding." << std::endl; // return false; // } // // const int numImages = static_cast(imageFiles.size()); // // // Pre-create black canvas and ROI (reuse for all frames) // cv::Mat canvas = cv::Mat::zeros(targetHeight, targetWidth, CV_8UC3); // const cv::Rect roi(padLeft, padTop, scaledWidth, scaledHeight); // // if (numImages <= totalFrames) { // // Case 1: Few images - each shown for multiple frames // const int framesPerImage = totalFrames / numImages; // const int remainingFrames = totalFrames % numImages; // // for (int i = 0; i < numImages; ++i) { // cv::Mat img = cv::imread(imageFiles[i]); // if (img.empty()) { // std::cerr << "Warning: Could not read image: " << imageFiles[i] << std::endl; // continue; // } // // // Reset canvas to black // canvas.setTo(cv::Scalar(0, 0, 0)); // // // Resize and place on canvas // cv::Mat resizedImg; // cv::resize(img, resizedImg, cv::Size(scaledWidth, scaledHeight), // 0, 0, cv::INTER_AREA); // resizedImg.copyTo(canvas(roi)); // // // Calculate frames for this image // int framesToWrite = framesPerImage + (i < remainingFrames ? 1 : 0); // // // Write frames // for (int j = 0; j < framesToWrite; ++j) { // videoWriter.write(canvas); // } // } // } // else { // // Case 2: Many images - sample to fit total frames // for (int frame = 0; frame < totalFrames; ++frame) { // // Calculate which image to use // const double imageIndex = (static_cast(frame) * (numImages - 1)) / // (totalFrames - 1); // const int imageIdx = static_cast(std::round(imageIndex)); // // cv::Mat img = cv::imread(imageFiles[imageIdx]); // if (img.empty()) { // std::cerr << "Warning: Could not read image: " << imageFiles[imageIdx] << std::endl; // continue; // } // // // Reset canvas to black // canvas.setTo(cv::Scalar(0, 0, 0)); // // // Resize and place on canvas // cv::Mat resizedImg; // cv::resize(img, resizedImg, cv::Size(scaledWidth, scaledHeight), // 0, 0, cv::INTER_AREA); // resizedImg.copyTo(canvas(roi)); // // videoWriter.write(canvas); // } // } // // videoWriter.release(); // // std::cout << "Video created successfully: " << mp4OutputPath << std::endl; // std::cout << "Dimensions: " << targetWidth << "x" << targetHeight << std::endl; // std::cout << "Frame rate: " << targetFPS << " fps" << std::endl; // std::cout << "Duration: " << videoDurationSec << " seconds" << std::endl; // std::cout << "Total frames: " << totalFrames << std::endl; // std::cout << "Images used: " << numImages << std::endl; // // return true; // } // catch (const cv::Exception& e) { // std::cerr << "OpenCV exception in ImagesToMP4: " << e.what() << std::endl; // return false; // } // catch (const std::exception& e) { // std::cerr << "Exception in ImagesToMP4: " << e.what() << std::endl; // return false; // } //} } extern "C" __declspec(dllexport) int CreateANSCVHandle(ANSCENTER::ANSOPENCV** Handle, const char* licenseKey) { if (!Handle || !licenseKey) return -1; try { auto ptr = std::make_unique(); bool result = ptr->Init(licenseKey); if (result) { *Handle = ptr.release(); return 1; } *Handle = nullptr; return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ReleaseANSCVHandle(ANSCENTER::ANSOPENCV** Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { std::unique_ptr ptr(*Handle); *Handle = nullptr; return 0; } catch (...) { if (Handle) *Handle = nullptr; return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageResize(ANSCENTER::ANSOPENCV** Handle,unsigned char* inputImage,unsigned int bufferLength,int width,int height,LStrHandle outputImage) { // Validate inputs if (!Handle || !*Handle) { std::cerr << "Error: Invalid Handle in ANSCV_ImageResize" << std::endl; return 0; } if (!inputImage || bufferLength == 0) { std::cerr << "Error: Invalid input image in ANSCV_ImageResize" << std::endl; return 0; } if (!outputImage) { std::cerr << "Error: Invalid output handle in ANSCV_ImageResize" << std::endl; return 0; } if (width <= 0 || height <= 0) { std::cerr << "Error: Invalid dimensions in ANSCV_ImageResize" << std::endl; return 0; } try { // Decode input image (no copy needed - uses existing buffer) cv::Mat inputFrame = cv::imdecode( cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR ); if (inputFrame.empty()) { std::cerr << "Error: Failed to decode image in ANSCV_ImageResize" << std::endl; return 0; } // Resize image cv::Mat outputFrame; (*Handle)->ImageResize(inputFrame, width, height, outputFrame); if (outputFrame.empty()) { std::cerr << "Error: ImageResize failed in ANSCV_ImageResize" << std::endl; return 0; } // Convert to binary data std::string binaryData = (*Handle)->MatToBinaryData(outputFrame); // CRITICAL: No need to manually release - RAII handles it // inputFrame.release(); // Removed - automatic // outputFrame.release(); // Removed - automatic const int size = static_cast(binaryData.length()); if (size <= 0) { std::cerr << "Error: MatToBinaryData returned empty in ANSCV_ImageResize" << std::endl; return 0; } // Resize LabVIEW string handle MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) { std::cerr << "Error: DSSetHandleSize failed in ANSCV_ImageResize (error code: " << error << ")" << std::endl; return 0; } // Copy data to LabVIEW string (*outputImage)->cnt = size; memcpy((*outputImage)->str, binaryData.c_str(), size); return 1; // Success } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSCV_ImageResize: " << e.what() << std::endl; return 0; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ImageResize: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Unknown exception in ANSCV_ImageResize" << std::endl; return 0; } } extern "C" __declspec(dllexport) int ANSCV_ImageResizeWithRatio(ANSCENTER::ANSOPENCV** Handle,unsigned char* inputImage,unsigned int bufferLength,int width,LStrHandle outputImage) { // Validate inputs if (!Handle || !*Handle) { std::cerr << "Error: Invalid Handle in ANSCV_ImageResizeWithRatio" << std::endl; return 0; } if (!inputImage || bufferLength == 0) { std::cerr << "Error: Invalid input image in ANSCV_ImageResizeWithRatio" << std::endl; return 0; } if (!outputImage) { std::cerr << "Error: Invalid output handle in ANSCV_ImageResizeWithRatio" << std::endl; return 0; } if (width <= 0) { std::cerr << "Error: Invalid width in ANSCV_ImageResizeWithRatio" << std::endl; return 0; } try { // Decode input image cv::Mat inputFrame = cv::imdecode( cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR ); if (inputFrame.empty()) { std::cerr << "Error: Failed to decode image in ANSCV_ImageResizeWithRatio" << std::endl; return 0; } // Resize with aspect ratio preservation cv::Mat outputFrame; (*Handle)->ImageResizeWithRatio(inputFrame, width, outputFrame); if (outputFrame.empty()) { std::cerr << "Error: ImageResizeWithRatio failed" << std::endl; return 0; } // Convert to binary data std::string binaryData = (*Handle)->MatToBinaryData(outputFrame); // RAII handles cleanup automatically - no manual .release() needed const int size = static_cast(binaryData.length()); if (size <= 0) { std::cerr << "Error: MatToBinaryData returned empty" << std::endl; return 0; } // Resize LabVIEW string handle MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) { std::cerr << "Error: DSSetHandleSize failed (error code: " << error << ")" << std::endl; return 0; } // Copy data to LabVIEW string (*outputImage)->cnt = size; memcpy((*outputImage)->str, binaryData.c_str(), size); return 1; // Success } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSCV_ImageResizeWithRatio: " << e.what() << std::endl; return 0; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ImageResizeWithRatio: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Unknown exception in ANSCV_ImageResizeWithRatio" << std::endl; return 0; } } extern "C" __declspec(dllexport) int ANSCV_ImageToBase64(ANSCENTER::ANSOPENCV** Handle,unsigned char* inputImage,unsigned int bufferLength,LStrHandle outputImage) { // Validate inputs if (!Handle || !*Handle) { std::cerr << "Error: Invalid Handle in ANSCV_ImageToBase64" << std::endl; return 0; } if (!inputImage || bufferLength == 0) { std::cerr << "Error: Invalid input image in ANSCV_ImageToBase64" << std::endl; return 0; } if (!outputImage) { std::cerr << "Error: Invalid output handle in ANSCV_ImageToBase64" << std::endl; return 0; } try { // Decode input image cv::Mat inputFrame = cv::imdecode( cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR ); if (inputFrame.empty()) { std::cerr << "Error: Failed to decode image in ANSCV_ImageToBase64" << std::endl; return 0; } // Convert to Base64 std::string base64Data = (*Handle)->MatToBase64(inputFrame); // RAII handles cleanup automatically - no manual .release() needed const int size = static_cast(base64Data.length()); if (size <= 0) { std::cerr << "Error: MatToBase64 returned empty string" << std::endl; return 0; } // Resize LabVIEW string handle MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) { std::cerr << "Error: DSSetHandleSize failed in ANSCV_ImageToBase64 (error code: " << error << ")" << std::endl; return 0; } // Copy data to LabVIEW string (*outputImage)->cnt = size; memcpy((*outputImage)->str, base64Data.c_str(), size); return 1; // Success } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSCV_ImageToBase64: " << e.what() << std::endl; return 0; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ImageToBase64: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Unknown exception in ANSCV_ImageToBase64" << std::endl; return 0; } } extern "C" __declspec(dllexport) int ANSCV_ImageToGray(ANSCENTER::ANSOPENCV** Handle,unsigned char* inputImage,unsigned int bufferLength,LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ToGray(inputFrame); std::string st = (*Handle)->MatToBinaryData(outputFrame); inputFrame.release(); outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageDenoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ImageDenoise(inputFrame); std::string st = (*Handle)->MatToBinaryData(outputFrame); //inputFrame.release(); //outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageRepair(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ImageRepair(inputFrame); std::string st = (*Handle)->MatToBinaryData(outputFrame); //inputFrame.release(); //outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageAutoWhiteBalance(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ImageWhiteBalance(inputFrame); std::string st = (*Handle)->MatToBinaryData(outputFrame); //inputFrame.release(); //outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageBrightEnhance(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double brightnessScaleFactor, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ImageDarkEnhancement(inputFrame, brightnessScaleFactor); std::string st = (*Handle)->MatToBinaryData(outputFrame); //inputFrame.release(); //outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageContrastEnhance(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ImageContrastEnhancement(inputFrame); std::string st = (*Handle)->MatToBinaryData(outputFrame); //inputFrame.release(); //outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_BlurObjects(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); std::vector objects = (*Handle)->GetBoundingBoxes(strBboxes); cv::Mat outputFrame = (*Handle)->BlurObjects(inputFrame, objects); std::string st = (*Handle)->MatToBinaryData(outputFrame); //inputFrame.release(); //outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_BlurBackground(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); std::vector objects = (*Handle)->GetBoundingBoxes(strBboxes); cv::Mat outputFrame = (*Handle)->BlurBackground(inputFrame, objects); std::string st = (*Handle)->MatToBinaryData(outputFrame); //outputFrame.release(); //inputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_QRDecoder(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle detectedQRText) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); std::string st = (*Handle)->QRDecoder(inputFrame); //inputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(detectedQRText, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectedQRText)->cnt = size; memcpy((*detectedQRText)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_QRDecoderCV(ANSCENTER::ANSOPENCV** Handle, const cv::Mat& image, std::string& detectedQRText) { if (Handle == nullptr || *Handle == nullptr) return -1; if (image.empty()) return 0; // Indicate failure if image is empty try { detectedQRText = (*Handle)->QRDecoder(image); return 1; // Indicate success } catch (const std::exception& e) { std::cerr << "Error in ANSCV_QRDecoderCV: " << e.what() << std::endl; return -1; // Indicate failure } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_PatternMatchs(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, const char* templateFilePath, double threshold, LStrHandle detectedMatchedLocations) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat templateImage = cv::imread(templateFilePath, cv::IMREAD_COLOR); std::string st = (*Handle)->PatternMatches(inputFrame, templateImage, threshold); //inputFrame.release(); //templateImage.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(detectedMatchedLocations, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectedMatchedLocations)->cnt = size; memcpy((*detectedMatchedLocations)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageCrop(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int x, int y, int width, int height, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Rect roi(x, y, width, height); cv::Mat outputFrame = (*Handle)->ImageCrop(inputFrame, roi); std::string st = (*Handle)->MatToBinaryData(outputFrame); outputFrame.release(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_GetImageSize(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle imageSize) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); int width, height; width = inputFrame.cols; height = inputFrame.rows; //inputFrame.release(); std::string st = std::to_string(width) + "x" + std::to_string(height); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(imageSize, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*imageSize)->cnt = size; memcpy((*imageSize)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_GetImageSizeFromImageFile(ANSCENTER::ANSOPENCV** Handle, const char* imageFilePath, LStrHandle imageSize) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::VideoCapture cap(imageFilePath); int width, height; if (!cap.isOpened()) { std::cerr << "Error opening file: " << imageFilePath << std::endl; width = 0; height = 0; } else { width = static_cast(cap.get(cv::CAP_PROP_FRAME_WIDTH)); height = static_cast(cap.get(cv::CAP_PROP_FRAME_HEIGHT)); } cap.release(); std::string st = std::to_string(width) + "x" + std::to_string(height); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(imageSize, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*imageSize)->cnt = size; memcpy((*imageSize)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_RotateImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double angle, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->RotateImage(inputFrame, angle); std::string st = (*Handle)->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_FlipImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int flipCode, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->FlipImage(inputFrame, flipCode); std::string st = (*Handle)->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ShiftImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int shiftX, int shiftY, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->ShiftImage(inputFrame, shiftX, shiftY); std::string st = (*Handle)->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_AddGaussianNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double mean, double stddev, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->AddGaussianNoise(inputFrame, mean, stddev); std::string st = (*Handle)->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_AddSaltAndPepperNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double amount, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->AddSaltAndPepperNoise(inputFrame, amount); std::string st = (*Handle)->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_AddSpeckleNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double stddev, LStrHandle outputImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = (*Handle)->AddSpeckleNoise(inputFrame, stddev); std::string st = (*Handle)->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) void ANSCV_InitCameraResource() { try { std::lock_guard lock(imageMutex); // Automatically locks and unlocks ANSCENTER::ANSOPENCV::InitCameraNetwork(); } catch (...) { } } extern "C" __declspec(dllexport) void ANSCV_FreeCameraResource() { try { std::lock_guard lock(imageMutex); // Automatically locks and unlocks ANSCENTER::ANSOPENCV::DeinitCameraNetwork(); } catch (...) { } } extern "C" __declspec(dllexport) int ANSCV_ResizeImage_Static(unsigned char* inputImage, unsigned int bufferLength, int width, int height, int& newWidth, int& newHeight, LStrHandle outputImage) { //std::lock_guard lock(imageMutex); // Automatically locks and unlocks std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ResizeImage_Static!" << std::endl; return -6; } try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = ANSCENTER::ANSOPENCV::resizeImageToFit(inputFrame, width, height, newWidth, newHeight); std::vector imageData; bool success = cv::imencode(".jpg", outputFrame, imageData); inputFrame.release(); outputFrame.release(); if (success) { std::string st(imageData.begin(), imageData.end()); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } else return 0; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ResizeImage_Static: " << e.what() << std::endl; return 0; } } // Image Reference Management extern "C" __declspec(dllexport) int ANSCV_CloneImage_S(cv::Mat** imageIn, cv::Mat** imageOut) { try { //std::lock_guard lock(imageMutex); std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_CloneImage_S!" << std::endl; return -6; } try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } else { *imageOut = anscv_mat_new(**imageIn); // Link clone to same NV12 frame data (refcount++) gpu_frame_addref(*imageIn, *imageOut); return 1; // Success } } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed in ANSCV_CloneImage_S: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_CloneImage_S: " << e.what() << std::endl; return -3; } } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed in ANSCV_CloneImage_S: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_CloneImage_S: " << e.what() << std::endl; return -3; } } extern "C" __declspec(dllexport) int ANSCV_ReleaseImage_S(cv::Mat** imageIn) { try { if (!imageIn || !(*imageIn)) { return -2; } // anscv_mat_delete is thread-safe: checks the registry, only deletes if // the pointer is still registered (not already freed by a stream source). bool deleted = anscv_mat_delete(imageIn); return deleted ? 1 : -4; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ReleaseImage_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception in ANSCV_ReleaseImage_S." << std::endl; return -3; } } extern "C" __declspec(dllexport) int ANSCV_CropImage_S(cv::Mat** imageIn, int x, int y, int width, int height, int originalImageSize) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { // Step 1: Validate and copy input image safely cv::Mat localImage; { //std::lock_guard lock(imageMutex); std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_CropImage_S!" << std::endl; return -6; } try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } else { localImage = (**imageIn).clone(); // shallow copy (ref-counted) } } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed in ANSCV_CloneImage_S: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_CloneImage_S: " << e.what() << std::endl; return -3; } } // Step 2: Validate cropping parameters if (width <= 0 || height <= 0) { std::cerr << "Error: Invalid width or height for cropping image in ANSCV_CropImage_S!" << std::endl; return -2; } int originalWidth = localImage.cols; int originalHeight = localImage.rows; x = max(0, x); y = max(0, y); width = min(width, originalWidth - x); height = min(height, originalHeight - y); cv::Rect roi(x, y, width, height); // Step 3: Process crop outside lock ANSCENTER::ANSOPENCV ansCVInstance; if (!ansCVInstance.Init("")) { std::cerr << "Error: Failed to initialize ANSCV instance!" << std::endl; return -5; } cv::Mat croppedImage = ansCVInstance.ImageCrop(localImage, roi, originalImageSize); // Step 4: Replace original image safely { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_CropImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); if (croppedImage.empty()) { std::cerr << "Error: Failed to crop image in ANSCV_CropImage_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(croppedImage); } } return 1; // Success } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_CropImage_S: " << e.what() << std::endl; return -3; } } extern "C" __declspec(dllexport) int ANSCV_GetImage_CPP(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, std::string& outputImage) { try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image!" << std::endl; return -2; } cv::Mat* img = *imageIn; int originalWidth, originalHeight; // Quick dimension check under lock { std::unique_lock lock(timeImageMutex, std::chrono::milliseconds(1000)); if (!lock.owns_lock()) { std::cerr << "Error: Mutex timeout!" << std::endl; return -6; } if (img->empty() || !img->data) { std::cerr << "Error: Invalid image!" << std::endl; return -2; } originalWidth = img->cols; originalHeight = img->rows; } // Release lock early int imageMaxSize = max(originalWidth, originalHeight); cv::Mat processedImage; if (width > 0 && width < imageMaxSize) { // Only lock when we need to resize std::unique_lock lock(timeImageMutex, std::chrono::milliseconds(1000)); if (!lock.owns_lock()) return -6; ANSCENTER::ANSOPENCV ansCVInstance; if (!ansCVInstance.Init("")) return -5; processedImage = ansCVInstance.ImageResizeV2(*img, width); } else { // No resize needed - just copy quickly std::unique_lock lock(timeImageMutex, std::chrono::milliseconds(1000)); if (!lock.owns_lock()) return -6; processedImage = img->clone(); // Slightly faster than copyTo } if (processedImage.empty()) return -8; newWidth = processedImage.cols; newHeight = processedImage.rows; outputImage = ANSCENTER::CompressJpegToString(processedImage, quality); return outputImage.empty() ? -9 : 1; } catch (...) { return -4; } } //extern "C" __declspec(dllexport) int ANSCV_GetImage_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage) { // try { // // Initial validation // if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { // std::cerr << "Error: Invalid or empty input image in ANSCV_GetImage_S!" << std::endl; // return -2; // } // if (!outputImage) { // std::cerr << "Error: Output image handle is null!" << std::endl; // return -2; // } // // cv::Mat imgCopy; // // // Critical section: Only lock during image copy // { // auto lockStartTime = std::chrono::steady_clock::now(); // std::unique_lock lock(timeImageMutex, std::defer_lock); // // // Increased timeout to 30 seconds // if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { // auto elapsed = std::chrono::duration_cast( // std::chrono::steady_clock::now() - lockStartTime).count(); // std::cerr << "Error: Mutex timeout after " << elapsed << "ms in ANSCV_GetImage_S!" << std::endl; // return -6; // } // // auto lockAcquiredTime = std::chrono::duration_cast( // std::chrono::steady_clock::now() - lockStartTime).count(); // if (lockAcquiredTime > 1000) { // std::cerr << "Warning: Lock acquisition took " << lockAcquiredTime << "ms" << std::endl; // } // // // Re-validate after acquiring lock // if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { // std::cerr << "Error: Image became invalid after mutex acquisition!" << std::endl; // return -2; // } // // // Copy the image while holding the lock // auto copyStartTime = std::chrono::steady_clock::now(); // try { // (*imageIn)->copyTo(imgCopy); // } // catch (const cv::Exception& e) { // std::cerr << "Error: OpenCV exception during image copy: " << e.what() << std::endl; // return -7; // } // // auto copyElapsed = std::chrono::duration_cast( // std::chrono::steady_clock::now() - copyStartTime).count(); // if (copyElapsed > 500) { // std::cerr << "Warning: Image copy took " << copyElapsed << "ms (size: " // << (*imageIn)->cols << "x" << (*imageIn)->rows << ")" << std::endl; // } // } // Lock released here - all subsequent operations run without holding the mutex // // // Validate copied image // if (imgCopy.empty() || !imgCopy.data) { // std::cerr << "Error: Copied image is invalid in ANSCV_GetImage_S!" << std::endl; // return -2; // } // // // Store original dimensions // int originalWidth = imgCopy.cols; // int originalHeight = imgCopy.rows; // int imageMaxSize = max(originalWidth, originalHeight); // // // Resize if requested // if (width > 0 && width < imageMaxSize) { // auto resizeStartTime = std::chrono::steady_clock::now(); // // ANSCENTER::ANSOPENCV ansCVInstance; // if (!ansCVInstance.Init("")) { // std::cerr << "Error: Failed to initialize ANSOPENCV instance!" << std::endl; // return -5; // } // // cv::Mat resized = ansCVInstance.ImageResizeV2(imgCopy, width); // if (resized.empty()) { // std::cerr << "Error: Resizing failed!" << std::endl; // return -8; // } // // imgCopy = std::move(resized); // // auto resizeElapsed = std::chrono::duration_cast( // std::chrono::steady_clock::now() - resizeStartTime).count(); // if (resizeElapsed > 500) { // std::cerr << "Warning: Image resize took " << resizeElapsed << "ms (from " // << originalWidth << "x" << originalHeight << " to " // << imgCopy.cols << "x" << imgCopy.rows << ")" << std::endl; // } // } // // // Update output dimensions // newWidth = imgCopy.cols; // newHeight = imgCopy.rows; // // // Compress to JPEG // auto compressStartTime = std::chrono::steady_clock::now(); // std::string jpegString = ANSCENTER::CompressJpegToString(imgCopy, quality); // if (jpegString.empty()) { // std::cerr << "Error: JPEG compression failed!" << std::endl; // return -9; // } // // auto compressElapsed = std::chrono::duration_cast( // std::chrono::steady_clock::now() - compressStartTime).count(); // if (compressElapsed > 500) { // std::cerr << "Warning: JPEG compression took " << compressElapsed << "ms (quality: " // << quality << ", size: " << jpegString.size() << " bytes)" << std::endl; // } // // // Validate compressed size // int32_t size = static_cast(jpegString.size()); // if (size > 50 * 1024 * 1024) { // std::cerr << "Error: Compressed image size too large: " << size << " bytes" << std::endl; // return -10; // } // // // Allocate memory for output // MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size); // if (error != noErr) { // std::cerr << "Error: Failed to allocate memory for output image! Error code: " << error << std::endl; // return -10; // } // // // Copy data to output handle // (*outputImage)->cnt = size; // memcpy((*outputImage)->str, jpegString.data(), size); // // return 1; // } // catch (const std::exception& e) { // std::cerr << "Exception in ANSCV_GetImage_S: " << e.what() << std::endl; // return -3; // } // catch (...) { // std::cerr << "Unknown exception in ANSCV_GetImage_S!" << std::endl; // return -4; // } //} extern "C" __declspec(dllexport) int ANSCV_GetImage_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage) { try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_GetImage_S!" << std::endl; return -2; } if (!outputImage) { std::cerr << "Error: Output image handle is null!" << std::endl; return -2; } cv::Mat imgCopy; { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_GetImage_S!" << std::endl; return -6; } if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Image became invalid after mutex acquisition!" << std::endl; return -2; } try { (*imageIn)->copyTo(imgCopy); } catch (const cv::Exception& e) { std::cerr << "Error: OpenCV exception during image copy: " << e.what() << std::endl; return -7; } } if (imgCopy.empty() || !imgCopy.data) { std::cerr << "Error: Copied image is invalid in ANSCV_GetImage_S!" << std::endl; return -2; } int originalWidth = imgCopy.cols; int originalHeight = imgCopy.rows; int imageMaxSize = max(originalWidth, originalHeight); if (width > 0 && width < imageMaxSize) { ANSCENTER::ANSOPENCV ansCVInstance; if (!ansCVInstance.Init("")) { std::cerr << "Error: Failed to initialize ANSOPENCV instance!" << std::endl; return -5; } cv::Mat resized = ansCVInstance.ImageResizeV2(imgCopy, width); if (resized.empty()) { std::cerr << "Error: Resizing failed!" << std::endl; return -8; } imgCopy = std::move(resized); } newWidth = imgCopy.cols; newHeight = imgCopy.rows; std::string jpegString = ANSCENTER::CompressJpegToString(imgCopy, quality); if (jpegString.empty()) { std::cerr << "Error: JPEG compression failed!" << std::endl; return -9; } int32_t size = static_cast(jpegString.size()); if (size > 50 * 1024 * 1024) { std::cerr << "Error: Compressed image size too large: " << size << " bytes" << std::endl; return -10; } MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size); if (error != noErr) { std::cerr << "Error: Failed to allocate memory for output image! Error code: " << error << std::endl; return -10; } (*outputImage)->cnt = size; memcpy((*outputImage)->str, jpegString.data(), size); return 1; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_GetImage_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Unknown exception in ANSCV_GetImage_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ReSizeImage_S(cv::Mat** imageIn, int width, int originalImageSize) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { cv::Mat localImage; { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } else { localImage = **imageIn; // shallow copy (ref-counted) } } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed in ANSCV_CloneImage_S: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_CloneImage_S: " << e.what() << std::endl; return -3; } } if (width <= 0) { std::cerr << "Error: Invalid target width in ANSCV_ReSizeImage_S!" << std::endl; return -2; } const int originalWidth = localImage.cols; const int originalHeight = localImage.rows; int targetWidth = width; cv::Mat resizedImage; // Scale width based on original image size, if provided if (originalImageSize > 0 && originalImageSize != originalWidth) { double scale = static_cast(originalWidth) / originalImageSize; targetWidth = static_cast(width * scale); } // Skip resizing if target size is greater or equal to original if (targetWidth < originalWidth) { // Maintain aspect ratio int targetHeight = static_cast(std::round(targetWidth * static_cast(originalHeight) / originalWidth)); if (targetHeight <= 0) { std::cerr << "Error: Computed height is invalid!" << std::endl; return -2; } cv::resize(localImage, resizedImage, cv::Size(targetWidth, targetHeight), 0, 0, cv::INTER_LANCZOS4); } else { resizedImage = localImage.clone(); // No resizing needed } { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); if (resizedImage.empty()) { std::cerr << "Error: Resizing failed!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(resizedImage); } } return 1; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ReSizeImage_S: " << e.what() << std::endl; return -3; } } extern "C" __declspec(dllexport) int ANSCV_GetImageAndRemoveImgRef_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage) { bool cleanupRequired = true; try { cv::Mat imgCopy; { //std::lock_guard lock(imageMutex); std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_GetImageAndRemoveImgRef_S!" << std::endl; return -2; } (*imageIn)->copyTo(imgCopy); anscv_mat_delete(imageIn); // Safe delete + null cleanupRequired = false; // We already deleted inside lock } if (imgCopy.empty() || !imgCopy.data) { std::cerr << "Error: Copied image is invalid in ANSCV_GetImageAndRemoveImgRef_S!" << std::endl; return 0; } int originalWidth = imgCopy.cols; int originalHeight = imgCopy.rows; int imageMaxSize = max(originalWidth, originalHeight); if (width > 0 && width < imageMaxSize) { ANSCENTER::ANSOPENCV ansCVInstance; if (!ansCVInstance.Init("")) { std::cerr << "Error: Failed to initialize ANSOPENCV instance!" << std::endl; return -5; } cv::Mat resized = ansCVInstance.ImageResizeV2(imgCopy, width); if (resized.empty()) { std::cerr << "Error: Resizing failed!" << std::endl; return 0; } imgCopy = std::move(resized); } newWidth = imgCopy.cols; newHeight = imgCopy.rows; std::string jpegString = ANSCENTER::CompressJpegToString(imgCopy, quality); if (jpegString.empty()) { std::cerr << "Error: JPEG compression failed in ANSCV_GetImageAndRemoveImgRef_S!" << std::endl; return 0; } int32_t size = static_cast(jpegString.size()); MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size); if (error != noErr) { std::cerr << "Error: Failed to allocate memory for output image!" << std::endl; return 0; } (*outputImage)->cnt = size; memcpy((*outputImage)->str, jpegString.data(), size); return 1; // Success } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_GetImageAndRemoveImgRef_S: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown exception in ANSCV_GetImageAndRemoveImgRef_S!" << std::endl; } // Cleanup in case of exception and still pending if (cleanupRequired) { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); anscv_mat_delete(imageIn); } return -3; } extern "C" __declspec(dllexport) int ANSCV_GetImageInfo_S(cv::Mat** imageIn, int& width, int& height) { try { cv::Mat imgCopy; { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } else { imgCopy = **imageIn; // shallow copy (ref-counted) } } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed in ANSCV_CloneImage_S: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_CloneImage_S: " << e.what() << std::endl; return -3; } } if (imgCopy.empty() || !imgCopy.data) { std::cerr << "Error: Dereferenced image is invalid in ANSCV_GetImageInfo_S!" << std::endl; return 0; } // Assign the width and height once the image is validated width = imgCopy.cols; height = imgCopy.rows; return 1; // Success } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_GetImageInfo_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Unknown exception in ANSCV_GetImageInfo_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_CreateImageFromJpegString_S( unsigned char* inputImage, unsigned int bufferLength, cv::Mat** image) { // Validate input parameters if (!inputImage || bufferLength == 0 || image == nullptr) { std::cerr << "Error: Invalid input parameters in ANSCV_CreateImageFromJpegString_S!" << std::endl; return -2; } try { // Copy input buffer for decoding std::vector buffer(inputImage, inputImage + bufferLength); cv::Mat decodedImage = cv::imdecode(buffer, cv::IMREAD_COLOR); // Allocate image safely { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Check if decoding was successful if (decodedImage.empty()) { std::cerr << "Error: Failed to decode JPEG image in ANSCV_CreateImageFromJpegString_S!" << std::endl; return 0; } else { *image = anscv_mat_new(decodedImage); return 1; // Success } } } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed in ANSCV_CreateImageFromJpegString_S: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_CreateImageFromJpegString_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Unknown exception in ANSCV_CreateImageFromJpegString_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_CreateImageFromFile_S(const char* imageFilePath, cv::Mat** image) { try { if (!imageFilePath) { std::cerr << "Error: Null input parameter in ANSCV_CreateImageFromFile_S!" << std::endl; return -1; // Null pointer input } std::string stImageFileName(imageFilePath); if (stImageFileName.empty()) { std::cerr << "Error: Empty file path in ANSCV_CreateImageFromFile_S!" << std::endl; return -2; // Empty path } // Check if file exists std::ifstream fileCheck(stImageFileName); if (!fileCheck.good()) { std::cerr << "Error: File does not exist or is inaccessible: " << stImageFileName << std::endl; return -4; // File does not exist } // Load the image using OpenCV cv::Mat loadedImage = cv::imread(stImageFileName, cv::ImreadModes::IMREAD_COLOR); { // Allocate and assign the image //std::lock_guard lock(imageMutex); // Automatically locks and unlocks std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } if (loadedImage.empty()) { std::cerr << "Error: Failed to load image from file in ANSCV_CreateImageFromFile_S!" << std::endl; return 0; // Load failed } else { if (image == nullptr) { image = new cv::Mat*; // Allocate pointer to cv::Mat* *image = nullptr; // Initialize to nullptr } *image = anscv_mat_new(loadedImage); // Use registry for safe lifecycle return 1; // Success } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_CreateImageFromFile_S: " << e.what() << std::endl; return -3; // Exception } catch (...) { std::cerr << "Unknown error occurred in ANSCV_CreateImageFromFile_S!" << std::endl; return -5; // Unknown error } } // Image Preprocessing extern "C" __declspec(dllexport) int ANSCV_ImageAutoWhiteBalance_S(cv::Mat** imageIn) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } try { ANSCENTER::ANSOPENCV ansCVInstance; if (!ansCVInstance.Init("")) { std::cerr << "Error: Failed to initialize ANSCV instance!" << std::endl; return -5; } cv::Mat imOut = ansCVInstance.ImageWhiteBalance(**imageIn); // Thread-safe assignment { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageAutoWhiteBalance_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageAutoWhiteBalance_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageAutoWhiteBalance_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageBrightEnhance_S(cv::Mat** imageIn, double brightnessScaleFactor) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance cv::Mat imOut = ansCVInstance.ImageDarkEnhancement(**imageIn, brightnessScaleFactor); { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Lock only during shared resource write if (imOut.empty()) { std::cerr << "Error: Brightness enhancement failed in ANSCV_ImageBrightEnhance_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageBrightEnhance_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageBrightEnhance_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageContrastEnhance_S(cv::Mat** imageIn) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } try { ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Perform white balance correction cv::Mat imOut = ansCVInstance.ImageContrastEnhancement(**imageIn); { // Assign processed image back to input pointer //std::lock_guard lock(imageMutex); // Lock only during shared resource write std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageContrastEnhance_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; // Success } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageContrastEnhance_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageContrastEnhance_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageDenoise_S(cv::Mat** imageIn) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Perform denoising cv::Mat imOut = ansCVInstance.ImageDenoise(**imageIn); { //std::lock_guard lock(imageMutex); // Lock only during shared resource modification std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } if (imOut.empty()) { std::cerr << "Error: Denoising processing failed in ANSCV_ImageDenoise_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; // Success } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageDenoise_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageDenoise_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageRepair_S(cv::Mat** imageIn) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Perform image repair cv::Mat imOut = ansCVInstance.ImageRepair(**imageIn); { //std::lock_guard lock(imageMutex); // Lock only during shared resource modification std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } if (imOut.empty()) { std::cerr << "Error: Image repair processing failed in ANSCV_ImageRepair_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; // Success } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageRepair_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageRepair_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageToGray_S(cv::Mat** imageIn) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Perform white balance correction cv::Mat imOut = ansCVInstance.ToGray(**imageIn); { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Lock only during shared resource modification if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageToGray_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageToGray_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageToGray_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageRotate_S(cv::Mat** imageIn, double angle) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Perform white balance correction cv::Mat imOut = ansCVInstance.RotateImage(**imageIn, angle); // Assign processed image back to input pointer { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Ensure thread safety if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageRotate_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageRotate_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageRotate_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageFlip_S(cv::Mat** imageIn, int flipCode) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Perform white balance correction cv::Mat imOut = ansCVInstance.FlipImage(**imageIn, flipCode); // Assign processed image back to input pointer { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Ensure thread safety if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageFlip_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageFlip_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageFlip_S!" << std::endl; return -4; } } // Post processing extern "C" __declspec(dllexport) int ANSCV_ImageBlurObjects_S(cv::Mat** imageIn, const char* strBboxes) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance std::vector objects = ansCVInstance.GetBoundingBoxes(strBboxes); // Perform white balance correction cv::Mat imOut = ansCVInstance.BlurObjects(**imageIn, objects); // Assign processed image back to input pointer { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Ensure thread safety if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageBlurObjects_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageBlurObjects_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageBlurObjects_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageBlurBackground_S(cv::Mat** imageIn, const char* strBboxes) { gpu_frame_invalidate(imageIn ? *imageIn : nullptr); try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance std::vector objects = ansCVInstance.GetBoundingBoxes(strBboxes); // Perform white balance correction cv::Mat imOut = ansCVInstance.BlurBackground(**imageIn, objects); // Assign processed image back to input pointer { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Ensure thread safety if (imOut.empty()) { std::cerr << "Error: White balance processing failed in ANSCV_ImageBlurBackground_S!" << std::endl; return 0; } else { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } **imageIn = std::move(imOut); return 1; } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageBlurBackground_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageBlurBackground_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImageQRDecoder_S(cv::Mat** imageIn, int maxImageWidth, const char* strBboxes, LStrHandle detectedQRText) { try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance std::vector Bboxes = ansCVInstance.GetBoundingBoxes(strBboxes); // Decode the QR code std::string qrText = ansCVInstance.QRDecoderWithBBox(**imageIn, maxImageWidth, Bboxes); { // Assign QR decoded text to detectedQRText handle std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Ensure thread safety when modifying the handle if (qrText.empty()) { std::cerr << "Error: QR decoding failed in ANSCV_ImageQRDecoder_S!" << std::endl; return 0; } int size = qrText.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(detectedQRText, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectedQRText)->cnt = size; memcpy((*detectedQRText)->str, qrText.c_str(), size); return 1; // Success } else { return 0; // Error setting handle size } } else { return 0; // No QR code found } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImageQRDecoder_S: " << e.what() << std::endl; return -3; } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImageQRDecoder_S!" << std::endl; return -4; } } extern "C" __declspec(dllexport) int ANSCV_ImagePatternMatchs_S(cv::Mat** imageIn, const char* templateFilePath, double threshold, LStrHandle detectedMatchedLocations) { try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !(*imageIn)->data) { std::cerr << "Error: Invalid or empty input image in ANSCV_CloneImage_S!" << std::endl; return -2; } ANSCENTER::ANSOPENCV ansCVInstance; ansCVInstance.Init(""); // Initialize ANSCV instance // Load template image cv::Mat templateImage = cv::imread(templateFilePath, cv::IMREAD_COLOR); if (templateImage.empty()) { std::cerr << "Error: Failed to load template image from " << templateFilePath << std::endl; return -2; // Return error if template cannot be loaded } // Perform pattern matching std::string strMatchedLocations = ansCVInstance.PatternMatches(**imageIn, templateImage, threshold); { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; return -6; } //std::lock_guard lock(imageMutex); // Ensure thread safety when modifying detectedMatchedLocations int size = strMatchedLocations.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(detectedMatchedLocations, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectedMatchedLocations)->cnt = size; memcpy((*detectedMatchedLocations)->str, strMatchedLocations.c_str(), size); return 1; // Success } else { std::cerr << "Error: Failed to set handle size for detectedMatchedLocations!" << std::endl; return 0; // Error setting handle size } } else { return 0; // No matches found } } } catch (const std::exception& e) { std::cerr << "Error: Exception occurred in ANSCV_ImagePatternMatchs_S: " << e.what() << std::endl; return -3; // Exception occurred } catch (...) { std::cerr << "Error: Unknown exception occurred in ANSCV_ImagePatternMatchs_S!" << std::endl; return -4; // Unknown exception } } //extern "C" __declspec(dllexport) int ANSCV_ImagesToMP4_S(const char* imageFolder, const char* outputVideoPath, int targetDurationSec) { // try { // std::unique_lock lock(timeImageMutex, std::defer_lock); // if (!lock.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT_MS))) { // std::cerr << "Error: Mutex timeout in ANSCV_ReSizeImage_S!" << std::endl; // return -6; // } // if (!imageFolder || strlen(imageFolder) == 0) { // std::cerr << "Error: Invalid image folder path in ANSCV_ImagesToMP4_S!" << std::endl; // return -1; // Invalid input // } // // // Create MP4 video from images in the specified folder // bool success = ANSCENTER::ANSOPENCV::ImagesToMP4(imageFolder, outputVideoPath, targetDurationSec); // if (!success) { // std::cerr << "Error: Failed to create MP4 video from images in folder: " << imageFolder << std::endl; // return 0; // Failure // } // // return 1; // Success // } // catch (const std::exception& e) { // std::cerr << "Error: Exception occurred in ANSCV_ImagesToMP4_S: " << e.what() << std::endl; // return -2; // Exception occurred // } // catch (...) { // std::cerr << "Error: Unknown exception occurred in ANSCV_ImagesToMP4_S!" << std::endl; // return -3; // Unknown exception // } // //} extern "C" __declspec(dllexport) int ANSCV_ImagesToMP4_S( const char* imageFolder, const char* outputVideoPath, int targetDurationSec) { try { // Remove the timeImageMutex lock entirely! // ImagesToMP4 already handles per-file synchronization if (!imageFolder || strlen(imageFolder) == 0) { std::cerr << "Error: Invalid image folder path!" << std::endl; return -1; } bool success = ANSCENTER::ANSOPENCV::ImagesToMP4( imageFolder, outputVideoPath, targetDurationSec); if (!success) { std::cerr << "Error: Failed to create MP4 from: " << imageFolder << std::endl; return 0; } return 1; } catch (const std::exception& e) { std::cerr << "Error: Exception in ANSCV_ImagesToMP4_S: " << e.what() << std::endl; return -2; } catch (...) { std::cerr << "Error: Unknown exception!" << std::endl; return -3; } } // ============================================================================ // V2 functions: accept uint64_t handleVal by value instead of ANSOPENCV** // This eliminates the LabVIEW buffer reuse bug with double-pointer handles. // ============================================================================ extern "C" __declspec(dllexport) int ANSCV_ImageResize_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, int width, int height, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; if (!inputImage || bufferLength == 0) return 0; if (!outputImage) return 0; if (width <= 0 || height <= 0) return 0; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); if (inputFrame.empty()) return 0; cv::Mat outputFrame; h->ImageResize(inputFrame, width, height, outputFrame); if (outputFrame.empty()) return 0; std::string binaryData = h->MatToBinaryData(outputFrame); const int size = static_cast(binaryData.length()); if (size <= 0) return 0; MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*outputImage)->cnt = size; memcpy((*outputImage)->str, binaryData.c_str(), size); return 1; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSCV_ImageResize_V2: " << e.what() << std::endl; return 0; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ImageResize_V2: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Unknown exception in ANSCV_ImageResize_V2" << std::endl; return 0; } } extern "C" __declspec(dllexport) int ANSCV_ImageResizeWithRatio_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, int width, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; if (!inputImage || bufferLength == 0) return 0; if (!outputImage) return 0; if (width <= 0) return 0; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); if (inputFrame.empty()) return 0; cv::Mat outputFrame; h->ImageResizeWithRatio(inputFrame, width, outputFrame); if (outputFrame.empty()) return 0; std::string binaryData = h->MatToBinaryData(outputFrame); const int size = static_cast(binaryData.length()); if (size <= 0) return 0; MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*outputImage)->cnt = size; memcpy((*outputImage)->str, binaryData.c_str(), size); return 1; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSCV_ImageResizeWithRatio_V2: " << e.what() << std::endl; return 0; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ImageResizeWithRatio_V2: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Unknown exception in ANSCV_ImageResizeWithRatio_V2" << std::endl; return 0; } } extern "C" __declspec(dllexport) int ANSCV_ImageToBase64_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; if (!inputImage || bufferLength == 0) return 0; if (!outputImage) return 0; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); if (inputFrame.empty()) return 0; std::string base64Data = h->MatToBase64(inputFrame); const int size = static_cast(base64Data.length()); if (size <= 0) return 0; MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error != noErr) return 0; (*outputImage)->cnt = size; memcpy((*outputImage)->str, base64Data.c_str(), size); return 1; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in ANSCV_ImageToBase64_V2: " << e.what() << std::endl; return 0; } catch (const std::exception& e) { std::cerr << "Exception in ANSCV_ImageToBase64_V2: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Unknown exception in ANSCV_ImageToBase64_V2" << std::endl; return 0; } } extern "C" __declspec(dllexport) int ANSCV_ImageToGray_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ToGray(inputFrame); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageDenoise_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ImageDenoise(inputFrame); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageRepair_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ImageRepair(inputFrame); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageAutoWhiteBalance_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ImageWhiteBalance(inputFrame); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageBrightEnhance_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, double brightnessScaleFactor, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ImageDarkEnhancement(inputFrame, brightnessScaleFactor); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageContrastEnhance_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ImageContrastEnhancement(inputFrame); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ImageCrop_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, int x, int y, int width, int height, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Rect roi(x, y, width, height); cv::Mat outputFrame = h->ImageCrop(inputFrame, roi); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_GetImageSize_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle imageSize) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); int width = inputFrame.cols; int height = inputFrame.rows; std::string st = std::to_string(width) + "x" + std::to_string(height); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(imageSize, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*imageSize)->cnt = size; memcpy((*imageSize)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_GetImageSizeFromImageFile_V2(uint64_t handleVal, const char* imageFilePath, LStrHandle imageSize) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::VideoCapture cap(imageFilePath); int width, height; if (!cap.isOpened()) { std::cerr << "Error opening file: " << imageFilePath << std::endl; width = 0; height = 0; } else { width = static_cast(cap.get(cv::CAP_PROP_FRAME_WIDTH)); height = static_cast(cap.get(cv::CAP_PROP_FRAME_HEIGHT)); } cap.release(); std::string st = std::to_string(width) + "x" + std::to_string(height); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(imageSize, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*imageSize)->cnt = size; memcpy((*imageSize)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_BlurObjects_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); std::vector objects = h->GetBoundingBoxes(strBboxes); cv::Mat outputFrame = h->BlurObjects(inputFrame, objects); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_BlurBackground_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); std::vector objects = h->GetBoundingBoxes(strBboxes); cv::Mat outputFrame = h->BlurBackground(inputFrame, objects); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_QRDecoder_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, LStrHandle detectedQRText) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); std::string st = h->QRDecoder(inputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(detectedQRText, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectedQRText)->cnt = size; memcpy((*detectedQRText)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_PatternMatchs_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, const char* templateFilePath, double threshold, LStrHandle detectedMatchedLocations) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat templateImage = cv::imread(templateFilePath, cv::IMREAD_COLOR); std::string st = h->PatternMatches(inputFrame, templateImage, threshold); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(detectedMatchedLocations, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*detectedMatchedLocations)->cnt = size; memcpy((*detectedMatchedLocations)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_RotateImage_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, double angle, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->RotateImage(inputFrame, angle); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_FlipImage_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, int flipCode, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->FlipImage(inputFrame, flipCode); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_ShiftImage_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, int shiftX, int shiftY, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->ShiftImage(inputFrame, shiftX, shiftY); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_AddGaussianNoise_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, double mean, double stddev, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->AddGaussianNoise(inputFrame, mean, stddev); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_AddSaltAndPepperNoise_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, double amount, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->AddSaltAndPepperNoise(inputFrame, amount); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_AddSpeckleNoise_V2(uint64_t handleVal, unsigned char* inputImage, unsigned int bufferLength, double stddev, LStrHandle outputImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR); cv::Mat outputFrame = h->AddSpeckleNoise(inputFrame, stddev); std::string st = h->MatToBinaryData(outputFrame); int size = st.length(); if (size > 0) { MgErr error = DSSetHandleSize(outputImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputImage)->cnt = size; memcpy((*outputImage)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (...) { return -1; } } // ── IMAQ <-> cv::Mat conversion ────────────────────────────────── extern "C" __declspec(dllexport) int ANSCV_IMAQ2Image(Image* imaqImage, cv::Mat** imageOut) { try { if (!imaqImage || !imageOut) return -2; ImageInfo info; if (!imaqGetImageInfo(imaqImage, &info)) return -4; if (!info.imageStart || info.xRes <= 0 || info.yRes <= 0) return -2; int width = info.xRes; int height = info.yRes; int stride = info.pixelsPerLine; // pixels per row (may include padding) 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 } *imageOut = anscv_mat_new(result); return 1; } catch (const std::exception& e) { std::cerr << "Error in ANSCV_IMAQ2Image: " << e.what() << std::endl; return -3; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ANSCV_Image2IMAQ(cv::Mat** imageIn, Image* imaqImage) { try { if (!imageIn || !(*imageIn) || (*imageIn)->empty() || !imaqImage) return -2; const cv::Mat& mat = **imageIn; int width = mat.cols; int height = mat.rows; // Set the IMAQ image size (allocates pixel buffer) if (!imaqSetImageSize(imaqImage, width, height)) return -4; ImageInfo info; if (!imaqGetImageInfo(imaqImage, &info)) return -4; if (!info.imageStart) return -4; int dstStride = info.pixelsPerLine; 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)); } 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)); } 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)); } break; } default: return -5; // Unsupported cv::Mat type } return 1; } catch (const std::exception& e) { std::cerr << "Error in ANSCV_Image2IMAQ: " << e.what() << std::endl; return -3; } catch (...) { return -1; } }