<chrono> 与 ratio.h编译错误

本文详细介绍了如何解决在使用Visual Studio 2017进行C++编程时遇到的ratio(111): error C2065: 'INTMAX_MAX' undeclared identifier错误。通过在预处理器定义中加入__STDC_LIMIT_MACROS宏,可以有效修复这一问题。

vs2017 提示错误:ratio(111): error C2065: 'INTMAX_MAX': undeclared identifier

 

修复:

http://www.cplusplus.com/forum/unices/144894/

 

也就是在C++预处理器中加入: __STDC_LIMIT_MACROS 

 

如:  WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions);__STDC_LIMIT_MACROS 

// 引入必要的头文件 #include <opencv2/opencv.hpp> // OpenCV 核心库,用于图像处理 #include <opencv2/stitching.hpp> // OpenCV 图像拼接库 #include <iostream> // 输入输出流,用于控制台输出 #include <vector> // 向量容器,用于存储多个图像 #include <string> // 字符串处理 #include <algorithm> // 算法库,用于排序等操作 #include <regex> // 正则表达式库,用于文件名解析 #include <sstream> // 字符串流,用于字符串处理 #include <iomanip> // 输入输出格式化 #include <chrono> // 时间库,用于性能测量 #include <thread> // 线程库,用于实时显示 #include <atomic> // 原子操作库 #include <mutex> // 互斥锁库 // 配置参数 - 这些是程序的设置,可以根据需要修改 const std::string IMAGE_DIR = "C:/Users/zhangqt203387/images"; // 图像文件夹路径 const std::string OUTPUT_DIR = "output"; // 输出文件夹名称 const int RESIZE_THRESHOLD = 1024; // 图像最大尺寸限制 const std::string FEATURE_DETECTOR = "SIFT"; // 使用的特征检测算法 const double RANSAC_THRESHOLD = 5.0; // RANSAC算法的容错阈值 const double MATCH_RATIO = 0.6; // 特征匹配的质量阈值 const int MIN_MATCHES = 10; // 最小匹配点数量 const bool SHOW_PROGRESS = true; // 是否显示拼接过程 const double DISPLAY_SCALE = 0.3; // 显示图像的缩放比例 // 图像预处理参数 const bool ENABLE_PREPROCESSING = true; // 是否启用图像预处理 const int MEDIAN_BLUR_SIZE = 3; // 中值滤波核大小 const double INDANE_CLIP_LIMIT = 2.0; // INDANE算法对比度限制 const int INDANE_TILE_SIZE = 8; // INDANE算法瓦片大小 // 特征提取参数 const int MAX_FEATURES = 5000; // 最大特征点数量 const double CONTRAST_THRESHOLD = 0.04; // 对比度阈值 // 图像融合参数 const bool USE_MULTI_BAND_BLENDING = true; // 是否使用多频带融合 const int BLEND_WIDTH = 5; // 融合宽度 // 定义图像拼接器类 class AdvancedImageStitcher { private: // 类的私有成员变量 std::string detectorType; // 特征检测器类型 cv::Ptr<cv::Feature2D> detector; // OpenCV特征检测器对象 cv::Ptr<cv::DescriptorMatcher> matcher; // 特征匹配器对象 std::vector<cv::Mat> images; // 存储加载的图像 std::vector<std::string> imagePaths; // 存储图像文件路径 cv::Mat panorama; // 存储拼接后的全景图 std::atomic<bool> stopDisplay; // 停止显示标志 std::mutex panoramaMutex; // 全景图互斥锁 // 设置特征检测器和匹配器 void setupDetectors() { // 根据选择的类型创建特征检测器 if (detectorType == "SIFT") { detector = cv::SIFT::create(MAX_FEATURES, 3, CONTRAST_THRESHOLD); } else if (detectorType == "AKAZE") { detector = cv::AKAZE::create(); } else { // 默认使用ORB detector = cv::ORB::create(MAX_FEATURES); } // 根据特征检测器类型创建匹配器 if (detectorType == "SIFT" ) { matcher = cv::FlannBasedMatcher::create(); // SIFT和SURF使用FLANN匹配器 } else { matcher = cv::BFMatcher::create(cv::NORM_HAMMING, false); // 其他使用暴力匹配器 } } // INDANE图像增强算法 cv::Mat applyINDANE(const cv::Mat& input) { cv::Mat lab; cv::cvtColor(input, lab, cv::COLOR_BGR2Lab); std::vector<cv::Mat> labChannels; cv::split(lab, labChannels); // 对L通道应用CLAHE cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(); clahe->setClipLimit(INDANE_CLIP_LIMIT); clahe->setTilesGridSize(cv::Size(INDANE_TILE_SIZE, INDANE_TILE_SIZE)); clahe->apply(labChannels[0], labChannels[0]); cv::Mat enhancedLab; cv::merge(labChannels, enhancedLab); cv::Mat result; cv::cvtColor(enhancedLab, result, cv::COLOR_Lab2BGR); return result; } // 逆滤波去模糊 cv::Mat applyInverseFilter(const cv::Mat& input) { // 创建模糊核(假设轻微高斯模糊) cv::Mat kernel = cv::getGaussianKernel(15, 1.5); kernel = kernel * kernel.t(); // 将图像转换为浮点型 cv::Mat floatInput; input.convertTo(floatInput, CV_32F); // 应用维纳滤波(逆滤波的稳定版本) cv::Mat result; cv::filter2D(floatInput, result, -1, kernel); // 将结果转换回8位 result.convertTo(result, CV_8U); return result; } // 图像预处理函数 cv::Mat preprocessImage(const cv::Mat& input) { if (!ENABLE_PREPROCESSING) { return input.clone(); } cv::Mat result = input.clone(); // 步骤1: INDANE图像增强 // result = applyINDANE(result); // 步骤2: 逆滤波去模糊 // result = applyInverseFilter(result); // 步骤3: 中值滤波去噪 cv::medianBlur(result, result, MEDIAN_BLUR_SIZE); return result; } // 自然排序比较函数 - 使文件名中的数字按数值大小排序 static bool naturalCompare(const std::string& a, const std::string& b) { // 将字符串转换为小写,实现不区分大小写的比较 std::string aLower = a; std::string bLower = b; std::transform(aLower.begin(), aLower.end(), aLower.begin(), ::tolower); std::transform(bLower.begin(), bLower.end(), bLower.begin(), ::tolower); // 使用正则表达式分割字符串中的数字和非数字部分 std::regex re("(\\d+)|(\\D+)"); std::sregex_iterator it1(aLower.begin(), aLower.end(), re); std::sregex_iterator it2(bLower.begin(), bLower.end(), re); std::sregex_iterator end; // 逐个比较分割后的部分 while (it1 != end && it2 != end) { std::smatch match1 = *it1; std::smatch match2 = *it2; // 如果两部分都是数字,则按数值比较 if (std::regex_match(match1.str(), std::regex("\\d+")) && std::regex_match(match2.str(), std::regex("\\d+"))) { int num1 = std::stoi(match1.str()); // 将字符串转换为整数 int num2 = std::stoi(match2.str()); if (num1 != num2) return num1 < num2; // 数值比较 } // 否则按字符串比较 else if (match1.str() != match2.str()) { return match1.str() < match2.str(); // 字符串比较 } // 移动到下一个部分 ++it1; ++it2; } // 如果前面的部分都相同,则按长度比较 return aLower.length() < bLower.length(); } // 特征匹配函数 std::vector<cv::DMatch> matchFeatures(const cv::Mat& des1, const cv::Mat& des2) { // 使用KNN匹配(K近邻匹配)找到最佳匹配 std::vector<std::vector<cv::DMatch>> knnMatches; matcher->knnMatch(des1, des2, knnMatches, 2); // k=2表示每个点找两个最佳匹配 // 筛选优质匹配点 std::vector<cv::DMatch> goodMatches; for (const auto& matchPair : knnMatches) { if (matchPair.size() == 2) { // 应用比率测试:最佳匹配的距离应小于次佳匹配距离的一定比例 if (matchPair[0].distance < MATCH_RATIO * matchPair[1].distance) { goodMatches.push_back(matchPair[0]); // 保留优质匹配 } } } // 输出匹配点数量信息 std::cout << "良好匹配点: " << goodMatches.size() << std::endl; return goodMatches; } // 计算单应性矩阵函数 cv::Mat findHomography(const std::vector<cv::KeyPoint>& kp1, const std::vector<cv::KeyPoint>& kp2, const std::vector<cv::DMatch>& matches, int& inliers) { // 检查是否有足够的匹配点 if (matches.size() < MIN_MATCHES) { return cv::Mat(); // 返回空矩阵 } // 提取匹配点的坐标 std::vector<cv::Point2f> srcPoints, dstPoints; for (const auto& match : matches) { srcPoints.push_back(kp2[match.trainIdx].pt); // 第二幅图像中的点 dstPoints.push_back(kp1[match.queryIdx].pt); // 第一幅图像中的点 } // 使用RANSAC算法计算单应性矩阵 cv::Mat mask; // 掩码,标记哪些点是内点(正确匹配) cv::Mat H = cv::findHomography(srcPoints, dstPoints, cv::RANSAC, RANSAC_THRESHOLD, mask); // 计算内点数量(正确匹配的点) inliers = 0; if (!mask.empty()) { inliers = cv::countNonZero(mask); // 统计非零元素数量 } // 输出内点信息 std::cout << "内点数量: " << inliers << "/" << matches.size() << std::endl; return H; // 返回单应性矩阵 } // 加权平均融合 cv::Mat weightedBlend(const cv::Mat& img1, const cv::Mat& img2, const cv::Mat& H) { // 计算变换后图像的大小 std::vector<cv::Point2f> corners = { cv::Point2f(0, 0), cv::Point2f(img2.cols, 0), cv::Point2f(img2.cols, img2.rows), cv::Point2f(0, img2.rows) }; std::vector<cv::Point2f> warpedCorners; cv::perspectiveTransform(corners, warpedCorners, H); // 计算边界 float x_min = 0, x_max = img1.cols; float y_min = 0, y_max = img1.rows; for (const auto& pt : warpedCorners) { x_min = std::min(x_min, pt.x); x_max = std::max(x_max, pt.x); y_min = std::min(y_min, pt.y); y_max = std::max(y_max, pt.y); } // 创建结果图像 cv::Mat result(cv::Size(std::ceil(x_max - x_min), std::ceil(y_max - y_min)), img1.type()); // 应用变换 cv::Mat translation = cv::Mat::eye(3, 3, CV_64F); translation.at<double>(0, 2) = -x_min; translation.at<double>(1, 2) = -y_min; cv::Mat H_adjusted = translation * H; cv::warpPerspective(img2, result, H_adjusted, result.size()); // 叠加第一幅图像(使用加权平均) for (int y = 0; y < img1.rows; y++) { for (int x = 0; x < img1.cols; x++) { int result_x = x - x_min; int result_y = y - y_min; if (result_x >= 0 && result_x < result.cols && result_y >= 0 && result_y < result.rows) { cv::Vec3b pixel1 = img1.at<cv::Vec3b>(y, x); cv::Vec3b pixel2 = result.at<cv::Vec3b>(result_y, result_x); // 计算权重(简单线性混合) double weight = 0.5; if (x < img1.cols * 0.1) weight = 0.9; // 边缘区域权重更高 else if (x > img1.cols * 0.9) weight = 0.1; cv::Vec3b blended; for (int c = 0; c < 3; c++) { blended[c] = cv::saturate_cast<uchar>(weight * pixel1[c] + (1 - weight) * pixel2[c]); } result.at<cv::Vec3b>(result_y, result_x) = blended; } } } return result; } // 显示图像函数(用于中间过程) void showImage(const std::string& title, const cv::Mat& image) { if (image.empty()) return; // 检查图像是否为空 // 计算缩放比例,确保图像适合屏幕显示 int origH = image.rows; int origW = image.cols; double scale = std::min(DISPLAY_SCALE, 800.0 / origW); scale = std::min(scale, 600.0 / origH); scale = std::max(scale, 0.1); // 确保最小缩放比例 // 缩放图像 cv::Mat displayImg; cv::resize(image, displayImg, cv::Size(), scale, scale); // 显示图像 cv::imshow(title, displayImg); cv::waitKey(1); // 非阻塞等待,允许图像更新 } // 实时显示线程函数 void displayThreadFunc() { while (!stopDisplay) { { std::lock_guard<std::mutex> lock(panoramaMutex); if (!panorama.empty()) { showImage("实时拼接进度", panorama); } } std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 每100ms更新一次 } cv::destroyWindow("实时拼接进度"); } public: // 构造函数:初始化特征检测器类型并设置检测器 AdvancedImageStitcher(const std::string& detectorType = "SIFT") : detectorType(detectorType), stopDisplay(false) { setupDetectors(); } // 析构函数 ~AdvancedImageStitcher() { stopDisplay = true; } // 加载图像函数 void loadImages(const std::string& imageDir) { // 支持的图像扩展名 std::vector<std::string> extensions = { ".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif" }; // 使用OpenCV的glob函数查找图像文件 std::vector<cv::String> filePaths; cv::glob(imageDir + "/*", filePaths); std::vector<std::string> imagePaths; // 筛选图像文件 for (const auto& path : filePaths) { std::string ext = path.substr(path.find_last_of(".")); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end()) { imagePaths.push_back(path); } } // 检查是否找到图像文件 if (imagePaths.empty()) { std::cerr << "在 '" << imageDir << "' 目录下未找到任何图像文件" << std::endl; return; } // 使用自然排序对图像路径进行排序 std::sort(imagePaths.begin(), imagePaths.end(), naturalCompare); // 输出找到的图像信息 std::cout << "找到 " << imagePaths.size() << " 张图像:" << std::endl; for (size_t i = 0; i < imagePaths.size(); i++) { std::string fileName = imagePaths[i].substr(imagePaths[i].find_last_of("\\/") + 1); std::cout << " " << i + 1 << ": " << fileName << std::endl; } // 保存图像路径 this->imagePaths = imagePaths; // 加载并预处理图像 for (const auto& path : imagePaths) { // 读取图像 cv::Mat img = cv::imread(path); if (img.empty()) { std::cout << "无法加载图像: " << path << std::endl; continue; } // 图像预处理 cv::Mat processedImg = preprocessImage(img); // 缩减图像分辨率(如果太大) int h = processedImg.rows; int w = processedImg.cols; if (w > RESIZE_THRESHOLD || h > RESIZE_THRESHOLD) { double scale = RESIZE_THRESHOLD / static_cast<double>(std::max(w, h)); int newW = static_cast<int>(w * scale); int newH = static_cast<int>(h * scale); cv::resize(processedImg, processedImg, cv::Size(newW, newH), 0, 0, cv::INTER_AREA); std::cout << "已缩放: " << w << "x" << h << " -> " << newW << "x" << newH << std::endl; } // 将图像添加到向量中 images.push_back(processedImg); } // 检查是否成功加载了图像 if (images.empty()) { std::cerr << "未能成功加载任何图像" << std::endl; } } // 拼接图像函数 cv::Mat stitch() { // 如果只有一张或没有图像,直接返回 if (images.size() <= 1) { panorama = images.empty() ? cv::Mat() : images[0].clone(); return panorama; } // 启动实时显示线程 std::thread displayThread; if (SHOW_PROGRESS) { displayThread = std::thread(&AdvancedImageStitcher::displayThreadFunc, this); } // 从第一张图像开始 panorama = images[0].clone(); // 存储所有变换矩阵 std::vector<cv::Mat> transforms; transforms.push_back(cv::Mat::eye(3, 3, CV_64F)); // 第一张图像的变换矩阵是单位矩阵 // 依次拼接后续图像 for (size_t i = 1; i < images.size(); i++) { std::cout << "\n拼接图像 " << i + 1 << "/" << images.size() << std::endl; // 转换为灰度图(特征检测需要灰度图像) cv::Mat panoramaGray, newImgGray; cv::cvtColor(panorama, panoramaGray, cv::COLOR_BGR2GRAY); cv::cvtColor(images[i], newImgGray, cv::COLOR_BGR2GRAY); // 提取特征点和特征描述符 std::vector<cv::KeyPoint> kp1, kp2; cv::Mat des1, des2; detector->detectAndCompute(panoramaGray, cv::noArray(), kp1, des1); detector->detectAndCompute(newImgGray, cv::noArray(), kp2, des2); // 检查是否有足够的特征点 if (des1.empty() || des2.empty() || des1.rows < MIN_MATCHES || des2.rows < MIN_MATCHES) { std::cout << "特征点不足" << std::endl; continue; } // 特征匹配 std::vector<cv::DMatch> matches = matchFeatures(des1, des2); if (matches.size() < MIN_MATCHES) { std::cout << "匹配点不足: " << matches.size() << " < " << MIN_MATCHES << std::endl; continue; } // 计算单应性矩阵 int inliers; cv::Mat H = findHomography(kp1, kp2, matches, inliers); // 检查单应性矩阵是否有效 if (H.empty() || inliers < MIN_MATCHES) { std::cout << "单应性矩阵计算失败或内点不足: " << inliers << std::endl; continue; } // 保存变换矩阵 transforms.push_back(H); // 应用变换并融合图像 cv::Mat newPanorama = weightedBlend(panorama, images[i], H); if (!newPanorama.empty()) { std::lock_guard<std::mutex> lock(panoramaMutex); panorama = newPanorama; } // 如果启用进度显示,更新当前拼接状态 if (SHOW_PROGRESS) { std::lock_guard<std::mutex> lock(panoramaMutex); // 显示线程会自动处理显示 } } // 停止显示线程 if (SHOW_PROGRESS) { stopDisplay = true; displayThread.join(); } return panorama; // 返回最终的全景图 } // 显示最终结果 void showResult() { if (panorama.empty()) { std::cout << "没有可显示的结果" << std::endl; return; } // 计算缩放比例,确保图像适合屏幕显示 int h = panorama.rows; int w = panorama.cols; double scale = std::min(DISPLAY_SCALE, 1200.0 / w); scale = std::min(scale, 800.0 / h); scale = std::max(scale, 0.1); // 确保最小缩放比例 // 缩放图像 cv::Mat displayImg; cv::resize(panorama, displayImg, cv::Size(), scale, scale); // 创建窗口并显示结果 cv::namedWindow("拼接结果", cv::WINDOW_NORMAL); cv::imshow("拼接结果", displayImg); // 等待用户按键 std::cout << "按任意键关闭窗口..." << std::endl; cv::waitKey(0); cv::destroyAllWindows(); // 关闭所有OpenCV窗口 } // 保存结果到文件 bool saveResult(const std::string& filename = "panorama.png") { if (panorama.empty()) { std::cout << "没有可保存的结果" << std::endl; return false; } // 保存图像 std::string path = OUTPUT_DIR + "/" + filename; bool success = cv::imwrite(path, panorama); if (success) { std::cout << "结果已保存到: " << path << std::endl; } else { std::cout << "保存失败: " << path << std::endl; } return success; } }; // 主函数 int main() { // 创建拼接器对象,使用配置的特征检测器类型 AdvancedImageStitcher stitcher(FEATURE_DETECTOR); try { // 加载图像 std::cout << "加载图像..." << std::endl; stitcher.loadImages(IMAGE_DIR); // 拼接图像 std::cout << "开始拼接..." << std::endl; auto start_time = std::chrono::high_resolution_clock::now(); cv::Mat result = stitcher.stitch(); auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time); std::cout << "拼接完成,耗时: " << duration.count() << "ms" << std::endl; // 如果有结果,显示并保存 if (!result.empty()) { // 显示结果 std::cout << "显示结果..." << std::endl; stitcher.showResult(); // 保存结果 std::string filename = "industrial_panorama_" + FEATURE_DETECTOR + ".png"; std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); stitcher.saveResult(filename); } else { std::cout << "拼接失败" << std::endl; } } catch (const std::exception& e) { // 捕获并输出异常 std::cerr << "错误: " << e.what() << std::endl; } return 0; // 程序正常退出 }拼接完图像为什么都是黑影虚图,重新修改下
最新发布
08-27
#include "rclcpp/rclcpp.hpp" #include "sensor_msgs/msg/image.hpp" #include "cv_bridge/cv_bridge.h" #include <opencv2/opencv.hpp> #include <opencv2/core.hpp> #include <opencv2/imgproc.hpp> #include <stdexcept> // 关键头文件 class ImageSubscriber : public rclcpp::Node { public: ImageSubscriber() : Node("image_subscriber") { // 使用lambda避免绑定错误 subscription_ = create_subscription<sensor_msgs::msg::Image>( "/detector/initial_img", 10, [this](sensor_msgs::msg::Image::ConstSharedPtr msg) { this->image_callback(msg); }); } private: void image_callback(sensor_msgs::msg::Image::ConstSharedPtr msg) { try { cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(msg, "bgr8"); cv::Mat image=cv_ptr->image; cv::imshow("Preview", image); cv::waitKey(1); std::vector<cv::Mat> channels; cv::Mat binary,Gaussian,gray,kernal; kernal = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); cv::cvtColor(image,gray,cv::COLOR_BGR2GRAY); cv::threshold(gray,binary,120,255,0); cv::waitKey(1); cv::dilate(binary,binary,kernal); cv::erode(binary, binary, kernal); cv::imshow("dilated", binary); cv::waitKey(1); std::vector<std::vector<cv::Point>> contours; // 存储所有轮廓(二维点集) std::vector<cv::Vec4i> hierarchy; cv::findContours(binary,contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE); cv::Mat result = image.clone(); cv::Mat result2 = image.clone(); std::vector<cv::RotatedRect> lightBarRects; for (int i = 0; i < contours.size(); i++) { double area = contourArea(contours[i if (area < 5 || contours[i].size() <= 1) continue; // 用椭圆拟合区域得到外接矩形(特殊的处理方式:因为灯条是椭圆型的,所以用椭圆去拟合轮廓,再直接获取旋转外接矩形即可) cv::RotatedRect Light_Rec = fitEllipse(contours[i]); // 长宽比和轮廓面积比限制(由于要考虑灯条的远近都被识别到,所以只需要看比例即可) if (Light_Rec.size.width / Light_Rec.size.height > 4) continue; cv::lightInfos.push_back(cv::LightDescriptor(Light_Rec)); } //二重循环多条件匹配灯条 for (size_t i = 0; i < lightInfos.size(); i++) { for (size_t j = i + 1; (j < lightInfos.size()); j++) { LightDescriptor& leftLight = lightInfos[i]; LightDescriptor& rightLight = lightInfos[j]; float angleGap_ = abs(leftLight.angle - rightLight.angle); //由于灯条长度会因为远近而受到影响,所以按照比值去匹配灯条 float LenGap_ratio = abs(leftLight.length - rightLight.length) / max(leftLight.length, rightLight.length); float dis = pow(pow((leftLight.center.x - rightLight.center.x), 2) + pow((leftLight.center.y - rightLight.center.y), 2), 0.5); //均长 float meanLen = (leftLight.length + rightLight.length) / 2; float lengap_ratio = abs(leftLight.length - rightLight.length) / meanLen; float yGap = abs(leftLight.center.y - rightLight.center.y); float yGap_ratio = yGap / meanLen; float xGap = abs(leftLight.center.x - rightLight.center.x); float xGap_ratio = xGap / meanLen; float ratio = dis / meanLen; //匹配不通过的条件 if (angleGap_ > 15 || LenGap_ratio > 1.0 || lengap_ratio > 0.8 || yGap_ratio > 1.5 || xGap_ratio > 2.2 || xGap_ratio < 0.8 || ratio > 3 || ratio < 0.8) { continue; } //绘制矩形 cv::Point center =cv::Point((leftLight.center.x + rightLight.center.x) / 2, (leftLight.center.y + rightLight.center.y) / 2); cv::RotatedRect rect = cv::RotatedRect(center, cv::Size(dis, meanLen), (leftLight.angle + rightLight.angle) / 2); cv::Point2f vertices[4]; rect.points(vertices); for (int i = 0; i < 4; i++) { line(result, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 0, 255), 2.2); } } } for (size_t i = 0; i < contours.size(); i++) { // 计算最小外接矩形 cv::RotatedRect minRect = cv::minAreaRect(contours[i]); lightBarRects.push_back(minRect); // 获取矩形的四个顶点 cv::Point2f vertices[4]; minRect.points(vertices); // 绘制最小外接矩形 for (int j = 0; j < 4; j++) { cv::line(result, vertices[j], vertices[(j+1)%4], cv::Scalar(0, 255, 0), 2); } } cv::imshow("Armor Detection", result); cv::waitKey(1); std::vector<cv::RotatedRect> armorRects; const double maxAngleDiff = 15.0; cv::Point2f vertices2[4]; for (size_t i = 0; i < lightBarRects.size(); i++) { for (size_t j = i + 1; j < lightBarRects.size(); j++) { cv::RotatedRect bar1 = lightBarRects[i]; cv::RotatedRect bar2 = lightBarRects[j]; // 简化角度计算:直接取旋转矩形的角度(OpenCV原生角度) float angle1 = fabs(bar1.angle); float angle2 = fabs(bar2.angle); // 计算角度差(简化角度转换,直接用原生角度差) double angleDiff = fabs(angle1 - angle2); // 仅通过角度差判断是否匹配 if (angleDiff < maxAngleDiff) { // 简化装甲板计算:直接用两灯条中心和尺寸构建 cv::Point2f armorCenter = (bar1.center + bar2.center) / 2; float distance = norm(bar1.center - bar2.center); // 两灯条间距 cv::Size2f armorSize(distance * 1.1, (bar1.size.height + bar2.size.height) / 2 * 1.2); float armorAngle = (bar1.angle + bar2.angle) / 2; // 平均角度 armorRects.push_back(cv::RotatedRect(armorCenter, armorSize, armorAngle)); } } } for (auto& armor : armorRects) { armor.points(vertices2); for (int j = 0; j < 4; j++) { cv::line(result2, vertices2[j], vertices2[(j+1)%4], cv::Scalar(0, 0, 255), 2); } imshow("hihi",result2); cv::waitKey(1); } std::vector<cv::Point2f> vertices2_vec(vertices2, vertices2 + 4); std::vector<cv::Point3f>obj=std::vector<cv::Point3f>{ {-0.027555,0.675,0}, {-0.027555,-0.675,0}, {0.027555,-0.675,0}, {0.027555,0.675,0} }; cv::Mat rVec=cv::Mat::zeros(3,1,CV_64FC1); cv::Mat tVec=cv::Mat::zeros(3,1,CV_64FC1); cv::Mat camera=(cv::Mat_<double>(3,3)<<1749.23969217601,0,711.302879207889,0,1748.77539275011,562.465887239595,0,0,1); cv::Mat dis=(cv::Mat_<double>(1,5)<<0.0846,-0.5788,0,0,0); cv::solvePnP(obj,vertices2_vec,camera,dis,rVec,tVec,false,cv::SOLVEPNP_EPNP); } catch (const cv_bridge::Exception& e) { RCLCPP_ERROR(get_logger(), "cv_bridge异常: %s", e.what()); throw std::runtime_error("CV错误: " + std::string(e.what())); } } rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr subscription_; }; int main(int argc, char* argv[]) { rclcpp::init(argc, argv); auto node = std::make_shared<ImageSubscriber>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; } 结合以上代码写一个最终方案
08-08
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值