当前位置: 首页 > news >正文

openCV3.0 C++ 学习笔记补充(自用 代码+注释)---持续更新 四(91-)

环境:OpenCV3.2.0 + VS2017

91、合并Y方向重叠的轮廓

以轮廓的最小垂直外接矩形框的y为依据,合并y重叠的轮廓。

数学逻辑:几何合并的数学表达

  • 坐标系统:假设矩形由左上角坐标(x, y)和宽高(width, height)定义。
  • 合并公式
    • 合并后左上角:(min(x1, x2), min(y1, y2))
    • 合并后右下角:(max(x1+w1, x2+w2), max(y1+h1, y2+h2))
    • 合并后尺寸:width = max_x - min_xheight = max_y - min_y
  • 91.1:简单直接,但对于大量轮廓可能效率较低(O(n²))

  • 91.2:使用并查集算法,效率更高

	std::vector<std::vector<cv::Point>> contour_end;//最终轮廓std::vector<cv::Rect> bound_Rect_end;if (1) { // 合并 Y方向重叠的轮廓cv::Mat visual_bR = cv::Mat::zeros(480, 640, CV_8UC3);imgOriginal.copyTo(visual_bR);cv::RNG rng(12345);std::vector<std::vector<cv::Point>> mergedContours;//mergedContours = mergeOverlappingContoursY(contour_retained);mergedContours = mergeOverlappingContoursYOptimized(contour_retained); // 优化版本if (debug) cout << "Y方向重叠合并后 mergedContours.size() = " << mergedContours.size() << endl;if (debug) {for (size_t i = 0; i < mergedContours.size(); i++) {cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));cv::drawContours(visual_bR, mergedContours, i, color, 2);}if (debug) cv::imshow("Merged Contours", visual_bR);}int remainNum = 0;//剩余的轮廓数std::vector<cv::Rect> boundRect;for (int i = 0; i < mergedContours.size(); i++){std::vector<cv::Point> curContours = mergedContours.at(i);if (curContours.size() < 40) continue;boundRect.push_back(cv::boundingRect(curContours));if (debug) cv::rectangle(visual_bR, boundRect[boundRect.size() - 1].tl(), boundRect[boundRect.size() - 1].br(), cv::Scalar(0, 255, 0), 1);if (debug) cv::putText(visual_bR, std::to_string(boundRect.size() - 1), boundRect[boundRect.size() - 1].tl(), cv::FONT_HERSHEY_COMPLEX, 0.45, cv::Scalar(255, 135, 160), 1);if (debug) cv::putText(visual_bR, std::to_string(boundRect.size() - 1), boundRect[boundRect.size() - 1].br(), cv::FONT_HERSHEY_COMPLEX, 0.45, cv::Scalar(255, 135, 160), 1);//if (debug) cv::drawContours(visual_bR, curContours, i, cv::Scalar(255, 135, 160), -1, CV_AA);if (boundRect.size() - 1 >= 0) {cv::Rect curBR = boundRect.at(boundRect.size() - 1);double whRatio = curBR.width*1.0 / curBR.height;//宽高比double full = curContours.size()*1.0 / (curBR.width*curBR.height);if (debug) std::cout << "--- curBR_" << boundRect.size() - 1 << curBR << whRatio << "  \tfull=" << full << std::endl;//if (whRatio > 1) continue;//宽高比不满足要求的直接 continue//if (curBR.width > imgOriginal.cols / 3) continue;//if (curBR.height > imgOriginal.rows / 3) continue;if (curBR.width  < 150) continue;//if (curBR.height > 150) continue;}cv::RotatedRect curMinRect = cv::minAreaRect(curContours);float longerSide = curMinRect.size.width > curMinRect.size.height ? curMinRect.size.width : curMinRect.size.height;float shorterSide = curMinRect.size.width < curMinRect.size.height ? curMinRect.size.width : curMinRect.size.height;double lsRatio = longerSide * 1.0 / shorterSide;//长宽比if (debug) {cv::Point2f vertices[4];curMinRect.points(vertices);for (int i = 0; i < 4; i++)line(visual_bR, vertices[i], vertices[(i + 1) % 4], cv::Scalar(80, 175, 210), 2);if (debug) cv::putText(visual_bR, std::to_string(i), vertices[1], cv::FONT_HERSHEY_COMPLEX, 0.45, cv::Scalar(80, 175, 210), 1);if (debug) cv::putText(visual_bR, std::to_string(i), vertices[3], cv::FONT_HERSHEY_COMPLEX, 0.45, cv::Scalar(80, 175, 210), 1);cv::RotatedRect curMR = curMinRect;if (debug) std::cout << i << "  curMR.angle=" << curMR.angle << " \t, curMR.center=" << curMR.center << "\t, curMR.size=" << curMR.size << lsRatio << std::endl;}if (lsRatio < 6.4) continue;//if (lsRatio > 2.5) continue;remainNum++;cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));cv::drawContours(visual_bR, mergedContours, i, color, -1, CV_AA);//cv::drawContours(visual_bR, mergedContours, i, cv::Scalar(255, 255, 255), -1, CV_AA); //用全黑色填充contour_end.push_back(curContours);bound_Rect_end.push_back(boundRect[boundRect.size() - 1]);}if (debug) cv::putText(visual_bR, std::to_string(remainNum), cv::Point(visual_bR.cols / 3, visual_bR.rows / 3), cv::FONT_HERSHEY_COMPLEX, 1.45, cv::Scalar(80, 75, 210), 1);if (debug) cv::imshow("Merged Contours", visual_bR);//visual_bR.copyTo(canvas);}

91.1 直接根据最小垂直外接矩形框的y合并 mergeOverlappingContoursY

/*
合并 Y方向上有重叠的轮廓
contours 输入的轮廓集合
返回:合并后的轮廓集合*/
std::vector<std::vector<cv::Point>> mergeOverlappingContoursY(const std::vector<std::vector<cv::Point>>& contours) 
{if (contours.empty()) return contours;// 存储轮廓及其边界矩形std::vector<std::pair<cv::Rect, std::vector<cv::Point>>> contourRects;for (const auto& contour : contours) {if (!contour.empty()) {contourRects.emplace_back(cv::boundingRect(contour), contour);}}// 按 Y 坐标排序std::sort(contourRects.begin(), contourRects.end(),[](const std::pair<cv::Rect, std::vector<cv::Point>>& a,const std::pair<cv::Rect, std::vector<cv::Point>>& b) {return a.first.y < b.first.y;});// 合并重叠的轮廓std::vector<std::vector<cv::Point>> mergedContours;for (size_t i = 0; i < contourRects.size(); ++i) {cv::Rect currentRect = contourRects[i].first;std::vector<cv::Point> currentContour = contourRects[i].second;// 检查是否已经处理过if (currentRect.width == 0 && currentRect.height == 0) {continue;}// 尝试合并与当前轮廓在 Y 方向上有重叠的轮廓for (size_t j = i + 1; j < contourRects.size(); ++j) {cv::Rect otherRect = contourRects[j].first;// 跳过已处理的轮廓if (otherRect.width == 0 && otherRect.height == 0) {continue;}// 检查 Y 方向是否有重叠bool yOverlap = (currentRect.y <= otherRect.y + otherRect.height) &&(currentRect.y + currentRect.height >= otherRect.y);if (yOverlap) {// 合并轮廓点currentContour.insert(currentContour.end(),contourRects[j].second.begin(),contourRects[j].second.end());//opencv中以重载运算符:从 按位或 变为 矩形合并currentRect |= otherRect; // 更新当前矩形contourRects[j].first = cv::Rect(0, 0, 0, 0); // 标记已处理}}// 将合并后的轮廓添加到结果中mergedContours.push_back(currentContour);}return mergedContours;
}

91.2 使用并查集合并 Y方向上有重叠的轮廓 mergeOverlappingContoursYOptimized

  1. 计算每个轮廓的边界矩形
  2. 使用并查集数据结构跟踪重叠的轮廓 (对轮廓下标做并查集)
  3. 根据并查集结果合并轮廓 (根据下标判断是否属同一集合)
  4. 返回合并后的轮廓
/*
使用并查集合并 Y方向上有重叠的轮廓
contours 输入的轮廓集合
返回:合并后的轮廓集合*/
std::vector<std::vector<cv::Point>> mergeOverlappingContoursYOptimized(const std::vector<std::vector<cv::Point>>& contours) 
{if (contours.empty()) return contours;// 存储轮廓及其边界矩形std::vector<cv::Rect> rects;for (const auto& contour : contours) {if (!contour.empty()) {rects.push_back(cv::boundingRect(contour));}}// 初始化并查集std::vector<int> parent(rects.size());for (int i = 0; i < parent.size(); ++i) {parent[i] = i;}// 查找函数auto find = [&](int x) {while (parent[x] != x) {parent[x] = parent[parent[x]];  // 路径压缩x = parent[x];}return x;};// 合并函数auto unite = [&](int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {parent[rootY] = rootX;}};// 检查重叠并合并for (size_t i = 0; i < rects.size(); ++i) {for (size_t j = i + 1; j < rects.size(); ++j) {// 检查 Y 方向是否有重叠bool yOverlap = (rects[i].y <= rects[j].y + rects[j].height) &&(rects[i].y + rects[i].height >= rects[j].y);if (yOverlap) {unite(i, j);}}}// 根据并查集结果合并轮廓std::vector<std::vector<cv::Point>> mergedContours;std::vector<bool> processed(rects.size(), false); //标记已处理的轮廓for (size_t i = 0; i < rects.size(); ++i) {if (processed[i]) continue;int root = find(i);std::vector<cv::Point> mergedContour;for (size_t j = i; j < rects.size(); ++j) {if (find(j) == root) { //同一集合的轮廓mergedContour.insert(mergedContour.end(),contours[j].begin(),contours[j].end());processed[j] = true; //标记为已处理}}mergedContours.push_back(mergedContour);}return mergedContours;
}

拓展:重叠条件修改

【重叠y超一定比例才合并

// 计算重叠比例
float overlapHeight = std::min(currentRect.y + currentRect.height, otherRect.y + otherRect.height) - std::max(currentRect.y, otherRect.y);
float minHeight = std::min(currentRect.height, otherRect.height);
float overlapRatio = overlapHeight / minHeight;// 只有当重叠比例超过阈值时才合并
if (yOverlap && overlapRatio > 0.5) {// 合并轮廓
}

【X方向重叠条件

// 添加X方向重叠条件
bool xOverlap = (rects[i].x <= rects[j].x + rects[j].width) &&(rects[i].x + rects[i].width >= rects[j].x);

【重叠面积阈值

// 添加重叠面积阈值
float overlapArea = calculateOverlapArea(rects[i], rects[j]);
float minOverlapRatio = 0.2; // 20%重叠
if (yOverlap && overlapArea / std::min(rects[i].area(), rects[j].area()) > minOverlapRatio) {unite(i, j);
}

92、


文章转载自:

http://IGU7V4x1.sfcfy.cn
http://ps1srMWT.sfcfy.cn
http://bXU5mhlc.sfcfy.cn
http://l8HyzRiH.sfcfy.cn
http://Xg6qSdW5.sfcfy.cn
http://YjR8UYCt.sfcfy.cn
http://QFVZ16L4.sfcfy.cn
http://YQM3oJA9.sfcfy.cn
http://mcuL1kDb.sfcfy.cn
http://3LCzvuhG.sfcfy.cn
http://1EzvWSVx.sfcfy.cn
http://i3vpwuWk.sfcfy.cn
http://sXWkdY8m.sfcfy.cn
http://VLAzdhTA.sfcfy.cn
http://EBgjLsGF.sfcfy.cn
http://VaPOwRfh.sfcfy.cn
http://wYGmJK5p.sfcfy.cn
http://YO0QolWI.sfcfy.cn
http://9khY6S7t.sfcfy.cn
http://oGc9coyQ.sfcfy.cn
http://Eo4Vx4BU.sfcfy.cn
http://vuel6t3g.sfcfy.cn
http://v83zwljD.sfcfy.cn
http://MMpsFHrR.sfcfy.cn
http://vjDnizTn.sfcfy.cn
http://wHK3Rknt.sfcfy.cn
http://KCk63dMq.sfcfy.cn
http://27ZvvROV.sfcfy.cn
http://XrFa7QW3.sfcfy.cn
http://TofpBbS2.sfcfy.cn
http://www.dtcms.com/a/375676.html

相关文章:

  • leetcode-python-1941检查是否所有字符出现次数相同
  • python内存分析memory_profiler简单应用
  • 9.9 json-server
  • excel中筛选条件,数字筛选和文本筛选相互转换
  • zsh: no matches found: /Users/xxx/.ssh/id_rsa*
  • 【EPGF 白皮书】路径治理驱动的多版本 Python 架构—— Windows 环境治理与 AI 教学开发体系
  • C语言面向对象编程:模拟实现封装、继承、多态
  • 设计 模式
  • 【Scientific Data 】紫茎泽兰的染色体水平基因组组装
  • MVCC-多版本并发控制
  • 【MybatisPlus】SpringBoot3整合MybatisPlus
  • 如何在FastAPI中玩转“时光倒流”的数据库事务回滚测试?
  • MySQL数据库面试题整理
  • PostgreSQL 大对象管理指南:pg_largeobject 从原理到实践
  • 传统项目管理的局限性有哪些
  • 内核函数:copy_process
  • 《UE5_C++多人TPS完整教程》学习笔记50 ——《P51 多人游戏中的俯仰角(Pitch in Multiplayer)》
  • RL【5】:Monte Carlo Learning
  • 深度解析HTTPS:从加密原理到SSL/TLS的演进之路
  • minio 文件批量下载
  • 【算法专题训练】19、哈希表
  • AJAX入门-URL、参数查询、案例查询
  • 安装ultralytics
  • Eino ChatModel 组件指南摘要
  • 腾讯codebuddy-cli重磅上线-国内首家支持全形态AI编程工具!
  • 基于PCL(Point Cloud Library)的点云高效处理方法
  • UVa1302/LA2417 Gnome Tetravex
  • STC Link1D电脑端口无法识别之升级固件
  • 【C++】LLVM-mingw + VSCode:Windows 开发攻略
  • SRM系统有哪些核心功能?企业该如何科学选型?