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

OpenCV 图像轮廓查找与绘制全攻略:从函数使用到实战应用详解

在这里插入图片描述

摘要:本文详细介绍了 OpenCV 中用于查找图像轮廓的 cv2.findContours() 函数以及绘制轮廓的 cv2.drawContours() 函数的使用方法。涵盖 cv2.findContours() 各参数(如 mode 不同取值对应不同轮廓检索模式)及返回值的详细解析,搭配多幅示例图片与丰富代码示例展示不同模式下的效果差异,同时展示了 cv2.drawContours() 的用法,并通过案例讲解如何利用轮廓绘制功能获取前景对象,助力读者全面掌握图像轮廓相关操作要点及应用场景。
如果您觉得我的文章对您有帮助,可以点赞收藏关注,持续学习更多与OpenCV相关的知识

OpenCV 图像轮廓查找与绘制全攻略:从函数使用到实战应用详解

  • 查找并绘制轮廓
    • cv2.findContours()函数的使用
      • 返回值countours的属性
      • 参数mode与返回值hierarchy
    • cv2.drawContours()函数的使用
      • 利用轮廓绘制功能,获取前景对象
  • 致谢

查找并绘制轮廓

边缘检测能检测出边缘,但是边缘不连续,但不是一个整体。OpenCV提供了cv2.findContours()去查找图像轮廓,cv2.drawContours()将轮廓绘制起来。

cv2.findContours()函数的使用

该函数的语法如下:
image与被处理图像一致,contours返回的轮廓 ,hierarchy 图像的拓普信息(轮廓层次) = cv2.findContours(image原始图像,mode轮廓检索模式,method轮廓的近似方法)
这里有一个补充:当OpenCV版本的大于4.x,返回值没有image
我通过画图板画了一个例子,存储在和代码同一个文件夹下,大家可以复制我的图片,去作为练习,(补充一个重要的前提:OpenCV中,都是从黑色背景中找白色对象,对象是白的,背景是黑的,图片必须是灰度二值图像,对于彩色图像要做好阈值处理):
在这里插入图片描述
我将这张图片命名为countours.JPG,这是我的文件夹(我用的是 jupyter notebook 大家用pycharm的就把代码和图片放在同一个文件夹下就可以运行我的代码了):
在这里插入图片描述

返回值countours的属性

他的类型是list:

import numpy as np
import cv2
img = cv2.imread("countours.JPG",cv2.IMREAD_GRAYSCALE)
countours , hierarchy = cv2.findContours(img,mode = cv2.RETR_EXTERNAL,method = cv2.CHAIN_APPROX_NONE)
print(type(countours))

在这里插入图片描述
每一条轮廓的shape与内容都用列表的方法访问:
一定要注意 必须先用高斯滤波进行平滑处理去除噪声 再用二值化阈值处理,才能计算出正确的轮廓

import numpy as np
import cv2
img = cv2.imread("countours.JPG",cv2.IMREAD_GRAYSCALE)# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours , hierarchy = cv2.findContours(img,mode = cv2.RETR_EXTERNAL,method = cv2.CHAIN_APPROX_NONE)
print(len(countours))
for i in range(len(countours)):print(countours[i].shape)print(countours[i])

在这里插入图片描述
通过代码找出来十条轮廓,与我们手动标注的数量一致:
在这里插入图片描述

参数mode与返回值hierarchy

我将这张图片命名为mode.JPG,这是我的文件夹(我用的是 jupyter notebook 大家用pycharm的就把代码和图片放在同一个文件夹下就可以运行我的代码了):
在这里插入图片描述
在这里插入图片描述
不同的mode参数对应着不同的hierarchy,分为四种:
第一种:cv2.RETR_EXTERNAL(只检测外轮廓)

import numpy as np
import cv2
img = cv2.imread("mode.JPG",cv2.IMREAD_GRAYSCALE)# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours , hierarchy = cv2.findContours(img,mode = cv2.RETR_EXTERNAL,method = cv2.CHAIN_APPROX_NONE)
print(len(countours))
print(hierarchy)

输出值[1 -1 -1 -1]代表 第0个轮廓的后一个轮廓是第一个轮廓,他没有前一个轮廓 所以第二个元素是-1 他没有子轮廓 和父轮廓 所以第三第四个元素都是 - 1
输出值[-1 0 -1 -1]代表第1个轮廓 没有后面轮廓了 所以是-1 他的前一个轮廓 是0,他没有子轮廓和父轮廓 所以第三个第四个元素都是 -1
在这里插入图片描述
找到了两条轮廓,原因是cv2.RETR_EXTERNAL会导致只搜索外轮廓:
在这里插入图片描述
第二种:cv2.RETR_LIST
检测到的轮廓不考虑父子关系
在上面的代码中修改mode = cv2.RETR_LIST,运行结果如下:
在这里插入图片描述
这个结果前两行的第一个元素都是 1 2 说明他们有后一个轮廓 ,因为没有考虑父子关系,所以每一行的三四元素都是-1 第1 第2 轮廓都有前一个轮廓 分别是 0 和1 ,所以第二列 是 -1 0 1
第三种:cv2.RETR_CCOMP
检查所有轮廓并组织称一个两级层次结构,修改参数mode = cv2.RETR_CCOMP,继续运行得到如下结果:
在这里插入图片描述
可以看到第1 轮廓 和 第2 轮廓存在了 父子 关系 :
在这里插入图片描述
对于1,2轮廓来说他们是父子关系,所以不存在后一个轮廓所以是-1:
在这里插入图片描述
第四种:cv2.RETR_TREE
生成一个等级树,为了验证第四个和第三个的区别,我重新用画图板画了一张图起名为tree.JPG,大家可以复制到自己的文件夹下:
在这里插入图片描述

import numpy as np
import cv2
img = cv2.imread("tree.JPG",cv2.IMREAD_GRAYSCALE)
# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours_comp , hierarchy_comp = cv2.findContours(img,mode = cv2.RETR_CCOMP,method = cv2.CHAIN_APPROX_NONE)
countours_tree , hierarchy_tree = cv2.findContours(img,mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_NONE)
print(hierarchy_comp)
print(hierarchy_tree)

详细分析一下,运行结果:
在这里插入图片描述
显而易见的是 cv2.RETR_CCOMP时,最多产生一个两级的父子结构:
在这里插入图片描述
但是cv2.RETR_TREE产生了一个多级的父子结构树:
在这里插入图片描述

cv2.drawContours()函数的使用

语法:
cou_image待绘制的轮廓图像 = cv2.drawCountours(image待处理图像,countours需要绘制的轮廓 list类型,countourIdx绘制的边缘索引,color绘制的颜色,thickness绘制轮廓的粗细,lineType画笔类型,hierarchy拓普信息,maxLevel层次的深度,offset偏移参数,使得轮廓偏移多少位置)
使用tree.JPG做案例来用代码验证一下效果:

import numpy as np
import cv2
img = cv2.imread("tree.JPG",cv2.IMREAD_GRAYSCALE)
# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours_tree , hierarchy_tree = cv2.findContours(img,mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_NONE)n = len(countours_tree)
contourImg = []
cv2.imshow("orig",img)
for i in range(n):temp = np.zeros(img.shape,np.uint8)contourImg.append(temp)contourImg[i] = cv2.drawContours(contourImg[i],countours_tree,i,(255,255,255),5)cv2.imshow(f"contour[ {i}]",contourImg[i])
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

利用轮廓绘制功能,获取前景对象

这部分的案例使用pig.JPG图片,需要的可以直接复制,跟代码放在同一文件夹之下:
在这里插入图片描述
先讲解一下代码的思路,首先转化成灰度值和二值图像,然后获取轮廓,绘制整个轮廓,利用按位与运算与原图像进行按位与运算,提取前景对象。

import numpy as np
import cv2
bgr_img = cv2.imread("pig.JPG")
img = cv2.cvtColor(bgr_img,cv2.COLOR_BGR2GRAY)
# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours_tree , hierarchy_tree = cv2.findContours(img,mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_NONE)
mask = np.zeros(bgr_img.shape,np.uint8)
mask = cv2.drawContours(mask,countours_tree,-1,(255,255,255),-1)
loc = cv2.bitwise_and(mask,bgr_img)
cv2.imshow("mask",mask)
cv2.imshow("loc",loc)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

致谢

本文参考了一些博主的文章,博取了他们的长处,也结合了我的一些经验,对他们表达诚挚的感谢,使我对 图像轮廓查找与绘制 有更深入的了解,也推荐大家去阅读一下他们的文章。纸上学来终觉浅,明知此事要躬行:
python+opencv基础篇——实现提取轮廓

相关文章:

OpenCV 图像轮廓查找与绘制全攻略:从函数使用到实战应用详解

摘要:本文详细介绍了 OpenCV 中用于查找图像轮廓的 cv2.findContours() 函数以及绘制轮廓的 cv2.drawContours() 函数的使用方法。涵盖 cv2.findContours() 各参数(如 mode 不同取值对应不同轮廓检索模式)及返回值的详细解析,搭配…...

电机驱动MCU介绍

电机驱动MCU是一种专为电机控制设计的微控制器单元,它集成了先进的控制算法和高性能的功率输出能力。 电机驱动MCU采用高性能的处理器核心,具有快速的运算速度和丰富的外设接口。它内置了专业的电机控制算法,包括PID控制、FOC(Fi…...

人工智能学习框架详解及代码使用案例

人工智能学习框架详解及代码使用案例 人工智能(AI)学习框架是构建和训练AI模型的基础工具,它们提供了一组预定义的算法、函数和工具,使得开发者能够更快速、更高效地构建AI应用。本文将深入探讨人工智能学习框架的基本概念、分类、优缺点、选择要素以及实际应用,并通过代…...

修改Textview中第一个字的字体,避免某些机型人民币¥不显示

在 Android 中,系统提供了三种常用的字体类型,分别是: Serif(衬线字体): 这种字体有明显的衬线或笔画末端装饰,通常用于印刷品和书籍,给人一种正式和优雅的感觉。示例:Typeface.SERI…...

彻底理解quadtree四叉树、Octree八叉树 —— 点云的空间划分的标准做法

1.参考文章: (1)https://www.zhihu.com/question/25111128 这里面的第一个回答,有一幅图: 只要理解的四叉树的构建,对于八叉树的构建原理类比方法完全一样:对于二维平面内的随机分布的这些点&…...

Python时间序列优化之道滑动与累积窗口的应用技巧

大家好,在时间序列数据处理中,通常会进行滑动窗口计算(rolling)和累积窗口计算(expanding)等操作,以便分析时间序列的变化趋势或累积特征。Pandas提供的rolling和expanding函数提供了简单、高效的实现方式,特别适用于金融、气象、…...

Buffered 和 BuffWrite

Buffered和BuffWrite是Java IO包中的两个类,用于提高IO操作的效率。 Buffered是一个缓冲区类,可以将一个InputStream或者一个Reader包装起来,提供了一定的缓冲区大小,可以一次读取多个字节或字符,减少了读取的次数&am…...

【娱乐项目】基于cnchar库与JavaScript的汉字查询工具

Demo介绍 利用了 cnchar 库来进行汉字相关的信息查询,并展示了汉字的拼音、笔画数、笔画顺序、笔画动画等信息用户输入一个汉字后,点击查询按钮,页面会展示该汉字的拼音、笔画数、笔画顺序,并绘制相应的笔画动画和测试图案 cnchar…...

泷羽sec-蓝队基础之网络七层杀伤链 (下)学习笔记

声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...

FPGA 开发工程师

目录 一、FPGA 开发工程师的薪资待遇 二、FPGA 开发工程师的工作内容 1. 负责嵌入式 FPGA 方案设计,包括仿真、软件编写和调试等工作。 2. 使用工具软件建立 FPGA 综合工程,编写综合策略和时序约束。 3. 进行 FPGA 设计的优化与程序维护&#xff0c…...

【Leetcode 每日一题】3250. 单调数组对的数目 I

问题背景 给你一个长度为 n n n 的 正 整数数组 n u m s nums nums。 如果两个 非负 整数数组 ( a r r 1 , a r r 2 ) (arr_1, arr_2) (arr1​,arr2​) 满足以下条件,我们称它们是 单调 数组对: 两个数组的长度都是 n n n。 a r r 1 arr_1 arr1​ 是…...

较类中的方法和属性比较

在 Python 中,类中有以下几种常见的方法和属性,它们的作用和用法有所不同。以下是详细比较: --- ### **1. 实例方法** - **定义**:使用 def 定义,第一个参数是 self,表示实例对象本身。 - **作用**&#…...

nVisual可视化资源管理工具

nVisual主要功能 支持自定义层次化的场景结构 与物理世界结构一致,从全国到区域、从室外到室内、从机房到设备。 支持自定义多种空间场景 支持图片、CAD、GIS、3D等多种可视化场景搭建。 丰富的模型库 支持图标、机柜、设备、线缆等多种资源对象创建。 资源可…...

自动类型推导(auto 和 decltype)

​​​​​​一、auto关键字 基本概念 在 C 11 中引入了auto关键字用于自动类型推导。它可以让编译器根据变量的初始化表达式自动推断出变量的类型。这在处理复杂的类型,如迭代器、lambda 表达式的类型等情况时非常有用。 使用示例 例如,在迭代器的使用中…...

新型大语言模型的预训练与后训练范式,谷歌的Gemma 2语言模型

前言:大型语言模型(LLMs)的发展历程可以说是非常长,从早期的GPT模型一路走到了今天这些复杂的、公开权重的大型语言模型。最初,LLM的训练过程只关注预训练,但后来逐步扩展到了包括预训练和后训练在内的完整…...

基于投影寻踪博弈论-云模型的滑坡风险评价

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于投影寻踪博弈论-云模型的滑坡风险评价 基于投影寻踪博弈论-云模型的滑坡风险评价是一个复杂而有趣的主题,涉及到博弈论、风险评估和模糊逻辑等领域的交叉应用。这个方法结合了博弈论中的投影寻踪技术…...

WRF-Chem模式安装、环境配置、原理、调试、运行方法;数据准备及相关参数设置方法

大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果,同时气象因素是控制大气污染的关键自然因素。大气污染问题既是局部、当地的,也是区域的,甚至是全球的。本地的污染物排放除了对当地造成严重影响外,同时还会在…...

Spring中每次访问数据库都要创建SqlSession吗?

一、SqlSession是什么二、源码分析1)mybatis获取Mapper流程2)Spring创建Mapper接口的代理对象流程3)MapperFactoryBean#getObject调用时机4)SqlSessionTemplate创建流程5)SqlSessionInterceptor拦截逻辑6)开…...

力扣刷题TOP101:6.BM7 链表中环的入口结点

目录: 目的 思路 复杂度 记忆秘诀 python代码 目的 {1,2},{3,4,5}, 3 是环入口。 思路 这个任务是找到带环链表的环入口。可以看作是上一题龟兔赛跑(Floyd 判圈算法)的延续版:乌龟愤愤不平地举报兔子跑得太快,偷偷…...

浅谈telnet和ping

telnet 和 ping 是网络诊断工具,用于测试网络连接性和故障排查,但它们有不同的用途和功能。以下是它们的主要区别: 1. ping 功能描述 用途:ping 命令用于测试主机与目标地址(IP或域名)之间的连通性。工作…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

Python如何给视频添加音频和字幕

在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

C++使用 new 来创建动态数组

问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...