diff --git a/opencv_onnx.cpp b/opencv_onnx.cpp index fd7769c..c53ab36 100644 --- a/opencv_onnx.cpp +++ b/opencv_onnx.cpp @@ -1,36 +1,34 @@ -// -// Created by zjc on 24-11-19. -// #include #include #include -// 参数 -const float CONFIDENCE_THRESHOLD = 0.2; // 置信度阈值 -const float NMS_THRESHOLD = 0.2; // 非极大值抑制阈值 -const int INPUT_WIDTH = 640; // 模型输入宽度 -const int INPUT_HEIGHT = 640; // 模型输入高度 +// Parameters +const float CONFIDENCE_THRESHOLD = 0.2; // Confidence threshold +const float NMS_THRESHOLD = 0.2; // Non-maximum suppression threshold +const int INPUT_WIDTH = 640; // Model input width +const int INPUT_HEIGHT = 640; // Model input height -// 检测结构体 +// Detection structure struct Detection { cv::Rect box; float confidence; }; + class Timer { public: Timer() : start_time(std::chrono::high_resolution_clock::now()) {} - // 重新启动定时器 + // Restart the timer void restart() { start_time = std::chrono::high_resolution_clock::now(); } - // 获取并打印从上次启动到当前的时间差 + // Get and print the time elapsed since last start void printElapsedTime(const std::string& message) { auto end_time = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = end_time - start_time; std::cout << message << ": " << elapsed.count() << " seconds" << std::endl; - // 重新启动定时器以供下次测量 + // Restart the timer for the next measurement start_time = end_time; } @@ -38,195 +36,164 @@ private: std::chrono::high_resolution_clock::time_point start_time; }; - -// 在图像上绘制检测框 -void drawDetections(cv::Mat& inputImage, const std::vector& detections) { - for (const auto& detection : detections) { - cv::rectangle(inputImage, detection.box, cv::Scalar(0, 255, 0), 2); - std::string label = "Object: " + cv::format("%.2f", detection.confidence); - int baseLine; - cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); - cv::rectangle(inputImage, cv::Point(detection.box.x, detection.box.y - labelSize.height - baseLine), - cv::Point(detection.box.x + labelSize.width, detection.box.y), cv::Scalar(0, 255, 0), cv::FILLED); - cv::putText(inputImage, label, cv::Point(detection.box.x, detection.box.y - baseLine), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1); - } -} +// Function to resize and pad the input image cv::Mat resizeAndPad(const cv::Mat& image, int targetWidth, int targetHeight, int& padTop, int& padLeft, float& scale, const cv::Scalar& padColor) { int originalWidth = image.cols; int originalHeight = image.rows; - // 计算缩放比例 + // Calculate scaling factor scale = std::min((float)targetWidth / originalWidth, (float)targetHeight / originalHeight); - // 缩放后的新尺寸 + // New dimensions after scaling int newWidth = static_cast(originalWidth * scale); int newHeight = static_cast(originalHeight * scale); - // 缩放图像 + // Resize the image cv::Mat resizedImage; cv::resize(image, resizedImage, cv::Size(newWidth, newHeight)); - // 计算填充值 + // Padding calculations padTop = (targetHeight - newHeight) / 2; int padBottom = targetHeight - newHeight - padTop; padLeft = (targetWidth - newWidth) / 2; int padRight = targetWidth - newWidth - padLeft; - // 在图像周围添加填充,使用灰色 (128, 128, 128) 填充 + // Add padding around the image (using gray color) cv::Mat paddedImage; cv::copyMakeBorder(resizedImage, paddedImage, padTop, padBottom, padLeft, padRight, cv::BORDER_CONSTANT, padColor); return paddedImage; } + +// Function to create an image with only detected regions filled as white (rest black) in the original image size +cv::Mat createDetectionMask(const cv::Mat& originalImage, const std::vector& detections, float scale, int padTop, int padLeft) { + // Create a black image with the same size as the original image + cv::Mat mask = cv::Mat::zeros(originalImage.size(), CV_8UC1); // Single channel for black and white mask + + // Fill the detected regions with white + for (const auto& detection : detections) { + // Rescale the coordinates from the padded image back to the original image + int x = static_cast((detection.box.x - padLeft) / scale); + int y = static_cast((detection.box.y - padTop) / scale); + int w = static_cast(detection.box.width / scale); + int h = static_cast(detection.box.height / scale); + + // Ensure coordinates are within the bounds of the original image + x = std::max(0, std::min(x, originalImage.cols - 1)); + y = std::max(0, std::min(y, originalImage.rows - 1)); + w = std::min(w, originalImage.cols - x); + h = std::min(h, originalImage.rows - y); + + cv::rectangle(mask, cv::Rect(x, y, w, h), cv::Scalar(255), cv::FILLED); // White color for detections + } + + return mask; +} + int main() { - // 模型路径和图片路径 + // Model and image paths std::string modelPath = "C:\\Users\\zjc\\Desktop\\dimo_11.14.onnx"; std::string imagePath = "C:\\Users\\zjc\\Desktop\\dimo.bmp"; Timer timer1; - // 加载模型 - cv::dnn::Net net = cv::dnn::readNetFromONNX(modelPath); - net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); // 设置为使用 CUDA 后端 - net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); // 设置为在 GPU 上运行 - timer1.printElapsedTime("Time to load the model"); - // 读取输入图像 + // Load the model + cv::dnn::Net net = cv::dnn::readNetFromONNX(modelPath); + net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); // Use CUDA backend + net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); // Run on GPU + timer1.printElapsedTime("Time to load the model"); + + // Read the input image timer1.restart(); cv::Mat image = cv::imread(imagePath); if (image.empty()) { std::cerr << "Could not read the image: " << imagePath << std::endl; return -1; } - // 设置填充颜色为灰色 + + // Set padding color (gray) cv::Scalar padColor(128, 128, 128); - // 预处理图像并添加填充 + // Preprocess image and add padding int padTop, padLeft; float scale; cv::Mat inputImage = resizeAndPad(image, INPUT_WIDTH, INPUT_HEIGHT, padTop, padLeft, scale, padColor); - // 显示调整和填充后的图像 - // cv::imshow("Resized and Padded Image", inputImage); - // 预处理图像 + // Prepare image for model input cv::Mat blob = cv::dnn::blobFromImage(inputImage, 1 / 255.0, cv::Size(INPUT_WIDTH, INPUT_HEIGHT), cv::Scalar(0, 0, 0), true, false); net.setInput(blob); - timer1.printElapsedTime("Time to preprocessing"); - timer1.restart(); - for(int j = 0; j <1; j++) { - // 推理模型 + timer1.printElapsedTime("Time to preprocess image"); + + for (int j = 0; j < 1; j++) { + // Run inference cv::Mat output = net.forward(); - // 处理输出数据 + // Process output data std::vector detections; float* data = (float*)output.data; + for (int i = 0; i < 25200; ++i) { - float confidence = data[i * 6 + 4]; // 置信度 + float confidence = data[i * 6 + 4]; // Confidence score if (confidence >= CONFIDENCE_THRESHOLD) { - // 获取检测框并映射到图像坐标 - // Remove the unnecessary multiplication + // Get bounding box coordinates float cx = data[i * 6]; float cy = data[i * 6 + 1]; float w = data[i * 6 + 2]; float h = data[i * 6 + 3]; - // If needed, adjust for differences between input image size and model input size - // Since they are the same in your case, this step can be omitted or kept as is + // Map to image coordinates cx = cx * inputImage.cols / INPUT_WIDTH; cy = cy * inputImage.rows / INPUT_HEIGHT; w = w * inputImage.cols / INPUT_WIDTH; h = h * inputImage.rows / INPUT_HEIGHT; - // Proceed with the rest of your code int left = static_cast(cx - w / 2); int top = static_cast(cy - h / 2); int width = static_cast(w); int height = static_cast(h); - // Ensure coordinates are within image bounds left = std::max(0, std::min(left, inputImage.cols - 1)); top = std::max(0, std::min(top, inputImage.rows - 1)); width = std::min(width, inputImage.cols - left); height = std::min(height, inputImage.rows - top); - // Add detection + // Add detection to vector detections.push_back({cv::Rect(left, top, width, height), confidence}); - } } - // 非极大值抑制 + + // Non-Maximum Suppression std::vector indices; std::vector boxes; std::vector scores; + for (const auto& detection : detections) { boxes.push_back(detection.box); - scores.push_back(detection.confidence); } + cv::dnn::NMSBoxes(boxes, scores, CONFIDENCE_THRESHOLD, NMS_THRESHOLD, indices); std::cout << "Number of detections after NMS: " << indices.size() << std::endl; - if (indices.empty()) { - std::cout << "No boxes passed NMS." << std::endl; - } - for (int idx : indices) { - Detection detection = detections[idx]; - std::cout << "Drawing box at: (" << detection.box.x << ", " << detection.box.y - << "), width: " << detection.box.width << ", height: " << detection.box.height << std::endl; - drawDetections(inputImage, {detection}); - } std::vector finalDetections; for (int idx : indices) { finalDetections.push_back(detections[idx]); } - for (int i = 0; i < 25200; ++i) { - float confidence = data[i * 6 + 4]; - if (confidence >= CONFIDENCE_THRESHOLD) { - // std::cout << "Detection " << i << ": confidence=" << confidence << std::endl; - } - } - // 绘制检测框并显示图像 - drawDetections(image, finalDetections); + // Create the mask for the detected regions (matching original image size) + cv::Mat detectionMask = createDetectionMask(image, finalDetections, scale, padTop, padLeft); + + // Show the mask + cv::imshow("Detection Mask", detectionMask); + + // Save the result as an image + std::string savepath = "C:\\Users\\zjc\\Desktop\\suspect_mask.png"; + cv::imwrite(savepath, detectionMask); + timer1.printElapsedTime("Time to run inference"); } - int depth = inputImage.depth(); // 图像数据类型 - int channels = inputImage.channels(); // 通道数 - // 判断图像深度和通道数,打印类型 - std::string depthStr; - switch (depth) { - case CV_8U: - depthStr = "8-bit unsigned integer"; - break; - case CV_8S: - depthStr = "8-bit signed integer"; - break; - case CV_16U: - depthStr = "16-bit unsigned integer"; - break; - case CV_16S: - depthStr = "16-bit signed integer"; - break; - case CV_32S: - depthStr = "32-bit signed integer"; - break; - case CV_32F: - depthStr = "32-bit floating point"; - break; - case CV_64F: - depthStr = "64-bit floating point"; - break; - default: - depthStr = "Unknown depth"; - break; - } - - std::cout << "Image Depth: " << depthStr << std::endl; - std::cout << "Number of Channels: " << channels << std::endl; - - cv::imshow("Detections", inputImage); cv::waitKey(0); return 0; -} -// \ No newline at end of file +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d252e6..c332fe0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,9 +5,18 @@ add_library(Matrox Matrox/template_matching.cpp Matrox/mask.cpp ) - # 头文件路径 target_include_directories(Matrox PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - # 链接依赖库 target_link_libraries(Matrox PUBLIC Qt6::Widgets ${OpenCV_LIBS} ${MIL_LIBS}) + + + +#定义CVDL模块的库 +add_library(CVDL + CVDL/OnnxRunner.cpp +) +# 头文件路径 +target_include_directories(CVDL PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +# 链接依赖库 +target_link_libraries(CVDL PUBLIC Qt6::Widgets ${OpenCV_LIBS} ${MIL_LIBS}) \ No newline at end of file diff --git a/src/Matrox/template_matching.cpp b/src/Matrox/template_matching.cpp index bf6132b..c0c3642 100644 --- a/src/Matrox/template_matching.cpp +++ b/src/Matrox/template_matching.cpp @@ -265,7 +265,7 @@ void TemplateMatcher::FindTemplates( const MIL_ID& inputImage, MIL_ID& outputIma // TODO: Opencv ONNX runner, -// 1. 构建相应的模型加载和模型运行函数 +// 1. 构建相应的模型加载和模型运行函数- // 2. 在src里头添加另一个cvdl库,专用于视觉深度学习 // 3. 添加一个类OnnxRunner diff --git a/src/Matrox/utils.cpp b/src/Matrox/utils.cpp index fdde47a..0021ebe 100644 --- a/src/Matrox/utils.cpp +++ b/src/Matrox/utils.cpp @@ -9,7 +9,8 @@ #include #include #include - +#include "Mil.h" +#include using namespace std; /** @@ -121,4 +122,106 @@ void read_params_from_file(const std::string& filename, std::map 1) { +// MIL_ID grayscaleImage; +// // 分配一个新的灰度图像缓冲区 +// MbufAlloc2d(MilSystem, width, height, 8 + M_UNSIGNED, M_IMAGE + M_PROC + M_DISP, &grayscaleImage); +// // 将彩色图像转换为灰度图 +// MimConvert(milImage, grayscaleImage, M_RGB_TO_L); +// // 获取转换后的灰度图像数据 +// MbufGet(grayscaleImage, buf); +// MbufFree(grayscaleImage); // 释放灰度图像缓冲区 +// } else { +// // 如果本身是灰度图像,直接获取数据 +// MbufGet(milImage, buf); +// } +// +// // 将数据封装成OpenCV Mat对象 +// cv::Mat cvMat(height, width, CV_8UC1, buf); +// return cvMat; +// } +cv::Mat milToMat(MIL_ID milImage) { + // 获取图像的宽度、高度和像素格式 + int width, height; + MIL_INT pixelFormat; + MbufInquire(milImage, M_SIZE_X, &width); // 获取图像宽度 + MbufInquire(milImage, M_SIZE_Y, &height); // 获取图像高度 + MbufInquire(milImage, M_TYPE, &pixelFormat); // 获取图像像素格式 + + // 判断像素格式并选择适当的数据类型 + // MIL_INT 和 M_UINT8 是典型的像素格式,OpenCV 处理不同的类型会有不同的实现 + cv::Mat matImage; + if (pixelFormat == M_RGB) + { + // 如果是RGB图像 + matImage = cv::Mat(height, width, CV_8UC3); + } + else + {// 如果是其他类型的图像,你需要根据实际格式进行调整 + std::cerr << "Unsupported MIL image format!" << std::endl; + return matImage; + } + + // 获取图像数据并复制到 OpenCV Mat + MbufGet(milImage, matImage.data); // 获取 MIL 图像数据并拷贝到 Mat 对象 + + return matImage; +} +void processImage(cv::Mat& img) { + // 1. 将图像从BGR转换到HSV色彩空间 + cv::Mat hsv; + cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV); + + // 2. 定义绿色的HSV色彩范围 + cv::Scalar lower_green(35, 40, 40); // 低阈值 (H, S, V) + cv::Scalar upper_green(85, 255, 255); // 高阈值 (H, S, V) + + // 3. 根据绿色范围创建掩膜 + cv::Mat green_mask; + cv::inRange(hsv, lower_green, upper_green, green_mask); + + // 4. 使用形态学操作去除噪点,增强绿色区域 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); + cv::morphologyEx(green_mask, green_mask, cv::MORPH_CLOSE, kernel); + + // 5. 查找轮廓 + std::vector> contours; + cv::findContours(green_mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + + // 6. 创建一个黑色背景图像 + cv::Mat result = cv::Mat::zeros(img.size(), img.type()); + + // 7. 遍历轮廓,找到矩形框并将其内部填充为白色 + for (size_t i = 0; i < contours.size(); i++) { + // 通过近似多边形来检测矩形 + std::vector approx; + cv::approxPolyDP(contours[i], approx, cv::arcLength(contours[i], true) * 0.02, true); + + // 如果是四边形,认为是矩形 + if (approx.size() == 4) { + // 计算矩形的bounding box + cv::Rect rect = cv::boundingRect(approx); + + // 仅在绿色区域内将矩形框填充为白色 + cv::rectangle(result, rect, cv::Scalar(255, 255, 255), cv::FILLED); + } + } + + // 8. 显示结果图像 + cv::imshow("Processed Image", result); + cv::waitKey(0); +} \ No newline at end of file diff --git a/src/Matrox/utils.h b/src/Matrox/utils.h index 08f505e..72b933f 100644 --- a/src/Matrox/utils.h +++ b/src/Matrox/utils.h @@ -12,6 +12,7 @@ #include #include #include +#include"opencv2/opencv.hpp" #include // 声明全局变量(注意:这里只是声明,不是定义) @@ -38,5 +39,6 @@ MIL_ID convert_to_uint8(MIL_ID input_img); std::wstring convert_to_wstring(const std::string& str); void read_params_from_file(const std::string& filename, std::map& params) ; - +cv::Mat milToMat(MIL_ID MilImage); +void processImage(cv::Mat& img); #endif //UTILS_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 986f60d..54a7271 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,16 +9,20 @@ target_link_libraries(test_color_range Matrox ${OpenCV_LIBS} ${MIL_LIBS}) # 测试用例 2: template_template_matching add_executable(test_template_matching - ${CMAKE_CURRENT_SOURCE_DIR}/test_template_matching.cpp -) - + ${CMAKE_CURRENT_SOURCE_DIR}/test_template_matching.cpp) # 链接 Matrox 模块和依赖库 target_link_libraries(test_template_matching Matrox ${OpenCV_LIBS} ${MIL_LIBS}) -add_executable(test_mask - ${CMAKE_CURRENT_SOURCE_DIR}/test_mask.cpp -) +#测试用例 3:test_mask +add_executable(test_mask + ${CMAKE_CURRENT_SOURCE_DIR}/test_mask.cpp) # 链接 Matrox 模块和依赖库 -target_link_libraries(test_mask Matrox ${OpenCV_LIBS} ${MIL_LIBS}) \ No newline at end of file +target_link_libraries(test_mask Matrox ${OpenCV_LIBS} ${MIL_LIBS}) + +#测试用例 4:test_onnx +add_executable(test_onnx + ${CMAKE_CURRENT_SOURCE_DIR}/test_onnx.cpp) +# 链接 Matrox 模块和依赖库 +target_link_libraries(test_onnx Matrox ${OpenCV_LIBS} ${MIL_LIBS}) \ No newline at end of file diff --git a/tests/test_onnx.cpp b/tests/test_onnx.cpp new file mode 100644 index 0000000..7a99da8 --- /dev/null +++ b/tests/test_onnx.cpp @@ -0,0 +1,66 @@ +#include "vector" +#include"iostream" +#include"string" +#include"Matrox/utils.h" +#include"opencv2/opencv.hpp" + +#define IMAGE_PATH MIL_TEXT("C:\\Users\\zjc\\Desktop\\8.bmp") +#define SAVE_PATH MIL_TEXT("C:\\Users\\zjc\\Desktop\\suspect.png") + +// int main() { +// MIL_ID MilImage = M_NULL; +// MIL_ID MilApplication = M_NULL, MilSystem = M_NULL, MilDisplay = M_NULL; +// +// MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, &MilDisplay, M_NULL, +// M_NULL); +// MbufRestore(IMAGE_PATH, MilSystem, &MilImage); +// cv::Mat opencvImage = milToMat(MilImage); +// imshow("opencv", opencvImage); +// // MosGetch(); +// +// return 0; +// } +int main() { + MIL_ID MilApplication = M_NULL, MilSystem = M_NULL, MilDisplay = M_NULL; + MIL_ID MilImage = M_NULL; + + // 初始化 MIL 应用程序、系统和显示 + MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, &MilDisplay, M_NULL, M_NULL); + if (MilApplication == M_NULL || MilSystem == M_NULL || MilDisplay == M_NULL) { + std::cerr << "MIL Initialization failed!" << std::endl; + return -1; + } + + // 加载图像 + MbufRestore(IMAGE_PATH, MilSystem, &MilImage); + if (MilImage == M_NULL) { + std::cerr << "Failed to load MIL image!" << std::endl; + MappFreeDefault(MilApplication, MilSystem, MilDisplay, M_NULL,M_NULL); + return -1; + } + + // 转换并显示 + cv::Mat opencvImage = milToMat(MilImage); + if (!opencvImage.empty()) { + cv::imshow("opencv", opencvImage); + cv::waitKey(0); + } + std:: string savepath="C:\\Users\\zjc\\Desktop\\suspect.png"; + cv::imwrite(savepath,opencvImage); + // 释放资源 + MbufFree(MilImage); + MappFreeDefault(MilApplication, MilSystem, MilDisplay, M_NULL,M_NULL); // 使用 MappFreeDefault 代替 MappFree + + return 0; +} + +// int main() { +// cv::Mat img = cv::imread("C:\\Users\\zjc\\Desktop\\suspect.png"); +// if (img.empty()) { +// std::cout << "图像加载失败!" << std::endl; +// return -1; +// } +// +// // 处理图像 +// processImage(img); +// } \ No newline at end of file diff --git a/tests/test_template_matching.cpp b/tests/test_template_matching.cpp index fb2e568..5ff24dc 100644 --- a/tests/test_template_matching.cpp +++ b/tests/test_template_matching.cpp @@ -19,8 +19,6 @@ MIL_ID MilApplication = M_NULL, MilSystem = M_NULL, MilDisplay = M_NULL; int main() { using namespace std; - - std::map params; read_params_from_file("C:\\Users\\zjc\\Desktop\\config\\template_color_config.txt", params); // Initialize MIL application @@ -61,6 +59,5 @@ int main() { MbufFree(detection_result); MbufFree(MilImage); - return 0; } \ No newline at end of file