2026-03-28 16:54:11 +11:00
|
|
|
|
#include "ANSOpenCV.h"
|
|
|
|
|
|
#include "ANSMatRegistry.h"
|
2026-04-12 21:55:09 +10:00
|
|
|
|
#include <nivision.h>
|
2026-03-28 16:54:11 +11:00
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
#include <json.hpp>
|
|
|
|
|
|
#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 <filesystem>
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
|
#include <mutex>
|
|
|
|
|
|
#include <turbojpeg.h>
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
#include <future>
|
|
|
|
|
|
#include <opencv2/imgproc.hpp>
|
|
|
|
|
|
#include <opencv2/highgui.hpp>
|
|
|
|
|
|
#include <opencv2/dnn.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SIMD includes
|
|
|
|
|
|
#ifdef __AVX2__
|
|
|
|
|
|
#include <immintrin.h>
|
|
|
|
|
|
#elif defined(__SSE2__)
|
|
|
|
|
|
#include <emmintrin.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
|
|
|
|
#include <libavformat/avformat.h>
|
|
|
|
|
|
#include <libswscale/swscale.h>
|
|
|
|
|
|
}
|
|
|
|
|
|
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 <typename T>
|
|
|
|
|
|
//T GetData(const boost::property_tree::ptree& pt, const std::string& key)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// T ret;
|
|
|
|
|
|
// if (boost::optional<T> data = pt.get_optional<T>(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<unsigned char*>(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<char*>(_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<char*>(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<unsigned char*>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<char*>(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<std::recursive_mutex> 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<char*>(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<std::recursive_mutex> 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<char*>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<double>(width) / inputFrame.cols;
|
|
|
|
|
|
const double scaleY = static_cast<double>(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<std::recursive_mutex> 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<double>(inputFrame.cols) / inputFrame.rows;
|
|
|
|
|
|
int height = static_cast<int>(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<cv::Rect>& objects) {
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> 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<cv::Rect>& objects) {
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<double>(originalWidth) / originalImageSize;
|
|
|
|
|
|
|
|
|
|
|
|
// Use std::round for more accurate scaling
|
|
|
|
|
|
roi.x = static_cast<int>(std::round(resizeROI.x * scale));
|
|
|
|
|
|
roi.y = static_cast<int>(std::round(resizeROI.y * scale));
|
|
|
|
|
|
roi.width = static_cast<int>(std::round(resizeROI.width * scale));
|
|
|
|
|
|
roi.height = static_cast<int>(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<std::recursive_mutex> 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<std::recursive_mutex> lock(_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<DetectionObject> 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<cv::Point> 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<float>(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<std::recursive_mutex> 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<DetectionObject> detectedObjects;
|
|
|
|
|
|
detectedObjects.reserve(barcodes.size());
|
|
|
|
|
|
|
|
|
|
|
|
// Process each barcode
|
|
|
|
|
|
for (const auto& b : barcodes) {
|
|
|
|
|
|
DetectionObject detectedObject;
|
|
|
|
|
|
detectedObject.classId = static_cast<int>(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<cv::Rect>& bBox) {
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> 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<double>(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<DetectionObject> 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<int>(std::round(rect.x * scaleFactor));
|
|
|
|
|
|
scaledRect.y = static_cast<int>(std::round(rect.y * scaleFactor));
|
|
|
|
|
|
scaledRect.width = static_cast<int>(std::round(rect.width * scaleFactor));
|
|
|
|
|
|
scaledRect.height = static_cast<int>(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<int>(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<int>(std::round((xmin + scaledRect.x) * invScaleFactor)),
|
|
|
|
|
|
static_cast<int>(std::round((ymin + scaledRect.y) * invScaleFactor)),
|
|
|
|
|
|
static_cast<int>(std::round(bwidth * invScaleFactor)),
|
|
|
|
|
|
static_cast<int>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> lock(_mutex);
|
|
|
|
|
|
double clipLimit = 2.0;
|
|
|
|
|
|
if (!_licenseValid || src.empty()) {
|
|
|
|
|
|
return src;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
cv::Mat dst;
|
|
|
|
|
|
cv::Ptr<cv::CLAHE> 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<cv::Mat> 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<cv::Mat> 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<std::recursive_mutex> 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<cv::Mat> 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<cv::Rect> ANSOPENCV::GetBoundingBoxes(std::string strBBoxes) {
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
|
|
|
|
|
std::vector<cv::Rect> 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<float>();
|
|
|
|
|
|
const auto y = result["y"].get<float>();
|
|
|
|
|
|
const auto width = result["width"].get<float>();
|
|
|
|
|
|
const auto height = result["height"].get<float>();
|
|
|
|
|
|
|
|
|
|
|
|
cv::Rect rectTemp;
|
|
|
|
|
|
rectTemp.x = static_cast<int>(x);
|
|
|
|
|
|
rectTemp.y = static_cast<int>(y);
|
|
|
|
|
|
rectTemp.width = static_cast<int>(width);
|
|
|
|
|
|
rectTemp.height = static_cast<int>(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<std::recursive_mutex> 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<double>(0, 2) += bbox.width / 2.0 - center.x;
|
|
|
|
|
|
rotationMatrix.at<double>(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<std::recursive_mutex> 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<std::recursive_mutex> 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_<double>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<double>(maxWidth) / width,
|
|
|
|
|
|
static_cast<double>(maxHeight) / height);
|
|
|
|
|
|
|
|
|
|
|
|
// Use rounding for more accurate dimensions
|
|
|
|
|
|
newWidth = static_cast<int>(std::round(width * scale));
|
|
|
|
|
|
newHeight = static_cast<int>(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<DetectionObject>& 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<std::recursive_mutex> 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<double>(intersectionArea) / (box1Area + box2Area - intersectionArea);
|
|
|
|
|
|
return iou;
|
|
|
|
|
|
}
|
|
|
|
|
|
void ANSOPENCV::NonMaximumSuppression(std::vector<DetectionObject>& detectedObjects, double iouThreshold) {
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
|
|
|
|
|
std::sort(detectedObjects.begin(), detectedObjects.end(),
|
|
|
|
|
|
[](const DetectionObject& a, const DetectionObject& b) {
|
|
|
|
|
|
return a.confidence > b.confidence;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<DetectionObject> 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<std::recursive_mutex> 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<double>(originalWidth) / originalImageSize;
|
|
|
|
|
|
targetWidth = static_cast<int>(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<int>(std::round(
|
|
|
|
|
|
targetWidth * static_cast<double>(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<double>(originalWidth) / originalImageSize;
|
|
|
|
|
|
targetWidth = static_cast<int>(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<int>(std::round(
|
|
|
|
|
|
targetWidth * static_cast<double>(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<double>(originalWidth) / originalImageSize;
|
|
|
|
|
|
|
|
|
|
|
|
// Use rounding for more accurate scaling
|
|
|
|
|
|
roi.x = static_cast<int>(std::round(resizeROI.x * scale));
|
|
|
|
|
|
roi.y = static_cast<int>(std::round(resizeROI.y * scale));
|
|
|
|
|
|
roi.width = static_cast<int>(std::round(resizeROI.width * scale));
|
|
|
|
|
|
roi.height = static_cast<int>(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<std::string, std::unique_ptr<std::timed_mutex>> fileMutexes;
|
|
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<std::timed_mutex> fileMutex;
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> 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<std::timed_mutex>();
|
|
|
|
|
|
}
|
|
|
|
|
|
fileMutex = std::shared_ptr<std::timed_mutex>(
|
|
|
|
|
|
fileMutexes[canonicalPath].get(), [](std::timed_mutex*) {});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<cv::String> imageFiles;
|
|
|
|
|
|
const std::vector<std::string> extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp" };
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& ext : extensions) {
|
|
|
|
|
|
std::vector<cv::String> 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<double>(targetWidth) / firstImage.cols;
|
|
|
|
|
|
const double scaleY = static_cast<double>(targetHeight) / firstImage.rows;
|
|
|
|
|
|
const double scale = min(scaleX, scaleY);
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate scaled dimensions (ensure even for H.264)
|
|
|
|
|
|
int scaledWidth = static_cast<int>(std::round(firstImage.cols * scale));
|
|
|
|
|
|
int scaledHeight = static_cast<int>(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<std::pair<std::string, int>> 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<int>(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<double>(frame) * (numImages - 1)) /
|
|
|
|
|
|
(totalFrames - 1);
|
|
|
|
|
|
const int imageIdx = static_cast<int>(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<std::timed_mutex> 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<cv::String> imageFiles;
|
|
|
|
|
|
// const std::vector<std::string> extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp" };
|
|
|
|
|
|
|
|
|
|
|
|
// for (const auto& ext : extensions) {
|
|
|
|
|
|
// std::vector<cv::String> 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<double>(targetWidth) / firstImage.cols;
|
|
|
|
|
|
// const double scaleY = static_cast<double>(targetHeight) / firstImage.rows;
|
|
|
|
|
|
// const double scale = min(scaleX, scaleY);
|
|
|
|
|
|
|
|
|
|
|
|
// // Calculate scaled dimensions (ensure even for H.264)
|
|
|
|
|
|
// int scaledWidth = static_cast<int>(std::round(firstImage.cols * scale));
|
|
|
|
|
|
// int scaledHeight = static_cast<int>(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<std::tuple<std::string, int>> 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<int>(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<double>(frame) * (numImages - 1)) /
|
|
|
|
|
|
// (totalFrames - 1);
|
|
|
|
|
|
// const int imageIdx = static_cast<int>(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<cv::String> imageFiles;
|
|
|
|
|
|
// const std::vector<std::string> extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp" };
|
|
|
|
|
|
//
|
|
|
|
|
|
// for (const auto& ext : extensions) {
|
|
|
|
|
|
// std::vector<cv::String> 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<double>(targetWidth) / firstImage.cols;
|
|
|
|
|
|
// const double scaleY = static_cast<double>(targetHeight) / firstImage.rows;
|
|
|
|
|
|
// const double scale = min(scaleX, scaleY);
|
|
|
|
|
|
//
|
|
|
|
|
|
// // Calculate scaled dimensions (ensure even for H.264)
|
|
|
|
|
|
// int scaledWidth = static_cast<int>(std::round(firstImage.cols * scale));
|
|
|
|
|
|
// int scaledHeight = static_cast<int>(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<std::tuple<std::string, int>> 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<int>(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<double>(frame) * (numImages - 1)) /
|
|
|
|
|
|
// (totalFrames - 1);
|
|
|
|
|
|
// const int imageIdx = static_cast<int>(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<ANSCENTER::ANSOPENCV>();
|
|
|
|
|
|
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<ANSCENTER::ANSOPENCV> 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<int>(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<int>(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<int>(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<cv::Rect> 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<cv::Rect> 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<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
|
|
|
|
|
|
height = static_cast<int>(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<std::mutex> lock(imageMutex); // Automatically locks and unlocks
|
|
|
|
|
|
ANSCENTER::ANSOPENCV::InitCameraNetwork();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (...) { }
|
|
|
|
|
|
}
|
|
|
|
|
|
extern "C" __declspec(dllexport) void ANSCV_FreeCameraResource() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
std::lock_guard<std::mutex> 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<std::mutex> lock(imageMutex); // Automatically locks and unlocks
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<uchar> 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<std::mutex> lock(imageMutex);
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::mutex> lock(imageMutex);
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<std::timed_mutex> 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<std::timed_mutex> 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<std::timed_mutex> 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::milliseconds>(
|
|
|
|
|
|
// 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::milliseconds>(
|
|
|
|
|
|
// 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::milliseconds>(
|
|
|
|
|
|
// 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::milliseconds>(
|
|
|
|
|
|
// 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::milliseconds>(
|
|
|
|
|
|
// 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<int32_t>(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<std::timed_mutex> 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<int32_t>(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<std::timed_mutex> 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<std::mutex> 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<double>(originalWidth) / originalImageSize;
|
|
|
|
|
|
targetWidth = static_cast<int>(width * scale);
|
|
|
|
|
|
}
|
|
|
|
|
|
// Skip resizing if target size is greater or equal to original
|
|
|
|
|
|
if (targetWidth < originalWidth) {
|
|
|
|
|
|
|
|
|
|
|
|
// Maintain aspect ratio
|
|
|
|
|
|
int targetHeight = static_cast<int>(std::round(targetWidth * static_cast<double>(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<std::timed_mutex> 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<std::mutex> 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<std::mutex> lock(imageMutex);
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<int32_t>(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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<std::mutex> 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<uchar> buffer(inputImage, inputImage + bufferLength);
|
|
|
|
|
|
cv::Mat decodedImage = cv::imdecode(buffer, cv::IMREAD_COLOR);
|
|
|
|
|
|
// Allocate image safely
|
|
|
|
|
|
{
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::mutex> 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<std::mutex> lock(imageMutex); // Automatically locks and unlocks
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<std::mutex> 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<std::mutex> lock(imageMutex); // Lock only during shared resource write
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::mutex> lock(imageMutex); // Lock only during shared resource modification
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::mutex> lock(imageMutex); // Lock only during shared resource modification
|
|
|
|
|
|
std::unique_lock<std::timed_mutex> 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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<std::mutex> 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<cv::Rect> 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<std::timed_mutex> 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<std::mutex> 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<cv::Rect> 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<std::timed_mutex> 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<std::mutex> 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<cv::Rect> 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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<std::mutex> 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<std::timed_mutex> 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<ANSCENTER::ANSOPENCV*>(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<int>(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<ANSCENTER::ANSOPENCV*>(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<int>(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<ANSCENTER::ANSOPENCV*>(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<int>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
|
|
|
|
|
|
height = static_cast<int>(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<ANSCENTER::ANSOPENCV*>(handleVal);
|
|
|
|
|
|
if (!h) return -1;
|
|
|
|
|
|
try {
|
|
|
|
|
|
cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR);
|
|
|
|
|
|
std::vector<cv::Rect> 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<ANSCENTER::ANSOPENCV*>(handleVal);
|
|
|
|
|
|
if (!h) return -1;
|
|
|
|
|
|
try {
|
|
|
|
|
|
cv::Mat inputFrame = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, inputImage), cv::IMREAD_COLOR);
|
|
|
|
|
|
std::vector<cv::Rect> 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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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<ANSCENTER::ANSOPENCV*>(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; }
|
2026-04-12 21:55:09 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── 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<unsigned char*>(info.imageStart) + y * dstStride,
|
|
|
|
|
|
mat.ptr<unsigned char>(y), width * sizeof(unsigned char));
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case CV_16UC1: {
|
|
|
|
|
|
// 16-bit grayscale -> IMAQ_IMAGE_U16
|
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
|
memcpy(static_cast<unsigned char*>(info.imageStart) + y * dstStride * sizeof(unsigned short),
|
|
|
|
|
|
mat.ptr<unsigned short>(y), width * sizeof(unsigned short));
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case CV_8UC3: {
|
|
|
|
|
|
// BGR -> IMAQ_IMAGE_RGB (BGRA with alpha=0)
|
|
|
|
|
|
cv::Mat bgra;
|
|
|
|
|
|
cv::cvtColor(mat, bgra, cv::COLOR_BGR2BGRA);
|
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
|
memcpy(static_cast<unsigned char*>(info.imageStart) + y * dstStride * sizeof(RGBValue),
|
|
|
|
|
|
bgra.ptr<unsigned char>(y), width * sizeof(RGBValue));
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case CV_8UC4: {
|
|
|
|
|
|
// BGRA -> IMAQ_IMAGE_RGB directly
|
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
|
memcpy(static_cast<unsigned char*>(info.imageStart) + y * dstStride * sizeof(RGBValue),
|
|
|
|
|
|
mat.ptr<unsigned char>(y), width * sizeof(RGBValue));
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case CV_32FC1: {
|
|
|
|
|
|
// Float -> IMAQ_IMAGE_SGL
|
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
|
memcpy(static_cast<unsigned char*>(info.imageStart) + y * dstStride * sizeof(float),
|
|
|
|
|
|
mat.ptr<float>(y), width * sizeof(float));
|
|
|
|
|
|
}
|
|
|
|
|
|
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; }
|
2026-03-28 16:54:11 +11:00
|
|
|
|
}
|