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

OpenCV 图像处理 轮廓检测基本原理

文章目录

    • 基本原理
      • 关键函数和参数
      • 注意事项
    • 示例代码
      • 示例效果
      • 代码详解
      • findContours 函数原型
      • findContours函数变体

基本原理

轮廓发现是图像处理中的一个重要步骤,用于检测物体的边界和形状。

  1. 图像预处理
    轮廓发现通常在灰度图像上进行。因此,首先将图像转换为灰度图像。接下来,应用滤波器来减少噪声。常用的滤波器有高斯模糊(Gaussian Blur),它有助于平滑图像并减少噪声。

  2. 边缘检测
    在预处理后的图像上应用边缘检测算法。常用的边缘检测算法是Canny边缘检测器,它能有效地检测出图像中的边缘。Canny边缘检测器使用梯度的方向和幅度来找到图像中的边缘。

  3. 轮廓提取
    一旦得到二值化的边缘图像,就可以使用OpenCV的findContours函数来提取轮廓。findContours函数将图像中的每一个边缘视为一个轮廓,并返回一个轮廓列表。每个轮廓都由一系列点组成,这些点定义了轮廓的形状。

  4. 轮廓的层次结构
    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)
    用于在图像上绘制轮廓。

注意事项

  1. 图像的预处理
    轮廓发现对输入图像的质量非常敏感。良好的预处理(如去噪、对比度增强等)可以显著提高轮廓检测的效果。

  2. 边缘检测器的选择
    边缘检测器的参数(如Canny边缘检测器的阈值)需要根据图像的特征进行调整。

  3. 轮廓的近似和表示
    对于复杂的形状,可以使用多边形逼近(如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;
}

示例效果

在这里插入图片描述

代码详解

  1. 读取图像

    Mat src = imread("image.jpg");
    

    使用imread函数加载图像。

  2. 灰度化

    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    

    使用cvtColor函数将彩色图像转换为灰度图像。

  3. 高斯模糊

    Mat blurred;
    GaussianBlur(gray, blurred, Size(5, 5), 1.5);
    

    使用GaussianBlur函数对灰度图像进行平滑处理,以减少噪声。

  4. Canny边缘检测

    Mat edges;
    Canny(blurred, edges, 100, 200);
    

    使用Canny函数进行边缘检测。这里100200是低和高阈值,用于控制边缘的检测。

  5. 发现轮廓

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    

    使用findContours函数提取图像中的轮廓。RETR_TREE参数用于获取轮廓的层次结构,CHAIN_APPROX_SIMPLE用于压缩水平、垂直和对角直线段,只保留它们的终点。

  6. 绘制轮廓

    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函数在图像上绘制检测到的轮廓。

  7. 显示结果

    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()
);

参数详解

  1. image: InputOutputArray

    • 输入图像,通常为二值化图像(如通过边缘检测得到的图像)。该图像会被修改,因此如果需要保留原图像,应该传递其副本。
    • 类型通常为 CV_8UC1,即单通道8位无符号整数。
  2. contours: OutputArrayOfArrays

    • 检测到的轮廓列表,每个轮廓是一个点的向量(即 std::vector<Point>)。每个点表示轮廓的一部分。
    • 具体类型为 std::vector<std::vector<Point>>
  3. hierarchy: OutputArray

    • 可选的层次结构输出向量。对于每个轮廓,hierarchy[i][0] 表示下一个轮廓的索引,hierarchy[i][1] 表示前一个轮廓的索引,hierarchy[i][2] 表示第一个子轮廓的索引,hierarchy[i][3] 表示父轮廓的索引。
    • 如果不需要层次结构,可以传递 noArray() 或一个空的 Mat
  4. mode: int

    • 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值有:
      • RETR_EXTERNAL: 只检索最外层的轮廓。
      • RETR_LIST: 检索所有轮廓,不建立层次关系。
      • RETR_CCOMP: 检索所有轮廓,组织为两级结构:顶层是连通分量的外边界,次层是孔的边界。
      • RETR_TREE: 检索所有轮廓,并重建完整的嵌套轮廓。
  5. method: int

    • 轮廓逼近方法,指定如何对轮廓点进行存储。可选值有:
      • CHAIN_APPROX_NONE: 存储所有的轮廓点。
      • CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留这些线段的终点。
      • CHAIN_APPROX_TC89_L1CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chin 链逼近算法。
  6. offset: Point (默认值为 Point())

    • 偏移量,用于所有轮廓点坐标的偏移。这在对图像中的ROI区域进行轮廓检测时尤其有用。

findContours函数变体

它不要求输出层次结构的层次信息。这种简化版的函数原型对于只关心检测到的轮廓而不需要它们之间的层次结构关系的情况是有用的。其具体定义如下:

CV_EXPORTS void findContours(InputArray image,OutputArrayOfArrays contours,int mode,int method,Point offset = Point()
);

参数详解

  1. image: InputArray
    • 输入图像,通常是一个二值图像(如通过边缘检测得到的图像)。该图像会被修改,因此如果需要保留原图像,应该传递其副本。
    • 类型通常为 CV_8UC1,即单通道8位无符号整数。
  2. contours: OutputArrayOfArrays
    • 检测到的轮廓列表,每个轮廓是一个点的向量(即 std::vector<Point>)。每个点表示轮廓的一部分。
    • 具体类型为 std::vector<std::vector<Point>>
  3. mode: int
    • 轮廓检索模式,决定如何提取轮廓以及如何处理它们之间的关系。可选值包括:
      • RETR_EXTERNAL: 只检索最外层的轮廓。
      • RETR_LIST: 检索所有轮廓,不建立层次关系。
      • RETR_CCOMP: 检索所有轮廓,组织为两级结构:顶层是连通分量的外边界,次层是孔的边界。
      • RETR_TREE: 检索所有轮廓,并重建完整的嵌套轮廓。
  4. method: int
    • 轮廓逼近方法,指定如何对轮廓点进行存储。可选值包括:
      • CHAIN_APPROX_NONE: 存储所有的轮廓点。
      • CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留这些线段的终点。
      • CHAIN_APPROX_TC89_L1CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chin 链逼近算法。
  5. offset: Point (默认值为 Point())
    • 偏移量,用于所有轮廓点坐标的偏移。这在对图像中的ROI(感兴趣区域)进行轮廓检测时尤其有用。

使用场景

这个版本的findContours函数适用于简单的轮廓检测任务,尤其是当不需要关心轮廓之间的层次关系时。例如,在一些形状分析或对象检测的应用中,只需要获取所有的轮廓而不关心它们的嵌套关系,此时可以使用这个简化的函数原型。

相关文章:

OpenCV 图像处理 轮廓检测基本原理

文章目录 基本原理关键函数和参数注意事项 示例代码示例效果代码详解findContours 函数原型findContours函数变体 基本原理 轮廓发现是图像处理中的一个重要步骤&#xff0c;用于检测物体的边界和形状。 图像预处理&#xff1a; 轮廓发现通常在灰度图像上进行。因此&#xff0…...

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; // 顺序表容…...

擅于辩论的人可以将黑的说成白的,但是存在无法解决的矛盾

擅于辩论的人有能力通过逻辑、证据和修辞等手段&#xff0c;巧妙地引导听众接受与事实相反的观点。 然而&#xff0c;这并不意味着擅于辩论的人就能将任何事物都颠倒黑白。辩论的基础是事实和逻辑&#xff0c;即使是最优秀的辩手&#xff0c;也必须遵循这些基本原则。如果某个…...

java的命令执行漏洞揭秘

0x01 前言 在Java中可用于执行系统命令常见的方式有两种&#xff0c;API为&#xff1a;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 什么是大小端&#xff1f;2. 2 为什么会有大小端&#xff1f;2. 3 练习 3. 浮点数在内存中的存储3. 1 一个代码3. 2 浮点数的存储3. 2. 1 浮点数存的过程3. 2. 2 浮点数取的过程3. 3 题目解析 1. 整数在内存中的…...

【大模型】【NL2SQL】基本原理

三个输入&#xff1a; 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入门语法 前言基本结构序言 简单的格式化命令添加注释&#xff1a;%加粗、斜体、下划线有序列表、无序列表 添加图片图片的标题、标签和引用 添加表格一个简单的表格为表格添加边框标题、标签、引用 数学表达式基本的数学命令 基本格式摘要段落、新行章节、分…...

使用Echarts来实现数据可视化

目录 一.什么是ECharts? 二.如何使用Springboot来从后端给Echarts返回响应的数据&#xff1f; eg:折线图&#xff1a; ①Controller层&#xff1a; ②service层&#xff1a; 一.什么是ECharts? ECharts是一款基于JavaScript的数据可视化图标库&#xff0c;提供直观&…...

一文搞懂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 是一个分布式版本控制系统&#xff0c;被广泛应用于软件开发中。 Git 具有众多优点&#xff0c;比如&#…...

jQuery入门(四)案例

jQuery 操作入门案例 一、复选框案例 功能: 列表的全选&#xff0c;反选&#xff0c;全不选功能实现。 实现步骤和分析&#xff1a; - 全选 1. 为全选按钮绑定单击事件。 2. 获取所有的商品项复选框元素&#xff0c;为其添加 checked 属性&#xff0c;属性值为 true。 -…...

揭秘MITM攻击:原理、手法与防范措施

中间人攻击发生时&#xff0c;攻击者会在通讯两端之间插入自己&#xff0c;成为通信链路的一部分。攻击者可以拦截、查看、修改甚至重新定向受害者之间的通信数据&#xff0c;而不被双方察觉。这种攻击常见于未加密的Wi-Fi网络、不安全的HTTP连接或者通过社会工程学手段诱导受害…...

【YOLOv8】一文全解+亮点介绍+训练教程+独家魔改优化技巧

前言 Hello&#xff0c;大家好&#xff0c;我是cv君&#xff0c;最近开始在空闲之余&#xff0c;经常更新文章啦&#xff01;除目标检测、分类、分隔、姿态估计等任务外&#xff0c;还会涵盖图像增强领域&#xff0c;如超分辨率、画质增强、降噪、夜视增强、去雾去雨、ISP、海…...

创建mvp ubo(uniform buffer object)

创建过程&#xff1a; 创建一个uniform buffer查找buffer memory requirements分配、绑定buffer memorymap buffer memory拷贝mvp data to buffer memoryunmap buffer memory 示例代码&#xff1a; glm::mat4 projection glm::perspective(glm::radians(45.0f), 1.0f, 0.1f…...

1.GPIO

理论说明 输入 上拉输入&#xff1a;拉高电平 下拉输入&#xff1a;拉低电平 浮空输入&#xff1a;不拉高也不拉低电平 输出 开漏输出&#xff1a;不能输出高电平&#xff08;P-MOS不可用&#xff0c;则只能低电平&#xff09; 推挽输出&#xff1a;可输出高低电平 输出速率…...

C++必修:STL之vector的了解与使用

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. C/C中的数组 1.1. C语言中的数组 在 C 语言中&#xff0c;数组是一组相同类型…...

【MySQL】索引 【上】 {没有索引的查询/磁盘/mysql与磁盘IO/初识索引}

文章目录 1.没有索引存在的问题2. 认识磁盘MySQL与存储MySQL与磁盘交互基本单位建立共识图解IO认识索引 在关系数据库中&#xff0c;索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构&#xff0c;它是某个表中一列或若干列值的集合和相应的指向表中物…...

GO goroutine状态流转

Gidle -> Grunnable newproc获取新的goroutine&#xff0c;并放置到P运行队列中 这也是go关键字之后实际编译调用的方法 func newproc(fn *funcval) {// 获取当前正在运行中的goroutinegp : getg()// 获取调用者的程序计数器地址&#xff0c;用于调试和跟踪pc : getcallerp…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...