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_x,?height = max_y - min_y
  • 91.1:簡單直接,但對于大量輪廓可能效率較低(O(n2))

  • 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);
	bool xOverlap = (currentRect.x <= otherRect.x + otherRect.width) &&(currentRect.x + currentRect.width >= otherRect.x);// 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://www.pswp.cn/news/922171.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/922171.shtml
英文地址,請注明出處:http://en.pswp.cn/news/922171.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

numpy數組的升維和降維的方法集錦

為適配計算包對numpy數組的維度要求&#xff0c;對numpy數組進行升維或降維轉化&#xff0c;是非常常見的操作。這里嘗試通過多種方式對numpy數組進行升維或降維。1 數組升維1.1 np.expand_dims在0維升維&#xff0c;示例如下a np.array([1,2,3,4,5]) np.expand_dims(a, axis0…

介紹 Python Elasticsearch Client 的 ES|QL 查詢構建器

作者&#xff1a;來自 Elastic Miguel Grinberg 學習如何使用 ES|QL 查詢構建器&#xff0c;這是一個新的 Python Elasticsearch client 功能&#xff0c;可以更輕松地使用熟悉的 Python 語法構建 ES|QL 查詢。 想要獲得 Elastic 認證嗎&#xff1f;快來了解下一期 Elasticsear…

三坐標測量儀:高精度測量內徑檢測手段及其實際運用

在工業制造領域中&#xff0c;內徑尺寸的精準度直接關系到產品的裝配性能、運行穩定性乃至使用壽命。傳統檢測方法如卡尺、內徑千分尺等難以滿足高精度、復雜結構件的需求。三坐標測量儀技術的出現&#xff0c;打破了這一困境&#xff0c;成為當前工業領域實現高精度內徑檢測的…

DIPMARK:一種隱蔽、高效且具備魯棒性的大語言模型水印技術

摘要水印技術為通過在數據中嵌入隱蔽信息來保障數據安全提供了一種很有前景的方法。該領域的一個首要挑戰在于&#xff0c;在水印嵌入過程中保持原始數據的分布。我們的研究拓展并優化了現有的水印框架&#xff0c;著重強調了保持分布&#xff08;DiP&#xff09;水印的重要性。…

IMU傳感器價格與高精度慣性導航系統供應商分析

本段將對IMU傳感器價格及高精度慣性導航系統的市場情況進行概覽。IMU傳感器作為慣性導航的重要組成部分&#xff0c;其價格水平受到技術、需求和供應商競爭等多重因素的影響。隨著無人機、自動駕駛車輛等新興應用場景的興起&#xff0c;IMU傳感器的市場需求逐漸攀升。這不僅帶動…

3-9〔OSCP ? 研記〕? WEB應用攻擊?利用REST API提權

鄭重聲明&#xff1a; 本文所有安全知識與技術&#xff0c;僅用于探討、研究及學習&#xff0c;嚴禁用于違反國家法律法規的非法活動。對于因不當使用相關內容造成的任何損失或法律責任&#xff0c;本人不承擔任何責任。 如需轉載&#xff0c;請注明出處且不得用于商業盈利。 …

UE5 基礎應用 —— 07 - 角色藍圖 簡單使用

目錄 一、角色藍圖 1.1 Pawn / Character 1.2 角色基類 1.3 角色基類設置 1.3.1 基礎設置 1.3.2 角色移動和相機旋轉 1.3.3 角色移動 —— 鎖定視角 1.3.4 角色跳躍 1.4 角色派生類設置 1.4.1 添加動畫藍圖 一、角色藍圖 1.1 Pawn / Character Pawn / Character 有什…

流暢的Python(二) 豐富的序列

流暢的Python 第二章&#xff1a;豐富的序列 摘要&#xff1a;在日常Python開發中&#xff0c;我們頻繁與各種數據結構打交道&#xff0c;其中序列類型&#xff08;如列表、元組、字符串&#xff09;是基石。然而&#xff0c;你是否曾因對它們理解不深&#xff0c;而在性能優化…

嵌入式 - ARM6

一、按鍵1. 初始化key.c手冊C32 - IOMUXC1. 復用功能配置IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B: 低四位&#xff08;0101&#xff09; IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);SION(信號監控)1: 0 //0 DISABLED — Input Path is determined by functionality MUX_…

菊水PBZ電源在蓄電池充放電測試中的應用探討

通過高速雙極性電源PBZ系列進行蓄電池恒流&#xff0c;恒壓充電的方法 對于儀器廠商來說&#xff0c;要求“請按照使用說明書使用”是產品的使用方針&#xff0c;或者說是正確用法。但是&#xff0c;作為具有代表性的通用產品&#xff0c;直流電源的實際使用方法可謂五花八門&…

Zephyr嵌入式實時操作系統安裝配置

Zephyr簡介 Zephyr 是一款由 Linux 基金會 托管的開源實時操作系統(RTOS),專為資源受限的嵌入式設備(從微控制器到小型邊緣計算節點)設計,廣泛應用于物聯網(IoT)、工業自動化、消費電子、醫療設備、汽車電子等領域。其核心優勢在于輕量級、高可配置性和對多架構硬件的廣…

Linux系統 SELinux 安全管理與故障排查

一、SELinux 安全上下文管理1. SELinux 簡介SELinux&#xff08;Security-Enhanced Linux&#xff09;是 Linux 內核的強制訪問控制&#xff08;MAC&#xff09;安全子系統&#xff0c;通過基于標簽的訪問控制實現細粒度權限管理&#xff0c;遵循最小權限原則。SELinux 有三種工…

解密完全二叉樹順序存儲之堆結構

前言:各位老鐵好&#xff0c;在前面博客中&#xff0c;筆者分享了有關二叉樹的博客&#xff0c;在那篇博客中&#xff0c;筆者講到了完全二叉樹的存儲結構中有兩種存儲方式&#xff0c;一種是順序存儲&#xff0c;一種是鏈式存儲&#xff0c;鏈式存儲筆者已經帶各位老鐵實現過了…

通過針刺!鵬輝能源移動電源電池革新之作 Secu 系列:不燃電解液加持,充電寶安全新選擇

9月11日&#xff0c;鵬輝能源對外發布新一代移動電源高安全電池Secu系列。該產品通過采用不燃的電解液破解移動電源產品安全難題&#xff0c;直擊當下移動電源安全事故頻發的行業痛點&#xff0c;為移動電源行業帶來更安全、更可靠的半固態電池解決方案。數字化時代&#xff0c…

軟件定義汽車(SDV)與區域電子電氣架構(Zonal EEA)的技術革新

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

在 Docker Compose 中解決文件權限不足的問題

在使用 Docker 和 Docker Compose 構建應用時&#xff0c;由于容器中的文件權限不足而導致某些容器可能無法訪問宿主機上的文件&#xff0c;或者容器內的文件系統無法正確讀取或寫入文件。問題描述在我的項目中&#xff0c;我使用 Docker Compose 來啟動多個服務&#xff0c;并…

認知語義學對人工智能自然語言處理的深層語義分析:理論啟示與實踐路徑

摘要隨著人工智能&#xff08;AI&#xff09;技術的飛速發展&#xff0c;自然語言處理&#xff08;NLP&#xff09;已成為其核心驅動力之一。然而&#xff0c;盡管以大型語言模型&#xff08;LLMs&#xff09;為代表的現代NLP系統在處理語言任務上取得了前所未有的成功&#xf…

React19 中的交互操作

需要安裝的庫 antd-mobile、use-immer在App.jsx 中引入組件 Actionimport "./App.css" import Action from "./pages/action" function App() {return (<><Action></Action></>) }export default Appaction.jsx 組件import LearnI…

倉頡編程語言青少年基礎教程:數組類型

倉頡編程語言青少年基礎教程&#xff1a;數組類型 數組本質上是有序、同類型數據的集合容器&#xff0c;其核心作用是高效組織、訪問和處理批量數據&#xff0c;同時結合語言特性&#xff0c;為開發者提供簡潔、高性能的數據管理方式。例如&#xff1a; main() { let v1: …

C++微基礎藍橋杯之旅9.9-9.12

這里主要還是強制類型轉換的使用//打印字符ASCII碼值 //輸入一個除空格以外的可見字符 //輸出其ASCII值--十進制整數 #include <iostream> using namespace std;int main() {char ch;cin >> ch;//字符cout << (int)ch << endl; return 0; }//打印字符…