OpenCV 图像处理 轮廓检测基本原理
文章目录
- 基本原理
- 关键函数和参数
- 注意事项
- 示例代码
- 示例效果
- 代码详解
- findContours 函数原型
- findContours函数变体
基本原理
轮廓发现是图像处理中的一个重要步骤,用于检测物体的边界和形状。
-
图像预处理:
轮廓发现通常在灰度图像上进行。因此,首先将图像转换为灰度图像。接下来,应用滤波器来减少噪声。常用的滤波器有高斯模糊(Gaussian Blur),它有助于平滑图像并减少噪声。 -
边缘检测:
在预处理后的图像上应用边缘检测算法。常用的边缘检测算法是Canny边缘检测器,它能有效地检测出图像中的边缘。Canny边缘检测器使用梯度的方向和幅度来找到图像中的边缘。 -
轮廓提取:
一旦得到二值化的边缘图像,就可以使用OpenCV的findContours函数来提取轮廓。findContours函数将图像中的每一个边缘视为一个轮廓,并返回一个轮廓列表。每个轮廓都由一系列点组成,这些点定义了轮廓的形状。 -
轮廓的层次结构:
findContours函数不仅可以返回轮廓,还可以返回轮廓的层次结构。这对于包含内嵌轮廓(如嵌套在其他轮廓中的孔洞)的图像非常有用。层次结构信息存储了每个轮廓的父子关系。
关键函数和参数
-
cv2.findContours(image, mode, method):image: 输入的二值图像(通常是边缘检测的结果)。mode: 轮廓检索模式,如cv2.RETR_EXTERNAL(只检测外轮廓)、cv2.RETR_TREE(检测所有轮廓并构建层次结构)。method: 轮廓逼近方法,如cv2.CHAIN_APPROX_SIMPLE(只保存轮廓的必要点)、cv2.CHAIN_APPROX_NONE(保存所有轮廓点)。
-
cv2.drawContours(image, contours, contourIdx, color, thickness):
用于在图像上绘制轮廓。
注意事项
-
图像的预处理:
轮廓发现对输入图像的质量非常敏感。良好的预处理(如去噪、对比度增强等)可以显著提高轮廓检测的效果。 -
边缘检测器的选择:
边缘检测器的参数(如Canny边缘检测器的阈值)需要根据图像的特征进行调整。 -
轮廓的近似和表示:
对于复杂的形状,可以使用多边形逼近(如Douglas-Peucker算法)来简化轮廓。
轮廓发现技术广泛应用于对象检测、形状分析、图像分割等领域。在这些应用中,轮廓的精确提取和表示对于后续处理和分析至关重要。
示例代码
在OpenCV中,使用C++进行轮廓发现通常包括以下主要步骤:读取图像、灰度化、边缘检测、轮廓发现和绘制轮廓。以下是一个基本的C++代码示例,展示如何使用OpenCV进行这些操作:
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {// 读取图像Mat src = imread("image.jpg");if (src.empty()) {cout << "无法加载图像!" << endl;return -1;}// 转换为灰度图像Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);// 应用高斯模糊以去除噪声Mat blurred;GaussianBlur(gray, blurred, Size(5, 5), 1.5);// 进行Canny边缘检测Mat edges;Canny(blurred, edges, 100, 200);// 发现轮廓vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);// 在原图上绘制轮廓Mat drawing = Mat::zeros(edges.size(), CV_8UC3);for (size_t i = 0; i < contours.size(); i++) {Scalar color = Scalar(255, 0, 0); // 轮廓的颜色drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0);}// 显示结果imshow("轮廓", drawing);waitKey(0);return 0;
}
示例效果

代码详解
-
读取图像:
Mat src = imread("image.jpg");使用
imread函数加载图像。 -
灰度化:
Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY);使用
cvtColor函数将彩色图像转换为灰度图像。 -
高斯模糊:
Mat blurred; GaussianBlur(gray, blurred, Size(5, 5), 1.5);使用
GaussianBlur函数对灰度图像进行平滑处理,以减少噪声。 -
Canny边缘检测:
Mat edges; Canny(blurred, edges, 100, 200);使用
Canny函数进行边缘检测。这里100和200是低和高阈值,用于控制边缘的检测。 -
发现轮廓:
vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);使用
findContours函数提取图像中的轮廓。RETR_TREE参数用于获取轮廓的层次结构,CHAIN_APPROX_SIMPLE用于压缩水平、垂直和对角直线段,只保留它们的终点。 -
绘制轮廓:
Mat drawing = Mat::zeros(edges.size(), CV_8UC3); for (size_t i = 0; i < contours.size(); i++) {Scalar color = Scalar(255, 0, 0); // 轮廓的颜色drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0); }使用
drawContours函数在图像上绘制检测到的轮廓。 -
显示结果:
imshow("轮廓", drawing); waitKey(0);使用
imshow函数显示绘制好的图像,并使用waitKey等待用户按键。
在实际应用中,可以根据具体需求调整模糊参数、Canny边缘检测的阈值,以及findContours的模式和方法参数。
findContours 函数原型
OpenCV 中的 findContours 函数用于检测图像中的轮廓。其函数原型如下:
void findContours(InputOutputArray image,OutputArrayOfArrays contours,OutputArray hierarchy,int mode,int method,Point offset = Point()
);
参数详解
-
image:
InputOutputArray- 输入图像,通常为二值化图像(如通过边缘检测得到的图像)。该图像会被修改,因此如果需要保留原图像,应该传递其副本。
- 类型通常为
CV_8UC1,即单通道8位无符号整数。
-
contours:
OutputArrayOfArrays- 检测到的轮廓列表,每个轮廓是一个点的向量(即
std::vector<Point>)。每个点表示轮廓的一部分。 - 具体类型为
std::vector<std::vector<Point>>。
- 检测到的轮廓列表,每个轮廓是一个点的向量(即
-
hierarchy:
OutputArray- 可选的层次结构输出向量。对于每个轮廓,
hierarchy[i][0]表示下一个轮廓的索引,hierarchy[i][1]表示前一个轮廓的索引,hierarchy[i][2]表示第一个子轮廓的索引,hierarchy[i][3]表示父轮廓的索引。 - 如果不需要层次结构,可以传递
noArray()或一个空的Mat。
- 可选的层次结构输出向量。对于每个轮廓,
-
mode:
int- 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值有:
RETR_EXTERNAL: 只检索最外层的轮廓。RETR_LIST: 检索所有轮廓,不建立层次关系。RETR_CCOMP: 检索所有轮廓,组织为两级结构:顶层是连通分量的外边界,次层是孔的边界。RETR_TREE: 检索所有轮廓,并重建完整的嵌套轮廓。
- 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值有:
-
method:
int- 轮廓逼近方法,指定如何对轮廓点进行存储。可选值有:
CHAIN_APPROX_NONE: 存储所有的轮廓点。CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留这些线段的终点。CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chin 链逼近算法。
- 轮廓逼近方法,指定如何对轮廓点进行存储。可选值有:
-
offset:
Point(默认值为Point())- 偏移量,用于所有轮廓点坐标的偏移。这在对图像中的ROI区域进行轮廓检测时尤其有用。
findContours函数变体
它不要求输出层次结构的层次信息。这种简化版的函数原型对于只关心检测到的轮廓而不需要它们之间的层次结构关系的情况是有用的。其具体定义如下:
CV_EXPORTS void findContours(InputArray image,OutputArrayOfArrays contours,int mode,int method,Point offset = Point()
);
参数详解
- image:
InputArray- 输入图像,通常是一个二值图像(如通过边缘检测得到的图像)。该图像会被修改,因此如果需要保留原图像,应该传递其副本。
- 类型通常为
CV_8UC1,即单通道8位无符号整数。
- contours:
OutputArrayOfArrays- 检测到的轮廓列表,每个轮廓是一个点的向量(即
std::vector<Point>)。每个点表示轮廓的一部分。 - 具体类型为
std::vector<std::vector<Point>>。
- 检测到的轮廓列表,每个轮廓是一个点的向量(即
- mode:
int- 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值包括:
RETR_EXTERNAL: 只检索最外层的轮廓。RETR_LIST: 检索所有轮廓,不建立层次关系。RETR_CCOMP: 检索所有轮廓,组织为两级结构:顶层是连通分量的外边界,次层是孔的边界。RETR_TREE: 检索所有轮廓,并重建完整的嵌套轮廓。
- 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值包括:
- method:
int- 轮廓逼近方法,指定如何对轮廓点进行存储。可选值包括:
CHAIN_APPROX_NONE: 存储所有的轮廓点。CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留这些线段的终点。CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chin 链逼近算法。
- 轮廓逼近方法,指定如何对轮廓点进行存储。可选值包括:
- offset:
Point(默认值为Point())- 偏移量,用于所有轮廓点坐标的偏移。这在对图像中的ROI(感兴趣区域)进行轮廓检测时尤其有用。
使用场景
这个版本的findContours函数适用于简单的轮廓检测任务,尤其是当不需要关心轮廓之间的层次关系时。例如,在一些形状分析或对象检测的应用中,只需要获取所有的轮廓而不关心它们的嵌套关系,此时可以使用这个简化的函数原型。
相关文章:
OpenCV 图像处理 轮廓检测基本原理
文章目录 基本原理关键函数和参数注意事项 示例代码示例效果代码详解findContours 函数原型findContours函数变体 基本原理 轮廓发现是图像处理中的一个重要步骤,用于检测物体的边界和形状。 图像预处理: 轮廓发现通常在灰度图像上进行。因此࿰…...
C 语言动态顺序表
test.h #ifndef _TEST_H #define _TEST_H #include <stdio.h> #include <stdlib.h> #include <string.h>typedef int data_type;// 定义顺序表结构体 typedef struct List{data_type *data; // 顺序表数据int size; // 顺序表当前长度int count; // 顺序表容…...
擅于辩论的人可以将黑的说成白的,但是存在无法解决的矛盾
擅于辩论的人有能力通过逻辑、证据和修辞等手段,巧妙地引导听众接受与事实相反的观点。 然而,这并不意味着擅于辩论的人就能将任何事物都颠倒黑白。辩论的基础是事实和逻辑,即使是最优秀的辩手,也必须遵循这些基本原则。如果某个…...
java的命令执行漏洞揭秘
0x01 前言 在Java中可用于执行系统命令常见的方式有两种,API为:java.lang.Runtime、java.lang.ProcessBuilder 0x02 java.lang.Runtime GetMapping("/runtime/exec")public String CommandExec(String cmd) {Runtime run Runtime.getRunti…...
爬虫中常见的加密算法Base64伪加密,MD5加密【DES/AES/RSA/SHA/HMAC】及其代码实现(一)
目录 基础常识 Base64伪加密 python代码实现 摘要算法 1. MD5 1.1 JavaScript 实现 1.2 Python 实现 2. SHA 2.1 JavaScript 实现 2.2 Python 实现 2.3 sha系列特征 3. HMAC 3.1 JavaScript 实现 3.2 Python 实现 对称加密 一. 常见算法归纳 1. 工作模式归纳 …...
C语言数据在内存中的存储超详解
文章目录 1. 整数在内存中的存储2. 大小端字节序和字节序判断2. 1 什么是大小端?2. 2 为什么会有大小端?2. 3 练习 3. 浮点数在内存中的存储3. 1 一个代码3. 2 浮点数的存储3. 2. 1 浮点数存的过程3. 2. 2 浮点数取的过程3. 3 题目解析 1. 整数在内存中的…...
【大模型】【NL2SQL】基本原理
三个输入: prompt 用户输入 数据库表格等信息 sql 语句...
RK3568平台(显示篇)DRM vop驱动程序分析
一.设备树配置 vopb: vopff900000 {compatible "rockchip,rk3399-vop-big";reg <0x0 0xff900000 0x0 0x2000>, <0x0 0xff902000 0x0 0x1000>;interrupts <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;assigned-clocks <&cru ACLK_VOP0>, &…...
vue3 动态加载组件
//模版调用 <component :is"geticon(item.icon)" />//引入 import { ref, onMounted, markRaw, defineAsyncComponent } from vue;//异步添加icon图标组建 function geticon(params) {const modules import.meta.glob(../components/icons/*.vue);const link …...
Latex on overleaf入门语法
Latex on overleaf入门语法 前言基本结构序言 简单的格式化命令添加注释:%加粗、斜体、下划线有序列表、无序列表 添加图片图片的标题、标签和引用 添加表格一个简单的表格为表格添加边框标题、标签、引用 数学表达式基本的数学命令 基本格式摘要段落、新行章节、分…...
使用Echarts来实现数据可视化
目录 一.什么是ECharts? 二.如何使用Springboot来从后端给Echarts返回响应的数据? eg:折线图: ①Controller层: ②service层: 一.什么是ECharts? ECharts是一款基于JavaScript的数据可视化图标库,提供直观&…...
一文搞懂GIT
文章目录 1. GiT概述1.1 GIT概述1.2 GIT安装 2. GIT组成3. GIT基本命令3.1 基本命令3.2 分支操作3.3 远程操作3.4 标签操作3.5 其他命令 1. GiT概述 1.1 GIT概述 Git 是一个分布式版本控制系统,被广泛应用于软件开发中。 Git 具有众多优点,比如&#…...
jQuery入门(四)案例
jQuery 操作入门案例 一、复选框案例 功能: 列表的全选,反选,全不选功能实现。 实现步骤和分析: - 全选 1. 为全选按钮绑定单击事件。 2. 获取所有的商品项复选框元素,为其添加 checked 属性,属性值为 true。 -…...
揭秘MITM攻击:原理、手法与防范措施
中间人攻击发生时,攻击者会在通讯两端之间插入自己,成为通信链路的一部分。攻击者可以拦截、查看、修改甚至重新定向受害者之间的通信数据,而不被双方察觉。这种攻击常见于未加密的Wi-Fi网络、不安全的HTTP连接或者通过社会工程学手段诱导受害…...
【YOLOv8】一文全解+亮点介绍+训练教程+独家魔改优化技巧
前言 Hello,大家好,我是cv君,最近开始在空闲之余,经常更新文章啦!除目标检测、分类、分隔、姿态估计等任务外,还会涵盖图像增强领域,如超分辨率、画质增强、降噪、夜视增强、去雾去雨、ISP、海…...
创建mvp ubo(uniform buffer object)
创建过程: 创建一个uniform buffer查找buffer memory requirements分配、绑定buffer memorymap buffer memory拷贝mvp data to buffer memoryunmap buffer memory 示例代码: glm::mat4 projection glm::perspective(glm::radians(45.0f), 1.0f, 0.1f…...
1.GPIO
理论说明 输入 上拉输入:拉高电平 下拉输入:拉低电平 浮空输入:不拉高也不拉低电平 输出 开漏输出:不能输出高电平(P-MOS不可用,则只能低电平) 推挽输出:可输出高低电平 输出速率…...
C++必修:STL之vector的了解与使用
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C学习 贝蒂的主页:Betty’s blog 1. C/C中的数组 1.1. C语言中的数组 在 C 语言中,数组是一组相同类型…...
【MySQL】索引 【上】 {没有索引的查询/磁盘/mysql与磁盘IO/初识索引}
文章目录 1.没有索引存在的问题2. 认识磁盘MySQL与存储MySQL与磁盘交互基本单位建立共识图解IO认识索引 在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物…...
GO goroutine状态流转
Gidle -> Grunnable newproc获取新的goroutine,并放置到P运行队列中 这也是go关键字之后实际编译调用的方法 func newproc(fn *funcval) {// 获取当前正在运行中的goroutinegp : getg()// 获取调用者的程序计数器地址,用于调试和跟踪pc : getcallerp…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
