#include "ANSOVSEG.h"
#include "OpenVINODeviceConfig.h"
namespace ANSCENTER {
	bool ANSOVSEG::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		if (!ANSODBase::OptimizeModel(fp16, optimizedModelFolder)) {
			return false;
		}
		if (FileExist(_modelFilePath)) {
			std::string modelName = GetFileNameWithoutExtension(_modelFilePath);
			std::string binaryModelName = modelName + ".bin";
			std::string modelFolder = GetParentFolder(_modelFilePath);
			std::string optimizedModelPath = CreateFilePath(modelFolder, binaryModelName);
			if (FileExist(optimizedModelPath)) {
				this->_logger.LogDebug("ANSOVSEG::OptimizeModel", "This model is optimized. No need other optimization.", __FILE__, __LINE__);
				optimizedModelFolder = modelFolder;
				return true;
			}
			else {
				this->_logger.LogFatal("ANSOVSEG::OptimizeModel", "This model can not be optimized.", __FILE__, __LINE__);
				optimizedModelFolder = modelFolder;
				return false;
			}
		}
		else {
			this->_logger.LogFatal("ANSOVSEG::OptimizeModel", "This model is not exist. Please check the model path again.", __FILE__, __LINE__);
			optimizedModelFolder = "";
			return false;
		}
	}
	bool ANSOVSEG::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		ModelLoadingGuard mlg(_modelLoading);
		try {
			bool result = ANSODBase::LoadModel(modelZipFilePath, modelZipPassword);
			if (!result) return false;
			// 0. Check if the configuration file exist
			if (FileExist(_modelConfigFile)) {
				ModelType modelType;
				std::vector<int> inputShape;
				_classes = ANSUtilityHelper::GetConfigFileContent(_modelConfigFile, modelType, inputShape);
				if (inputShape.size() == 2) {
					if (inputShape[0] > 0)_modelConfig.inpHeight = inputShape[0];
					if (inputShape[1] > 0)_modelConfig.inpWidth = inputShape[1];
				}
			}
			else {// This is old version of model zip file
				std::string onnxfile = CreateFilePath(_modelFolder, "train_last.xml");//yolov8n.xml
				if (std::filesystem::exists(onnxfile)) {
					_modelFilePath = onnxfile;
					_classFilePath = CreateFilePath(_modelFolder, "classes.names");
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Loading OpenVINO weight", _modelFilePath, __FILE__, __LINE__);
				}
				else {
					this->_logger.LogError("ANSOVSEG::Initialize.  Model file is not exist", _modelFilePath, __FILE__, __LINE__);
					return false;
				}
				std::ifstream isValidFileName(_classFilePath);
				if (!isValidFileName)
				{
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Load classes from string", _classFilePath, __FILE__, __LINE__);
					LoadClassesFromString();
				}
				else {
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Load classes from file", _classFilePath, __FILE__, __LINE__);
					LoadClassesFromFile();
				}
			}

			// Load Model from Here
			InitialModel();
			_isInitialized = true;
			return true;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("OPENVINOCL::LoadModel", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	bool ANSOVSEG::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		ModelLoadingGuard mlg(_modelLoading);
		try {
			bool result = ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap);
			if (!result) return false;
			std::string _modelName = modelName;
			if (_modelName.empty()) {
				_modelName = "train_last";
			}
			std::string modelFullName = _modelName + ".xml";
			_modelConfig = modelConfig;
			_modelConfig.detectionType = ANSCENTER::DetectionType::SEGMENTATION;
			_modelConfig.modelType = ModelType::OPENVINO;
			_modelConfig.inpHeight = 640;
			_modelConfig.inpWidth = 640;
			if (_modelConfig.modelMNSThreshold < 0.2)
				_modelConfig.modelMNSThreshold = 0.5;
			if (_modelConfig.modelConfThreshold < 0.2)
				_modelConfig.modelConfThreshold = 0.5;
			// 0. Check if the configuration file exist
			if (FileExist(_modelConfigFile)) {
				ModelType modelType;
				std::vector<int> inputShape;
				_classes = ANSUtilityHelper::GetConfigFileContent(_modelConfigFile, modelType, inputShape);
				if (inputShape.size() == 2) {
					if (inputShape[0] > 0)_modelConfig.inpHeight = inputShape[0];
					if (inputShape[1] > 0)_modelConfig.inpWidth = inputShape[1];
				}
			}
			else {// This is old version of model zip file
				std::string onnxfile = CreateFilePath(_modelFolder, modelFullName);//yolov8n.xml
				if (std::filesystem::exists(onnxfile)) {
					_modelFilePath = onnxfile;
					_classFilePath = CreateFilePath(_modelFolder, className);
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Loading OpenVINO weight", _modelFilePath, __FILE__, __LINE__);
				}
				else {
					this->_logger.LogError("ANSOVSEG::Initialize.  Model file is not exist", _modelFilePath, __FILE__, __LINE__);
					return false;
				}
				std::ifstream isValidFileName(_classFilePath);
				if (!isValidFileName)
				{
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Load classes from string", _classFilePath, __FILE__, __LINE__);
					LoadClassesFromString();
				}
				else {
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Load classes from file", _classFilePath, __FILE__, __LINE__);
					LoadClassesFromFile();
				}
			}
			// 1. Load labelMap and engine
			labelMap.clear();
			if (!_classes.empty())
				labelMap = VectorToCommaSeparatedString(_classes);

			// Load Model from Here
			InitialModel();
			_isInitialized = true;
			return true;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("OPENVINOCL::LoadModel", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	bool ANSOVSEG::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		ModelLoadingGuard mlg(_modelLoading);
		try {
			std::string openVINOVersion = ov::get_openvino_version().buildNumber;
			this->_logger.LogDebug("ANSOVSEG::Initialize. OpenVINO version", openVINOVersion, __FILE__, __LINE__);
			bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
			if (!result) return false;
			// Parsing for YOLO only here
			_modelConfig = modelConfig;
			_modelConfig.detectionType = ANSCENTER::DetectionType::SEGMENTATION;
			_modelConfig.modelType = ModelType::OPENVINO;
			_modelConfig.inpHeight = 640;
			_modelConfig.inpWidth = 640;
			if (_modelConfig.modelMNSThreshold < 0.2)
				_modelConfig.modelMNSThreshold = 0.5;
			if (_modelConfig.modelConfThreshold < 0.2)
				_modelConfig.modelConfThreshold = 0.5;
			// 0. Check if the configuration file exist
			if (FileExist(_modelConfigFile)) {
				ModelType modelType;
				std::vector<int> inputShape;
				_classes = ANSUtilityHelper::GetConfigFileContent(_modelConfigFile, modelType, inputShape);
				if (inputShape.size() == 2) {
					if (inputShape[0] > 0)_modelConfig.inpHeight = inputShape[0];
					if (inputShape[1] > 0)_modelConfig.inpWidth = inputShape[1];
				}
			}
			else {// This is old version of model zip file
				std::string onnxfile = CreateFilePath(_modelFolder, "train_last.xml");//yolov8n.xml
				if (std::filesystem::exists(onnxfile)) {
					_modelFilePath = onnxfile;
					_classFilePath = CreateFilePath(_modelFolder, "classes.names");
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Loading OpenVINO weight", _modelFilePath, __FILE__, __LINE__);
				}
				else {
					this->_logger.LogError("ANSOVSEG::Initialize.  Model file is not exist", _modelFilePath, __FILE__, __LINE__);
					return false;
				}
				std::ifstream isValidFileName(_classFilePath);
				if (!isValidFileName)
				{
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Load classes from string", _classFilePath, __FILE__, __LINE__);
					LoadClassesFromString();
				}
				else {
					this->_logger.LogDebug("ANSOVSEG::Initialize.  Load classes from file", _classFilePath, __FILE__, __LINE__);
					LoadClassesFromFile();
				}
			}
			// 1. Load labelMap and engine
			labelMap.clear();
			if (!_classes.empty())
				labelMap = VectorToCommaSeparatedString(_classes);

			// Load Model from Here
			InitialModel();
			_isInitialized = true;
			return true;

		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSOVSEG::Initialize", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	std::vector<Object> ANSOVSEG::RunInference(const cv::Mat& input) {
		return RunInference(input, "CustomCam");
	}
	std::vector<Object> ANSOVSEG::RunInference(const cv::Mat& input,const std::string& camera_id)
	{
		if (!PreInferenceCheck("ANSOVSEG::RunInference")) return {};

		try {
			// Validation
			if (!_licenseValid) {
				_logger.LogError("ANSOVSEG::RunInference", "Invalid License",
					__FILE__, __LINE__);
				return {};
			}

			if (!_isInitialized) {
				_logger.LogError("ANSOVSEG::RunInference",
					"Model is not initialized", __FILE__, __LINE__);
				return {};
			}

			if (input.empty() || input.cols < 10 || input.rows < 10) {
				return {};
			}

			// Preprocessing
			cv::Mat letterbox_img = PreProcessing(input);
			if (letterbox_img.empty()) {
				_logger.LogError("ANSOVSEG::RunInference", "PreProcessing failed",
					__FILE__, __LINE__);
				return {};
			}

			constexpr int imageSize = 640;
			const float scale = static_cast<float>(letterbox_img.rows) / imageSize;

			cv::Mat blob = cv::dnn::blobFromImage(letterbox_img, 1.0 / 255.0,
				cv::Size(imageSize, imageSize),
				cv::Scalar(), true);

			// Inference
			auto input_port = compiled_model_.input();
			ov::Tensor input_tensor(input_port.get_element_type(),
				input_port.get_shape(), blob.ptr(0));
			inference_request_.set_input_tensor(input_tensor);
			inference_request_.infer();

			// Get outputs
			auto output0 = inference_request_.get_output_tensor(0);
			auto output1 = inference_request_.get_output_tensor(1);
			auto output0_shape = output0.get_shape();

			const int rows = static_cast<int>(output0_shape[2]);      // 8400
			const int dimensions = static_cast<int>(output0_shape[1]); // 116
			float* data = output0.data<float>();

			// Proto masks [32, 25600]
			cv::Mat proto(32, 25600, CV_32F, output1.data<float>());

			// Parse detections (NO TRANSPOSE!)
			std::vector<int> class_ids;
			std::vector<float> class_scores;
			std::vector<cv::Rect> boxes;
			std::vector<cv::Mat> mask_confs;

			// Pre-allocate
			class_ids.reserve(rows / 10);
			class_scores.reserve(rows / 10);
			boxes.reserve(rows / 10);
			mask_confs.reserve(rows / 10);

			const cv::Rect imageBounds(0, 0, input.cols, input.rows);

			// Process detections without transpose
			for (int i = 0; i < rows; i++) {
				// Find max class score (columns 4-83, total 80 classes)
				float max_score = -1.0f;
				int max_class_id = 0;

				for (int j = 4; j < 84; j++) {
					const float score = data[j * rows + i];
					if (score > max_score) {
						max_score = score;
						max_class_id = j - 4;
					}
				}

				if (max_score > _modelConfig.detectionScoreThreshold) {
					// Extract box coordinates
					const float cx = data[0 * rows + i];
					const float cy = data[1 * rows + i];
					const float w = data[2 * rows + i];
					const float h = data[3 * rows + i];

					// Convert to pixel coordinates
					cv::Rect box(
						static_cast<int>((cx - 0.5f * w) * scale),
						static_cast<int>((cy - 0.5f * h) * scale),
						static_cast<int>(w * scale),
						static_cast<int>(h * scale)
					);

					box &= imageBounds;  // Clamp to bounds

					if (box.area() > 0) {
						class_ids.push_back(max_class_id);
						class_scores.push_back(max_score);
						boxes.push_back(box);

						// Extract mask coefficients (columns 84-115, 32 values)
						cv::Mat mask_conf(1, 32, CV_32F);
						for (int k = 0; k < 32; k++) {
							mask_conf.at<float>(0, k) = data[(84 + k) * rows + i];
						}
						mask_confs.push_back(mask_conf);
					}
				}
			}

			if (boxes.empty()) {
				return {};
			}

			// Apply NMS
			std::vector<int> indices;
			cv::dnn::NMSBoxes(boxes, class_scores,
				_modelConfig.detectionScoreThreshold,
				_modelConfig.modelMNSThreshold, indices);

			// Process final detections
			std::vector<Object> outputs;
			outputs.reserve(indices.size());

			const int classNameSize = static_cast<int>(_classes.size());

			for (int idx : indices) {
				// Generate mask for this detection
				cv::Mat m = mask_confs[idx] * proto;  // [1, 25600]

				// Apply sigmoid vectorized
				cv::exp(-m, m);
				m = 1.0 / (1.0 + m);

				// Reshape to 160x160
				cv::Mat m1 = m.reshape(1, 160);

				// Get ROI coordinates
				const cv::Rect& box = boxes[idx];
				int x1 = std::max(0, box.x);
				int y1 = std::max(0, box.y);
				int x2 = std::min(input.cols, box.br().x);
				int y2 = std::min(input.rows, box.br().y);

				if (x2 <= x1 || y2 <= y1) continue;  // Invalid box

				// Calculate mask ROI (in 160x160 space, 0.25 scale)
				int mx1 = static_cast<int>(x1 / scale * 0.25);
				int my1 = static_cast<int>(y1 / scale * 0.25);
				int mx2 = static_cast<int>(x2 / scale * 0.25);
				int my2 = static_cast<int>(y2 / scale * 0.25);

				// Clamp mask coordinates
				mx1 = std::clamp(mx1, 0, 160);
				my1 = std::clamp(my1, 0, 160);
				mx2 = std::clamp(mx2, 0, 160);
				my2 = std::clamp(my2, 0, 160);

				if (mx2 <= mx1 || my2 <= my1) continue;  // Invalid mask ROI

				// Extract and resize mask
				cv::Mat mask_roi = m1(cv::Range(my1, my2), cv::Range(mx1, mx2));
				cv::Mat rm;
				cv::resize(mask_roi, rm, cv::Size(x2 - x1, y2 - y1));

				// Threshold mask (vectorized)
				cv::threshold(rm, rm, 0.5, 1.0, cv::THRESH_BINARY);

				// Convert to uint8
				cv::Mat det_mask;
				rm.convertTo(det_mask, CV_8UC1, 255.0);

				// Create full-size mask
				cv::Mat bbox_mask = cv::Mat::zeros(input.size(), CV_8UC1);
				det_mask.copyTo(bbox_mask(cv::Rect(x1, y1, x2 - x1, y2 - y1)));

				// Extract contours
				std::vector<std::vector<cv::Point>> contours;
				cv::findContours(bbox_mask, contours, cv::RETR_EXTERNAL,
					cv::CHAIN_APPROX_SIMPLE);

				if (contours.empty()) continue;  // No contour found

				// Find largest contour
				size_t max_contour_idx = 0;
				double max_area = 0;
				for (size_t i = 0; i < contours.size(); i++) {
					double area = cv::contourArea(contours[i]);
					if (area > max_area) {
						max_area = area;
						max_contour_idx = i;
					}
				}

				// Normalize polygon
				std::vector<cv::Point2f> normalizedPolygon;
				normalizedPolygon.reserve(contours[max_contour_idx].size());
				for (const auto& pt : contours[max_contour_idx]) {
					normalizedPolygon.emplace_back(
						static_cast<float>(pt.x),
						static_cast<float>(pt.y)
					);
				}

				// Build result object
				Object result;
				result.classId = class_ids[idx];
				result.confidence = class_scores[idx];
				result.box = box;
				result.mask = det_mask.clone();  // Only clone final mask
				result.polygon = std::move(normalizedPolygon);
				result.cameraId = camera_id;

				// Set class name
				if (!_classes.empty()) {
					result.className = (result.classId < classNameSize)
						? _classes[result.classId]
						: _classes.back();
				}
				else {
					result.className = "Unknown";
				}

				outputs.push_back(std::move(result));
			}

			if (_trackerEnabled) {
				outputs = ApplyTracking(outputs, camera_id);
				if (_stabilizationEnabled) outputs = StabilizeDetections(outputs, camera_id);
			}
			return outputs;

		}
		catch (const std::exception& e) {
			_logger.LogFatal("ANSOVSEG::RunInference", e.what(), __FILE__, __LINE__);
			return {};
		}
	}
	ANSOVSEG::~ANSOVSEG() {
		try {
			// We will do it all togther when exit ANSVIS
			if (FolderExist(_modelFolder)) {
				if (!DeleteFolder(_modelFolder)) {
					this->_logger.LogError("ANSOVSEG::~ANSOVSEG()", "Failed to release OPENVINO Models", __FILE__, __LINE__);
				}
			}
		}
		catch (std::exception& e) {
			std::cout << "ANSOVSEG::~ANSOVSEG()" << e.what() << std::endl;
		}
	}
	bool ANSOVSEG::Destroy() {
		try {
			// We will do it all togther when exit ANSVIS
			if (FolderExist(_modelFolder)) {
				if (!DeleteFolder(_modelFolder)) {
					this->_logger.LogError("ANSOVSEG::Destroy()", "Failed to release OPENVINO Models", __FILE__, __LINE__);
				}
			}
			return true;
		}
		catch (std::exception& e) {
			std::cout << "ANSOVSEG::Destroy()" << e.what() << std::endl;
			return false;
		}

	}

	//private
	void ANSOVSEG::InitialModel() {
		try {
			// Step 1: Initialize OpenVINO Runtime Core
			ov::Core core;
			// Step 2: Get Available Devices and Log
			std::vector<std::string> available_devices = core.get_available_devices();
			// Define device priority: NPU > GPU > CPU. NPU gated by
			// OPENVINO_ENABLE_NPU — see OpenVINODeviceConfig.h.
			std::vector<std::string> priority_devices;
			if (IsOpenVINONpuEnabled()) priority_devices.push_back("NPU");
			priority_devices.push_back("GPU");
			bool device_found = false;

			// Iterate over prioritized devices
			for (const auto& device : priority_devices) {
				if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) {
					if (device == "NPU") {
						core.set_property("NPU", ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY));
						core.set_property("GPU", ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY));
						compiled_model_ = core.compile_model(_modelFilePath, "AUTO:NPU,GPU");
					}
					else {
						// Configure and compile for individual device
						core.set_property(device, ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY));
						compiled_model_ = core.compile_model(_modelFilePath, device);
					}
					device_found = true;
					break;
				}
			}
			// Fallback: Default to CPU if no devices found
			if (!device_found) {
				core.set_property("CPU", ov::hint::performance_mode(ov::hint::PerformanceMode::LATENCY));
				compiled_model_ = core.compile_model(_modelFilePath, "CPU");
			}
			// Step 3: Create Inference Request
			inference_request_ = compiled_model_.create_infer_request();

		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSOVSEG::InitialModel", e.what(), __FILE__, __LINE__);
		}

	}
	cv::Mat ANSOVSEG::PreProcessing(const cv::Mat& source) {
		try {
			if (source.empty()) {
				this->_logger.LogFatal("ANSOVSEG::PreProcessing", "Empty image provided", __FILE__, __LINE__);
				return cv::Mat();
			}

			// Convert grayscale to 3-channel BGR if needed
			cv::Mat processedImage;
			if (source.channels() == 1) {
				cv::cvtColor(source, processedImage, cv::COLOR_GRAY2BGR);
			}
			else {
				processedImage = source;
			}

			int col = processedImage.cols;
			int row = processedImage.rows;
			int maxSize = std::max(col, row);

			// Create a square padded image with a black background
			cv::Mat result = cv::Mat::zeros(maxSize, maxSize, CV_8UC3);

			// Copy the original image to the top-left corner of the square matrix
			processedImage.copyTo(result(cv::Rect(0, 0, col, row)));

			return result;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("ANSOVSEG::PreProcessing", e.what(), __FILE__, __LINE__);
			return cv::Mat();
		}
	}
	cv::Rect ANSOVSEG::GetBoundingBox(const cv::Rect& src) {
		cv::Rect box = src;
		box.x = (box.x - 0.5 * box.width) * factor_.x;
		box.y = (box.y - 0.5 * box.height) * factor_.y;
		box.width *= factor_.x;
		box.height *= factor_.y;
		return box;
	}
	std::vector<Object>ANSOVSEG::PostProcessing(const std::string& camera_id) {
		try {
			std::vector<int> class_list;
			std::vector<float> confidence_list;
			std::vector<cv::Rect> box_list;

			float* detections = inference_request_.get_output_tensor().data<float>();
			const cv::Mat detection_outputs(model_output_shape_, CV_32F, (float*)detections);

			for (int i = 0; i < detection_outputs.cols; ++i) {
				const cv::Mat classes_scores = detection_outputs.col(i).rowRange(4, detection_outputs.rows);

				cv::Point class_id;
				double score;

				cv::minMaxLoc(classes_scores, nullptr, &score, nullptr, &class_id);

				if (score >= _modelConfig.detectionScoreThreshold) {
					class_list.push_back(class_id.y);
					confidence_list.push_back(score);

					const float x = detection_outputs.at<float>(0, i);
					const float y = detection_outputs.at<float>(1, i);
					const float w = detection_outputs.at<float>(2, i);
					const float h = detection_outputs.at<float>(3, i);

					cv::Rect box;

					box.x = static_cast<int>(x);
					box.y = static_cast<int>(y);
					box.width = static_cast<int>(w);
					box.height = static_cast<int>(h);

					box_list.push_back(box);
				}
			}

			std::vector<int> NMS_result;
			cv::dnn::NMSBoxes(box_list, confidence_list, _modelConfig.modelConfThreshold, _modelConfig.modelMNSThreshold, NMS_result);

			std::vector<Object> output;
			for (int i = 0; i < NMS_result.size(); i++)
			{
				Object result;
				int id = NMS_result[i];
				result.classId = class_list[id];
				result.confidence = confidence_list[id];
				result.box = GetBoundingBox(box_list[id]);
				result.cameraId = camera_id;
				output.push_back(result);
			}
			//EnqueueDetection(output,camera_id);
			return output;
		}

		catch (const std::exception& e) {
			std::vector<Object> result;
			result.clear();
			this->_logger.LogError("ANSOVSEG::PostprocessImage", e.what(), __FILE__, __LINE__);
			return result;
		}
	}
	float ANSOVSEG::sigmoid_function(float a) {
		float b = 1. / (1. + exp(-a));
		return b;
	}

}

// Only work for Yolov8 for now, extract polygons does not work as expected
	//std::vector<Object> ANSOVSEG::RunInference(const cv::Mat& input, const std::string& camera_id) {
	//	std::lock_guard<std::recursive_mutex> lock(_mutex);
	//	std::vector<Object> outputs;
	//	outputs.clear();
	//	if (!_licenseValid) {
	//		this->_logger.LogError("ANSOVSEG::RunInference", "Invalid License", __FILE__, __LINE__);
	//		return outputs;
	//	}
	//	if (!_isInitialized) {
	//		this->_logger.LogError("ANSOVSEG::RunInference", "Model is not initialized", __FILE__, __LINE__);
	//		return outputs;
	//	}
	//	try {			
	//		// Step 0: Prepare input
	//		if (input.empty()) return outputs;
	//		if ((input.cols < 10) || (input.rows < 10)) return outputs;
	//		cv::Mat letterbox_img = PreProcessing(input);
	//		if (letterbox_img.empty()) {
	//			_logger.LogError("OPENVINOOD::RunInference", "PreProcessing failed", __FILE__, __LINE__);
	//			return outputs;
	//		}
	//		int imageSize = 640;
	//		int maxImageSize = std::max(letterbox_img.cols, letterbox_img.rows);
	//		//if (maxImageSize < imageSize)imageSize = maxImageSize;
	//		float scale = static_cast<float>(letterbox_img.rows) / imageSize;
	//		cv::Mat blob = cv::dnn::blobFromImage(letterbox_img, 1.0 / 255.0,cv::Size(imageSize, imageSize),cv::Scalar(), true);

	//		// Step 1: Feed blob to the network
	//		auto input_port = compiled_model_.input();
	//		ov::Tensor input_tensor(input_port.get_element_type(), input_port.get_shape(), blob.ptr(0));
	//		inference_request_.set_input_tensor(input_tensor);
	//		inference_request_.infer();

	//		// Step 2: Get output
	//		auto output0 = inference_request_.get_output_tensor(0); //output0
	//		auto output1 = inference_request_.get_output_tensor(1); //otuput1
	//		auto output0_shape = output0.get_shape();
	//		auto output1_shape = output1.get_shape();
	//		
	//		// Step 3: Postprocess the inference result
	//		cv::Mat output_buffer(output0_shape[1], output0_shape[2], CV_32F, output0.data<float>());
	//		cv::Mat proto(32, 25600, CV_32F, output1.data<float>()); //[32,25600]
	//		transpose(output_buffer, output_buffer); //[8400,116]
	//		
	//		std::vector<int> class_ids;
	//		std::vector<float> class_scores;
	//		std::vector<cv::Rect> boxes;
	//		std::vector<cv::Mat> mask_confs;
	//		// Figure out the bbox, class_id and class_score
	//		for (int i = 0; i < output_buffer.rows; i++) {
	//			cv::Mat classes_scores = output_buffer.row(i).colRange(4, 84);
	//			cv::Point class_id;
	//			double maxClassScore;
	//			minMaxLoc(classes_scores, 0, &maxClassScore, 0, &class_id);
	//			if (maxClassScore > _modelConfig.detectionScoreThreshold) {
	//				class_scores.push_back(maxClassScore);
	//				class_ids.push_back(class_id.x);
	//				float cx = output_buffer.at<float>(i, 0);
	//				float cy = output_buffer.at<float>(i, 1);
	//				float w = output_buffer.at<float>(i, 2);
	//				float h = output_buffer.at<float>(i, 3);

	//				int left = int((cx - 0.5 * w) * scale);
	//				int top = int((cy - 0.5 * h) * scale);
	//				int width = int(w * scale);
	//				int height = int(h * scale);

	//				cv::Mat mask_conf = output_buffer.row(i).colRange(84, 116);
	//				mask_confs.push_back(mask_conf);
	//				boxes.push_back(cv::Rect(left, top, width, height));
	//			}
	//		}
	//		//NMS
	//		std::vector<int> indices;
	//		cv::dnn::NMSBoxes(boxes, class_scores, _modelConfig.detectionScoreThreshold, _modelConfig.modelMNSThreshold, indices);
	//		int classNameSize = static_cast<int>(_classes.size());
	//		for (size_t i = 0; i < indices.size(); i++) {
	//			// Visualize the objects
	//			if (class_scores[i] >= _modelConfig.detectionScoreThreshold) {
	//				int index = indices[i];			
	//				// Visualize the Masks
	//				cv::Mat m = mask_confs[i] * proto;
	//				for (int col = 0; col < m.cols; col++) {
	//					m.at<float>(0, col) = sigmoid_function(m.at<float>(0, col));
	//				}
	//				cv::Mat m1 = m.reshape(1, 160); // 1x25600 -> 160x160
	//				int x1 = std::max(0, boxes[index].x);
	//				int y1 = std::max(0, boxes[index].y);
	//				int x2 = std::min(input.cols, boxes[index].br().x);
	//				int y2 = std::min(input.rows, boxes[index].br().y);

	//				int mx1 = int(x1 / scale * 0.25);
	//				int my1 = int(y1 / scale * 0.25);
	//				int mx2 = int(x2 / scale * 0.25);
	//				int my2 = int(y2 / scale * 0.25);

	//				cv::Mat mask_roi = m1(cv::Range(my1, my2), cv::Range(mx1, mx2));
	//				cv::Mat rm, det_mask;
	//				cv::resize(mask_roi, rm, cv::Size(x2 - x1, y2 - y1));

	//				for (int r = 0; r < rm.rows; r++) {
	//					for (int c = 0; c < rm.cols; c++) {
	//						float pv = rm.at<float>(r, c);
	//						rm.at<float>(r, c) = (pv > 0.5) ? 1.0 : 0.0;
	//					}
	//				}
	//				rm = rm * 255; // Scale mask to 255 for visualization
	//				rm.convertTo(det_mask, CV_8UC1);
	//				// Extract contours from the bounding box region
	//				cv::Mat bbox_mask = cv::Mat::zeros(input.size(), CV_8UC1);
	//				det_mask.copyTo(bbox_mask(cv::Rect(x1, y1, x2 - x1, y2 - y1)));

	//				std::vector<std::vector<cv::Point>> contours;
	//				cv::findContours(bbox_mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

	//				// Normalize the polygon
	//				std::vector<cv::Point2f> normalizedPolygon;
	//				for (const auto& pt : contours[0]) {
	//					normalizedPolygon.emplace_back(static_cast<float>(pt.x) / 1.0, static_cast<float>(pt.y) / 1.0);
	//				}
	//				// add to outputs
	//				Object result;
	//				int class_id = class_ids[i];
	//				result.classId = class_id;
	//				if (!_classes.empty()) {
	//					if (result.classId < classNameSize) {
	//						result.className = _classes[result.classId];
	//					}
	//					else {
	//						result.className = _classes[classNameSize - 1]; // Use last valid class name if out of range
	//					}
	//				}
	//				else {
	//					result.className = "Unknown"; // Fallback if _classes is empty
	//				}

	//				result.confidence = class_scores[i];
	//				result.box = boxes[i];
	//				result.mask = det_mask(cv::Rect(x1, y1, x2 - x1, y2 - y1)).clone();
	//				result.polygon = normalizedPolygon;
	//				result.cameraId = camera_id;
	//				outputs.push_back(result);
	//
	//			}				
	//		}
	//		return outputs;
	//	}
	//	catch (std::exception& e) {
	//		this->_logger.LogFatal("ANSOVSEG::RunInference", e.what(), __FILE__, __LINE__);

	//		return outputs;
	//	}
	//}
