#include "ANSOVSEG.h" namespace ANSCENTER { bool ANSOVSEG::OptimizeModel(bool fp16, std::string& optimizedModelFolder) { std::lock_guard 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 lock(_mutex); 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 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 lock(_mutex); 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 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 lock(_mutex); 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 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 ANSOVSEG::RunInference(const cv::Mat& input) { return RunInference(input, "CustomCam"); } std::vector ANSOVSEG::RunInference(const cv::Mat& input,const std::string& camera_id) { std::lock_guard lock(_mutex); 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(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(output0_shape[2]); // 8400 const int dimensions = static_cast(output0_shape[1]); // 116 float* data = output0.data(); // Proto masks [32, 25600] cv::Mat proto(32, 25600, CV_32F, output1.data()); // Parse detections (NO TRANSPOSE!) std::vector class_ids; std::vector class_scores; std::vector boxes; std::vector 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((cx - 0.5f * w) * scale), static_cast((cy - 0.5f * h) * scale), static_cast(w * scale), static_cast(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(0, k) = data[(84 + k) * rows + i]; } mask_confs.push_back(mask_conf); } } } if (boxes.empty()) { return {}; } // Apply NMS std::vector indices; cv::dnn::NMSBoxes(boxes, class_scores, _modelConfig.detectionScoreThreshold, _modelConfig.modelMNSThreshold, indices); // Process final detections std::vector outputs; outputs.reserve(indices.size()); const int classNameSize = static_cast(_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(x1 / scale * 0.25); int my1 = static_cast(y1 / scale * 0.25); int mx2 = static_cast(x2 / scale * 0.25); int my2 = static_cast(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> 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 normalizedPolygon; normalizedPolygon.reserve(contours[max_contour_idx].size()); for (const auto& pt : contours[max_contour_idx]) { normalizedPolygon.emplace_back( static_cast(pt.x), static_cast(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 available_devices = core.get_available_devices(); // Define device priority: NPU > GPU > CPU std::vector priority_devices = { "NPU", "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::vectorANSOVSEG::PostProcessing(const std::string& camera_id) { try { std::vector class_list; std::vector confidence_list; std::vector box_list; float* detections = inference_request_.get_output_tensor().data(); 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(0, i); const float y = detection_outputs.at(1, i); const float w = detection_outputs.at(2, i); const float h = detection_outputs.at(3, i); cv::Rect box; box.x = static_cast(x); box.y = static_cast(y); box.width = static_cast(w); box.height = static_cast(h); box_list.push_back(box); } } std::vector NMS_result; cv::dnn::NMSBoxes(box_list, confidence_list, _modelConfig.modelConfThreshold, _modelConfig.modelMNSThreshold, NMS_result); std::vector 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 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 ANSOVSEG::RunInference(const cv::Mat& input, const std::string& camera_id) { // std::lock_guard lock(_mutex); // std::vector 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(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()); // cv::Mat proto(32, 25600, CV_32F, output1.data()); //[32,25600] // transpose(output_buffer, output_buffer); //[8400,116] // // std::vector class_ids; // std::vector class_scores; // std::vector boxes; // std::vector 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(i, 0); // float cy = output_buffer.at(i, 1); // float w = output_buffer.at(i, 2); // float h = output_buffer.at(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 indices; // cv::dnn::NMSBoxes(boxes, class_scores, _modelConfig.detectionScoreThreshold, _modelConfig.modelMNSThreshold, indices); // int classNameSize = static_cast(_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(0, col) = sigmoid_function(m.at(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(r, c); // rm.at(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> contours; // cv::findContours(bbox_mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // // Normalize the polygon // std::vector normalizedPolygon; // for (const auto& pt : contours[0]) { // normalizedPolygon.emplace_back(static_cast(pt.x) / 1.0, static_cast(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; // } //}