Qt輪廓分析設計+算法+避坑

輪廓分析擬合方面我現在只考慮矩形擬合和圓形擬合

細分的話,橢圓擬合,矩形擬合,最小外接矩形,最小外接圓。

對于一張圖像可能有不同的圖形,不同的圓,不同的矩形,我需要對其進行篩選,也需要對檢測的目標對針對性計算,例如面積,周長,圓心點坐標。

也需要對篩選的模塊進行進一步檢索,例如都是圓,兩個大一個小,我就需要通過篩選他們的面積來確定我具體要哪一個圓。

那最后就是顯示功能了,因為輪廓分析往往是基于圖像處理后顯示的,這樣不夠直觀也不夠美觀,我們需要讓他顯示在原圖上,我們可以選擇它顯示的方式,最小圓,最小矩形,十字線,中心點這樣會好看很多。

我們整體的頁面思路已經設計好了下面看一下實現

一.界面方面

從頭再過一下加強Qt的控件記憶

設計輪廓分析的主界面設計,采用垂直排序的方式,

setContentsMargins(10, 10, 10, 10):設置布局的內邊距為 10 像素,即布局內容與面板邊緣之間保持 10 像素的空白距離

setSpacing(15):設置布局中各個子控件之間的垂直間距為 15 像素

建立一個名字為計算分析的QGroupBox控件,它內部的控制方式QGridLayout,也就是網格布局。之后再建立一個QLabe將他方式到 計算分析的組里面這樣可以通過QGridLayout的方式來控制它的布局。

perimeterCheckBox->setChecked(true);  // 選中"周長"復選框
areaCheckBox->setChecked(true);       // 選中"面積"復選框

默認選中狀態

之后就是對控件進行排序看一下效果

還是比較美觀的之后進行下一步擬合分析組的設計和上面的設計是一樣的可以看一下

后面的過濾設置有一些不一樣我么看一下,首先是一個范圍值,要有最大和最小來將其圈起來

還是建立一個QGroupBox將將我需要的組件放進去

建立Qlabel來放置周長的最大值最小值篩選,顯示最小值

QDoubleSpinBox?是 Qt 提供的一個數值輸入控件,允許用戶通過上下箭頭或直接輸入來選擇一個帶小數的數值(即雙精度浮點數)

以此建立我們需要的控制數值的變量

我們看一下效果

最后就是我們的顯示和前面也是一樣的,直接看效果

最后我們還需要一個輪廓信息幫我們顯性的顯示每一個找到的輪廓的面積信息

要顯示

之后算法方面

二.算法方面

cv::findContours(InputArray image,              // 輸入圖像(通常為二值圖)OutputArrayOfArrays contours,  // 檢測到的輪廓點集OutputArray hierarchy,         // 輪廓的層級關系int mode,                      // 輪廓檢索模式int method,                    // 輪廓點近似方法Point offset = Point()         // 可選的偏移量
);

比較基礎的APi主要檢測圖像的外輪廓,后面會更新增加內部輪廓,如孔洞

下面是遍歷整個關鍵點存儲容器以此取出點位等價

for (size_t i = 0; i < contours.size(); ++i) {const std::vector<cv::Point>& contour = contours[i];// ...
}

這里有一個知識點就是contour到底是什么,里面包含了什么?

在 OpenCV 中,contour?是一個存儲輪廓點集的數據結構,本質是?std::vector<cv::Point>(或?std::vector<cv::Point2f>),表示由一系列連續點構成的曲線。

// 示例:contour 的類型定義
using Contour = std::vector<cv::Point>;  // 二維點集
struct Point {int x;  // x坐標int y;  // y坐標
};
  • 存儲形式
    contour?是有序的點序列,相鄰點之間用直線連接,形成閉合或開放的曲線。
    例如,一個矩形輪廓可能存儲為四個頂點的坐標:
    [(x1,y1), (x2,y2), (x3,y3), (x4,y4)]

根據這個特性我們就可以求一些關鍵信息,例如周長面積矩,重心等信息

double perimeter = cv::arcLength(contour, true);    // 周長
double area = cv::contourArea(contour);             // 面積
cv::Moments moments = cv::moments(contour);         // 矩(用于計算重心等)
cv::Point2f centroid(moments.m10/moments.m00, moments.m01/moments.m00);  // 重心

1.形狀分析

bool isConvex = cv::isContourConvex(contour);      // 是否為凸多邊形
std::vector<cv::Point> approx;
cv::approxPolyDP(contour, approx, epsilon, true);  // 多邊形逼近
double circularity = 4 * CV_PI * area / (perimeter * perimeter);  // 圓形度

2.邊界框計算

cv::Rect boundingRect = cv::boundingRect(contour);  // 直立外接矩形
cv::RotatedRect minRect = cv::minAreaRect(contour); // 最小外接矩形(帶旋轉)
cv::Point2f center; float radius;
cv::minEnclosingCircle(contour, center, radius);    // 最小外接圓

3.繪制與可視化

// 繪制輪廓
cv::drawContours(image, std::vector<std::vector<cv::Point>>{contour}, -1, color, thickness);// 繪制關鍵點
for (const auto& point : contour) {cv::circle(image, point, 2, cv::Scalar(0, 255, 0), -1);
}

我們先通過這個contour來獲得我們需要的信息

之后通過篩選來選擇我們要的圖像輪廓

這里有一個坑要>=不能是==?==?要求參數精確等于某個值,在現實中幾乎不可能滿足;而?>=?和?<=?允許參數在合理范圍內波動,更符合實際情況。

之后實時繪制輪廓,上面的篩選輪廓用于后續需要點位信息的時候調用

輪廓信息有了我們開始記錄信息便于輪廓可視化處理,我們需要建立一個結構體用來存儲信息

2.1幾個關鍵代碼解釋:

1. 計算并存儲輪廓質心
cv::Moments m = cv::moments(contour);
if (m.m00 != 0) {info.center = cv::Point2f(m.m10 / m.m00, m.m01 / m.m00);
}

  • cv::moments(contour):計算輪廓的幾何矩,包括面積矩m00(即輪廓面積)、一階矩m10m01
  • 質心公式
    • 質心橫坐標 =?m10 / m00
    • 質心縱坐標 =?m01 / m00
  • 條件檢查:當輪廓面積m.m00為 0(如空輪廓或單點)時,跳過賦值以避免除零錯誤。此時info.center的值保持未初始化(潛在風險!)。

2. 計算并存儲輪廓凸度

info.convexity = cv::isContourConvex(contour) ? 1.0 : 0.0;

  • cv::isContourConvex(contour):判斷輪廓是否為凸多邊形(所有內角 ≤ 180°)。
  • 凸度值
    • 1.0:輪廓是凸的。
    • 0.0:輪廓是非凸的(有凹陷)。

3. 計算并存儲最小外接圓

cv::minEnclosingCircle(contour, info.enclosingCircleCenter, info.enclosingCircleRadius);

  • cv::minEnclosingCircle():計算完全包含輪廓的最小圓。
  • 輸出參數
    • info.enclosingCircleCenter:圓心坐標(cv::Point2f)。
    • info.enclosingCircleRadius:圓半徑(float)。

4. 計算并存儲最小外接矩形的角度

cv::RotatedRect minRect = cv::minAreaRect(contour);
info.angle = minRect.angle;

  • cv::minAreaRect(contour):計算完全包含輪廓的最小旋轉矩形(可能傾斜)。
  • minRect.angle:返回矩形的旋轉角度(單位:度),范圍為[-90, 0)。角度定義為水平軸與矩形短邊的夾角。

5. 將結構體添加到列表

contourInfoList.push_back(info);

  • 將當前輪廓的所有特征信息存入容器contourInfoList

輪廓分析的核心代碼可以用與下面的兩種方式。1:當我點擊輪廓信息的時候將信息以彈窗的方式體現出來。2:將這些信息以繪圖的方式畫出來。

三.彈窗方面

構建ContourInfoDialog這個構造函數

  • 這個類借助構造函數接收一個輪廓信息列表和一個可選的父窗口部件。
  • 父窗口部件的初始化工作由 Qt 框架處理,通常是通過調用基類的構造函數來實現。
  • 析構函數會確保在對象被銷毀時,所有資源都能被正確釋放。

類的內部實現

1.構建窗口的格式

2.窗口內表格構建

void ContourInfoDialog::initUI(const std::vector<ContourInfo>& contourInfoList) {QVBoxLayout* mainLayout = new QVBoxLayout(this);// 創建表格m_infoTable = new QTableWidget(static_cast<int>(contourInfoList.size()), 10, this);m_infoTable->setHorizontalHeaderLabels({"ID", "位置(X,Y)", "尺寸(W,H)", "周長", "面積","圓度", "凸度", "橫縱比", "外接圓(半徑)", "角度"});// 設置表頭樣式m_infoTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);m_infoTable->setColumnWidth(0, 50);   // IDm_infoTable->setColumnWidth(1, 120);  // 位置m_infoTable->setColumnWidth(2, 120);  // 尺寸m_infoTable->setColumnWidth(3, 80);   // 周長m_infoTable->setColumnWidth(4, 80);   // 面積m_infoTable->setColumnWidth(5, 60);   // 圓度m_infoTable->setColumnWidth(6, 60);   // 凸度m_infoTable->setColumnWidth(7, 60);   // 橫縱比m_infoTable->setColumnWidth(8, 100);  // 外接圓m_infoTable->setColumnWidth(9, 60);   // 角度// 填充數據for (size_t i = 0; i < contourInfoList.size(); ++i) {const auto& info = contourInfoList[i];m_infoTable->setItem(static_cast<int>(i), 0, new QTableWidgetItem(QString::number(i + 1)));m_infoTable->setItem(static_cast<int>(i), 1, new QTableWidgetItem(QString("(%1, %2)").arg(info.center.x).arg(info.center.y)));m_infoTable->setItem(static_cast<int>(i), 2, new QTableWidgetItem(QString("%1 x %2").arg(info.boundingRect.width).arg(info.boundingRect.height)));m_infoTable->setItem(static_cast<int>(i), 3, new QTableWidgetItem(QString::number(info.perimeter, 'f', 1)));m_infoTable->setItem(static_cast<int>(i), 4, new QTableWidgetItem(QString::number(info.area, 'f', 1)));// 計算圓度 = 4π*面積/周長2double circularity = 4 * CV_PI * info.area / (info.perimeter * info.perimeter);m_infoTable->setItem(static_cast<int>(i), 5, new QTableWidgetItem(QString::number(circularity, 'f', 3)));m_infoTable->setItem(static_cast<int>(i), 6, new QTableWidgetItem(info.convexity > 0.5 ? "是" : "否"));m_infoTable->setItem(static_cast<int>(i), 7, new QTableWidgetItem(QString::number(info.aspectRatio, 'f', 2)));m_infoTable->setItem(static_cast<int>(i), 8, new QTableWidgetItem(QString("R:%1").arg(info.enclosingCircleRadius, 0, 'f', 1)));m_infoTable->setItem(static_cast<int>(i), 9, new QTableWidgetItem(QString::number(info.angle, 'f', 1) + "°"));}mainLayout->addWidget(m_infoTable);// 添加關閉按鈕QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this);connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);mainLayout->addWidget(buttonBox);
}

這樣我們就能完成想要的尋找輪廓的操作

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914444.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914444.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914444.shtml

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

相關文章

C++中STL六大組件List的簡單介紹

一、前言C非常重視效率&#xff0c;對效率有損失的代碼常常是能省則省。使用list要包含的頭文件是<list>&#xff0c;要包含頭文件就是#iinclude <list>&#xff0c;List肯定是一種鏈表&#xff0c;我們不妨回憶一下那種鏈表插入刪除效率最快也就是最簡單&#xff…

第十五節:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入門 - vue前端 生產部署

Vben Admin vben5 系列文章目錄 ?? 基礎篇 ? 第一節:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入門 ? 第二節:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入門 - Python Flask 后端開發詳解(附源碼) ? 第三節:Vben Admin 最新 v5.0 (vben5) + Python …

背包初步(0-1背包、完全背包)

當月光灑在我的臉上 我想我就快變了模樣 有一種叫做撕心裂肺的湯 喝了它有神奇的力量 動態規劃初步&#xff08;完全背包&#xff09; 目錄動態規劃初步&#xff08;完全背包&#xff09;0-1背包簡介完全背包檢查數組是否存在有效劃分&#xff08;前綴劃分DP&#xff09;單詞拆…

Linux驅動06 --- UDP

目錄 一、UDP 1.1 介紹 1.2 UDP 的通信方式 1.3 單播 發送函數 接收函數 1.4 廣播 1.5 組播/多播 一、UDP 1.1 介紹 傳輸層的另外一個協議 面向無連接&#xff0c;不穩定&#xff0c;速度快&#xff0c;可以一對多 UDP&#xff08;User Datagram Protocol&…

AJAX 投票:技術解析與應用場景

AJAX 投票:技術解析與應用場景 引言 隨著互聯網技術的不斷發展,Web應用的用戶體驗越來越受到重視。AJAX(Asynchronous JavaScript and XML)作為一種重要的技術,在實現異步數據交互方面發揮著關鍵作用。本文將深入探討AJAX投票系統的技術原理、應用場景以及優化策略。 A…

【字節跳動】數據挖掘面試題0017:推薦算法:雙塔模型,怎么把內容精準地推送給用戶

文章大綱 雙塔模型:推薦算法中的“高效匹配引擎一、雙塔模型的核心思想:“分而治之” 的匹配邏輯二、雙塔模型的結構:從特征輸入到相似度輸出1. 輸入層:特征的 “原材料處理”2. 塔網絡層:用戶與物品的“個性化編碼”3. 交互層:向量相似度的“偏好打分”三、雙塔模型的優…

7月14日日記

數學類今天考完最后一科英語放假回家了。有點羨慕他們。今天英語成績出來了&#xff0c;我是89分&#xff0c;一開始有點失望&#xff0c;感覺沒有上90&#xff0c;這是一個很好的沖擊4.0 的機會。但是后來一想好像也沒什么可惜的&#xff0c;這個分數還是很高的。舍友小林是90…

js的局部變量和全局變量

全局變量常常定義在函數外&#xff0c;具有全局定義域&#xff0c;在整個js代碼的任何地方都可以使用&#xff0c;這個就叫全局變量局部變量定義在函數內部&#xff0c;只在當前函數的定義域可以被使用&#xff0c;而且不同的函數可以定義相同的局部變量&#xff0c;他們之間相…

C++ 多態詳解:從概念到實現原理----《Hello C++ Wrold!》(14)--(C/C++)

文章目錄前言多態的概念多態的定義和實現虛函數虛函數的重寫(覆蓋)多態的構成條件override 和 final&#xff08;C11提出&#xff09;finaloverride重載、覆蓋(重寫)、隱藏(重定義)的對比抽象類接口繼承和實現繼承多態的原理虛函數表(也叫做虛表)引申:虛表的打印多態的原理靜態…

Node.js + Express的數據庫AB View切換方案設計

方案總覽數據導入過程&#xff1a; - 根據控制表判斷當前活躍組&#xff08;假設當前活躍的是a&#xff0c;那么接下來要導入到b&#xff09;。 - 清空非活躍表&#xff08;即b表&#xff09;的數據&#xff0c;然后將新數據導入到b表。 - 切換控制表&#xff0c;將活…

C++_編程提升_temaplate模板_案例

類模板案例案例描述: 實現一個通用的數組類&#xff0c;要求如下&#xff1a;可以對內置數據類型以及自定義數據類型的數據進行存儲將數組中的數據存儲到堆區構造函數中可以傳入數組的容量提供對應的拷貝構造函數以及operator防止淺拷貝問題提供尾插法和尾刪法對數組中的數據進…

Win11系統安裝Anaconda環境極簡教程

Win11系統安裝Anaconda環境極簡教程 &#x1f4e5; 第一步&#xff1a;下載 Anaconda 安裝包 打開瀏覽器&#xff0c;訪問 Anaconda 官網&#xff0c;選擇View All Installers 選擇所需版本&#xff08;此文以2024.02-1為例&#xff09;&#xff0c;點擊進行下載&#xff08;…

Datawhale AI夏令營-基于帶貨視頻評論的用戶洞察挑戰賽

一.賽事目標基于星火大模型Spark 4.0 Ultra&#xff0c;對視頻和評論的數據進行商品識別&#xff0c;情感分析&#xff0c;歸類分析&#xff0c;最終為帶貨效果進行評價。并通過優化模型來提高評價準確度二.賽事環境1.基礎平臺&#xff1a;星火大模型Spark 4.0 Ultra2.賽事數據…

如何基于FFMPEG 實現視頻推拉流

文章目錄 前言環境準備為什么選擇 FFmpeg什么是nginx 1.7.11.3 GryphonNginx的conf配置啟動nginx推流命令接收視頻流Untiy播放視頻流最后前言 我們經常會有在電腦上實現推拉流的需求,Unity 和Unreal 都提供了基于WebRTC 的視頻流方案,效果還不錯,但是當我們需要推拉整個電腦…

飛算JavaAI:從情緒價值到代碼革命,智能合并項目與定制化開發新范式

目錄一、飛算 JavaAI 是什么&#xff1f;二、飛算JavaAI&#xff1a;安裝登錄2.1 IDEA插件市場安裝&#xff08;推薦&#xff09;2.2 離線安裝包三、飛算JavaAI核心功能&#xff1a;一鍵生成完整工程代碼功能背景3.1 理解需求3.2 設計接口3.3 表結構自動設計3.4 處理邏輯&#…

Python 基礎語法與數據類型(十一) - 類 (class) 與對象 (實例)

文章目錄1. 什么是類 (Class)&#xff1f;1.1 定義一個類2. 什么是對象 (Object) 或實例 (Instance)&#xff1f;2.1 創建對象&#xff08;實例化&#xff09;3. 訪問屬性和調用方法4. 類屬性 vs 實例屬性5. self 的重要性總結練習題練習題答案前幾篇文章我們學習了變量、數據類…

精準數據檢索+數據飛輪自驅優化,彩訊AI知識庫助力企業知識賦能和效率創新

近兩年&#xff0c;人工智能技術的精細化發展&#xff0c;讓知識庫概念重新成為“熱門詞匯”&#xff0c;騰訊ima等智能工作臺產品為個人用戶打造專屬知識庫&#xff0c;而面向B端市場&#xff0c;企業AI知識庫也逐步成為企業集中存儲與管理核心文檔、數據、經驗和流程的知識中…

打破空間邊界!Nas-Cab用模塊化設計重構個人存儲邏輯

文章目錄前言1. Windows安裝Nas-Cab2. 本地局域網連接Nas-Cab3. 安裝Cpolar內網穿透4. 固定Nas-Cab 公網地址"數據管理不該受制于硬件形態或地理邊界。這個開源方案證明&#xff1a;當功能模塊化且可擴展時&#xff0c;私有云可以像水一樣滲透進所有設備——現在就去Git倉…

Sigma-Aldrich細胞培養基礎知識:細胞培養的安全注意事項

細胞培養實驗室風險評估風險評估的主要目的是防止人員受傷&#xff0c;保護財產&#xff0c;并避免對個人和環境的傷害。在許多國家&#xff0c;法律要求進行風險評估。例如&#xff0c;英國的《英國職業健康與安全法案&#xff08;1974年&#xff09;》就是一個例子。歐洲共同…

Imx6ull用網線與電腦連接

理解工作方式沒有路由器時&#xff0c;可以使用&#xff0c;只要保持虛擬機的兩個網卡一個與電腦在同一網,一個與板子在同一網段(保持通信)就可以從虛擬機往板子下載第一步&#xff1a;查看電腦連接的網絡這一步是在找到主機ip地址這兩步在其他同類教程里一樣的第二步:設置以太…