OpenCV中的图像处理3.9(七)轮廓线及其层次结构

news2024/11/27 8:26:05

目录

    • 3.9.4 轮廓线:更多的功能
      • 目标
      • 理论和代码
      • 练习
    • 3.9.5 轮廓线层次结构
      • 目标
      • 理论
      • 什么是层次结构?
      • OpenCV中的层次结构表示法
      • 轮廓线检索模式

翻译及二次校对:cvtutorials.com

编辑者:廿瓶鲸(和鲸社区Siby团队成员)

3.9.4 轮廓线:更多的功能

目标

在本章中,我们将了解到:

1.凸性缺陷以及如何找到它们。
2.寻找从一个点到一个多边形的最短距离
3.匹配不同的形状

理论和代码

1.凸性缺陷

我们在第二章关于轮廓的内容中看到了什么是凸面体。任何偏离这个凸包的物体都可以被认为是凸性缺陷。

OpenCV提供了一个现成的函数来寻找这个缺陷,即cv.convexityDefects()。一个基本的函数调用看起来如下。

hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)

注意:请记住,我们在寻找凸面体时必须传递returnPoints = False,以便找到凸性缺陷。

它返回一个数组,每一行都包含这些值 - [ 起始点,终点,最远点,到最远点的大致距离 ]。我们可以用一个图像将其可视化。我们画一条连接起点和终点的线,然后在最远点画一个圆。请记住,前三个返回值是cnt的索引。所以我们必须从cnt中获取这些值。

import cv2 as cv
import numpy as np
img = cv.imread('star.jpg')
img_gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,thresh = cv.threshold(img_gray, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt = contours[0]
hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)
for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv.line(img,start,end,[0,255,0],2)
    cv.circle(img,far,5,[0,0,255],-1)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

然后看看结果:

Image Name

2.点多边形测试

这个函数找出图像中的一个点和一个轮廓线之间的最短距离。它返回的距离是:当点在轮廓线外时为负数,当点在轮廓线内时为正数,如果点在轮廓线上则为零。

例如,我们可以检查点(50,50),如下所示。

dist = cv.pointPolygonTest(cnt,(50,50),True)

在这个函数中,第三个参数是measureDist,如果它是True,它找到有符号的距离。如果是False,它将发现该点是在轮廓线内还是在轮廓线外或在轮廓线上(它分别返回+1、-1、0)。

注意:如果你不想找距离,确保第三个参数是False,因为这是一个耗时的过程。所以,把它设为假值可以使速度提高2-3倍。

3.匹配形状

OpenCV有一个函数cv.matchShapes(),它使我们能够比较两个形状,或两个轮廓,并返回一个显示相似度的指标。结果越低,说明它的匹配度越高。它是根据hu-moment值来计算的。文档中解释了不同的测量方法。

import cv2 as cv
import numpy as np

img1 = cv.imread('star.jpg',0)
img2 = cv.imread('star2.jpg',0)

ret, thresh = cv.threshold(img1, 127, 255,0)
ret, thresh2 = cv.threshold(img2, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv.findContours(thresh2,2,1)
cnt2 = contours[0]

ret = cv.matchShapes(cnt1,cnt2,1,0.0)
print( ret )

我试着用下面给出的不同形状来匹配形状。

Image Name

我得到了以下结果:

  • 匹配图像A与自身=0.0
  • 图像A与图像B的匹配=0.001946
  • 图片A与图片C的匹配度=0.326911

看,即使是图像旋转也不会对这个比较产生什么影响。

注意:Hu-Moments是七个对平移、旋转和缩放不变的矩。第七个是歪斜不变的。这些值可以通过cv.HuMoments()函数找到。

练习

1.查看cv.pointPolygonTest()的文档,你可以找到一个红蓝相间的漂亮图像。它代表了所有像素到上面的白色曲线的距离。曲线内的所有像素都是蓝色的,这取决于距离。同样地,外面的点是红色的。轮廓线的边缘用白色标记。所以问题很简单。写一段代码来创建这样的距离表示。
2.用cv.matchShapes()比较数字或字母的图像。( 这将是走向OCR的一个简单步骤)

3.9.5 轮廓线层次结构

目标

这一次,我们学习了轮廓的层次结构,即轮廓的父子关系。

理论

在过去的几篇关于轮廓线的文章中,我们已经使用了OpenCV提供的几个与轮廓线有关的函数。但是当我们使用cv.findContours()函数在图像中找到轮廓时,我们传递了一个参数,即轮廓检索模式。我们通常传递cv.RETR_LIST或cv.RETR_TREE,而且效果不错。但它实际上是什么意思?

另外,在输出中,我们得到了三个数组,第一个是图像,第二个是我们的轮廓,还有一个我们命名为层次的输出(请查看以前文章中的代码)。但我们从未在任何地方使用过这个层次结构。那么这个层次结构是什么,它的作用是什么?它与前面提到的函数参数有什么关系?

这就是我们将在本文中讨论的问题。

什么是层次结构?

通常我们使用cv.findContours()函数来检测图像中的物体,有时物体在不同的位置。但在某些情况下,有些形状是在其他形状里面的。就像嵌套的图形。在这种情况下,我们称外部的为父,内部的为子。这样一来,图像中的轮廓就有了一些相互之间的关系。我们可以指定一个轮廓是如何相互连接的,比如,它是另一个轮廓的孩子,或者它是一个父母等等。这种关系的表现形式被称为层次结构(Hierarchy)。

请看下面的一个例子:

Image Name

在这张图片中,有几个形状,我把它们编号为0-5。2和2a表示最外层盒子的外部和内部轮廓线。

这里,0,1,2是外部或最外层的轮廓。我们可以说,它们是在层次结构0中,或者简单地说,它们是在同一层次中。

接下来是轮廓2a。它可以被认为是轮廓线2的孩子(或者反过来说,轮廓线2是轮廓线2a的父母)。因此,让它在层次结构1中。同样地,轮廓3是轮廓2的孩子,它属于下一个层次。最后,轮廓线4、5是轮廓线3a的子女,它们位于最后一个层次。从我给盒子编号的方式来看,我认为轮廓线4是轮廓线3a的第一个孩子(也可以是轮廓线5)。

我提到这些东西是为了理解像同一层次结构水平、外部轮廓、子轮廓、父轮廓、第一子轮廓等术语。现在让我们来了解一下OpenCV。

OpenCV中的层次结构表示法

所以每个轮廓都有自己的信息,关于它是什么层次,谁是它的孩子,谁是它的父母等等。OpenCV将其表示为一个由四个值组成的数组。[下一个,上一个,第一个孩子,父母] 。

"下一个表示同一层次的下一个轮廓 "。

例如,以我们图片中的轮廓线0为例。谁是它同一层次的下一个轮廓?它就是轮廓1。所以简单地说,Next=1。同样地,对于轮廓1,下一个是轮廓2。所以Next=2。

那么轮廓线2呢?在同一层面上没有下一个轮廓。所以简单地说,把Next = -1。那么轮廓线4呢?它与轮廓线5在同一层次。所以它的下一个轮廓是轮廓5,所以Next = 5。

"上一个表示同一层次的前一个轮廓 "。

这一点与上述相同。轮廓1的上一个轮廓是同一层次的轮廓0。同样,对于轮廓2,它是轮廓1。而对于轮廓线0,没有前一个,所以把它列为-1。

"First_Child表示其第一个子轮廓 "。

不需要任何解释。对于轮廓线2,子线是轮廓线2a。所以它得到轮廓2a的相应索引值。轮廓线3a呢?它有两个孩子。但我们只取第一个孩子。它是轮廓4。所以First_Child = 4为轮廓线3a。

"父代表示其父代轮廓的索引 "。

这与First_Child正好相反。对于轮廓4和轮廓5,父轮廓都是轮廓3a。对轮廓线3a,它是3轮廓线,依此类推。

注意:如果没有子代或父代,该字段将被视为-1。

现在我们知道了OpenCV中使用的层次结构风格,我们可以在上面给出的相同图片的帮助下检查OpenCV中的轮廓检索模式,即像cv.RETR_LIST, cv.RETR_TREE, cv.RETR_CCOMP, cv.RETR_EXTERNAL等标志是什么意思?

轮廓线检索模式

1.RETR_LIST

这是四个标志中最简单的一个(从解释的角度看)。它只是检索所有的轮廓,但不创建任何父子关系。在这个规则下,父母和孩子是平等的,他们只是轮廓,即他们都属于同一层次的水平。

所以在这里,层次结构数组中的第三和第四项总是-1。但是很明显,下一个和上一个项会有其相应的值。你可以自己检查并验证一下。

下面是我得到的结果,每一行都是相应轮廓的层次结构细节。例如,第一行对应的是轮廓线0,下一个轮廓线是轮廓线1,所以Next=1。没有上一个轮廓,所以Previous=-1。而剩下的两个,如前所述,是-1。

>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [ 3,  1, -1, -1],
        [ 4,  2, -1, -1],
        [ 5,  3, -1, -1],
        [ 6,  4, -1, -1],
        [ 7,  5, -1, -1],
        [-1,  6, -1, -1]]])

如果你不使用任何层次结构特征,这是在你的代码中使用的好选择。

2.RETR_EXTERNAL

如果你使用这个标志,它只返回最外部标志。所有child的轮廓都被留下了。我们可以说,在这个法则下,每个家庭中只有最年长的人得到照顾。它并不关心家庭的其他成员。

那么,在我们的图像中,有多少个最外轮廓?即在层次0的水平?只有3个,即0,1,2号轮廓线,对吗?现在试着用这个标志找到这些轮廓。在这里,给每个元素的值也和上面一样。将其与上述结果进行比较。下面是我得到的结果。

>>> hierarchy
array([[[ 1, -1, -1, -1],
        [ 2,  0, -1, -1],
        [-1,  1, -1, -1]]])

如果你想只提取外轮廓,你可以使用这个标志。这在某些情况下可能是有用的。

3.RETR_CCOMP

这个标志检索所有的轮廓线,并将它们排列成一个2级的层次结构。物体内部的孔洞轮廓(如果有的话)被放在层次结构2中。如果有任何物体在里面,它的轮廓又只被放在层次结构1中。而它的洞则放在层次结构2中,以此类推。

只需考虑一个黑色背景上的 "大的白色的零 "的图像。零的外圈属于第一层次,而零的内圈属于第二层次。

我们可以用一个简单的图像来解释它。这里我用红色标出了轮廓的顺序,用绿色标出了它们所属的层次(1或2)。这个顺序与OpenCV检测轮廓的顺序相同。

Image Name

所以考虑第一个轮廓,即轮廓0。它是层次结构1。它有两个洞,轮廓线1&2,它们属于层次结构2。因此,对于轮廓线0,同一层次的下一个轮廓线是轮廓线3。而没有前一个。它的第一个孩子是层次结构2中的轮廓1。它没有父级,因为它是在层次结构1中。所以它的层次结构数组是[3,-1,1,-1] 。

现在取轮廓线1。它是在层次结构2中。在同一层次中的下一个(在轮廓线1的亲属关系下)是轮廓线2。没有前一个。没有子代,但是父代是轮廓线0。所以数组是[2,-1,-1,0]。

同理,轮廓线2:它在层次结构2中。在轮廓0下的同一层次中没有下一个轮廓。所以没有下一个。上一个是轮廓1。没有子代,父代是轮廓0。所以数组是[-1,1,-1,0]。

轮廓3 : 层次结构1中的下一个是轮廓5。上一个是轮廓线0。子代是轮廓线4,没有父代。所以数组是[5,0,4,-1]。

轮廓4 : 它在层次结构2中位于轮廓3之下,没有兄弟姐妹。所以没有下一个,没有上一个,没有子代,父代是轮廓3。所以数组是[-1,-1,-1,3]。

剩下的你可以填满。这就是我得到的最终答案。

>>> hierarchy
array([[[ 3, -1,  1, -1],
        [ 2, -1, -1,  0],
        [-1,  1, -1,  0],
        [ 5,  0,  4, -1],
        [-1, -1, -1,  3],
        [ 7,  3,  6, -1],
        [-1, -1, -1,  5],
        [ 8,  5, -1, -1],
        [-1,  7, -1, -1]]])

4.RETR_TREE

RETR_TREE检索了所有的轮廓线,并创建了一个完整的家庭层次列表。它甚至可以告诉你,谁是爷爷、父亲、儿子、孙子,甚至更多…😃。

例如,我取了上面的图片,重写了cv.RETR_TREE的代码,按照OpenCV给出的结果重新排列了轮廓线,并进行了分析。同样,红色的字母给出了轮廓线的编号,绿色的字母给出了层次的顺序。

Image Name

以0号轮廓线为例:它在第0层。同一层次中的下一个轮廓是轮廓7。没有前一个轮廓线。子代是轮廓线1。也没有父代。所以数组是[7,-1,1,-1]。

拿轮廓线2来说:它在层次结构1中。在同一层次中没有轮廓线。没有前一个。子代是轮廓线3。父代是轮廓线1。所以数组是[-1,-1,3,1]。

剩下的,自己试试。下面是完整的答案。

>>> hierarchy
array([[[ 7, -1,  1, -1],
        [-1, -1,  2,  0],
        [-1, -1,  3,  1],
        [-1, -1,  4,  2],
        [-1, -1,  5,  3],
        [ 6, -1, -1,  4],
        [-1,  5, -1,  4],
        [ 8,  0, -1, -1],
        [-1,  7, -1, -1]]])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/529103.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pyqt5经典案例学习

目录 前言一、图表主题动画1-1、效果展示1-2、代码分段解析1-2-1、导库1-2-2、初始化1-2-3、数据生成函数以及信号与槽的连接 总结 前言 案例来源于GitHub项目《各种各样的PyQt测试和例子》 一、图表主题动画 1-1、效果展示 功能: 支持不同的主题和动画效果。用户…

Linux环境下的VScode使用教程

前言 (1)对于学习本文需要先有自行安装好VMware,对VMware有简单的了解。 (2)对于绝大多数使用Linux的人而言,经常在Windows环境下使用source insight进行编译程序,然后利用FileZilla将Windows的…

Ansys仿真寄生参数对信号反射的影响

1、短桩线传输线的反射 短桩线在PCB走线时会经常遇到,这个桩线会对信号的传输产生反射,那么桩线的长度和信号反射的关系可以仿真看一下,电路如下图所示,下图中,我们设置信号源的上升下降时间为0.8ns,桩线的…

Auto GPT 与 ChatGPT:有何区别?

人工智能正在迅速发展,即使是最熟练的人也越来越难以跟上。每隔一段时间,就会出现新的 AI 工具,在这些工具中,有些是时尚的,有些是真正有价值的。 Auto-GPT 是一种建立在 ChatGPT 技术之上的人工智能,很可…

leaflet根据坐标点设置多边形,生成geojson文件,计算面积值(133)

第133个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中根据坐标点设置多边形,通过.toGeoJSON() 来生成geojson文件,通过turf.area来计算面积值。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共123…

浅谈Redis7基础命令

Redis基本命令 Ping命令 心跳命令 set & get命令 set key value get key select命令 切换数据库 dbsize命令 flushdb命令 清除当前数据库数据 flushell命令 清除所有数据库数据 退出命令 quit、exit 关闭redis redis-cli shutdown shutdown 查看进程及端口号 #查看…

VirtualBox ping和xshell切换

使用virtualbox有一个奇怪的现象,每次连接xshell的时候ping百度,ping不通,能ping通的时候又连接不了xsell。 两种模式来回切换每次都要查要修改的东西,太麻烦了,就在这记录一下。 连接xshell 选中想要链接xshell的虚…

python3 安装 bz2包

python3 安装 bz2包 错误解决方法1、pip 安装2、重装python3(网上推荐,但没尝试)3、补全缺少文件(博主采用) 错误 ModuleNotFoundError: No module named _bz2 解决方法 1、pip 安装 尝试 pip3 install bz2 发现并…

一篇文章搞定《Android中的ANR》

------《ANR》 什么是ANR举个例子帮你认识ANRANR的产生原因ANR的监控手段方法一: 监控trace文件夹方法二:利用我们主线程的Looper方法三:监控SIGQUIT信号 ANR日志Traces.txtTraces文件分析几个分析案例:一、好定位的问题(简单案例…

使用FastGithub解决国内访问GitHub失败的问题

问题提出 在ArduinoIDE安装开发板开发包、库时经常由于这些包和库的索引指向的是github下面的开源项目,所以安装失败的可能性极高。开启了FastGithub后,更新成功率和速度都快了很多! 问题解决 使用FastGithub开源工具(支持Win,Ma…

在北京,36岁的软件测试人的心路历程及学习经验,太现实了

前言 涛哥(我认识的一位朋友,也是我的前辈)出身普通,仅仅是一个普通二本学校毕业,大学也是混日子混过去的,年轻时没有好好学习,被美女、游戏吸引到迷了自我,他对那年毕业的记忆清晰…

jpa使用

jpa: java persistence api jpa只要一对多 在一对多的关系中,一般将 JoinColumn 放在 多端,, 如果不需要两边都映射对应的实体,,可以在一端设置JoinColumn ,并在多端指定一个外键属性来映射这个关系

农业病虫虚拟仿真教学平台使实验资源共享

动物直肠检查是一项常见的内窥镜检查手术,往往会因为实验设备、实验动物、时间、经费等方面的因素影响,使一些应该开设的手术教学开展较少或者无法进行,造成学生或从业人士对专业知识掌握以及动手实践能力的不足 因此对于新手的培训必须经过大…

MISC:HTTP 流量分析技术.

MISC:HTTP 流量分析技术. Misc即杂项,是信息隐藏又称信息伪装,就是通过减少载体的某种冗余,如空间冗余、数据冗余等,来隐藏敏感信息,达到某种特殊的目的。 信息隐藏打破了传统密码学的思维范畴&#xff0c…

短期光伏发电量短期预测(Python代码,主要模型LSTM)

1.数据集(68779条数据) 开始时间 DATE_TIMEPLANT_IDSOURCE_KEYDC_POWERAC_POWERDAILY_YIELDTOTAL_YIELD15-05-2020 00:0041350011BY6WEcLGh8j5v7000625955915-05-2020 00:0041350011IF53ai7Xc0U56Y000618364515-05-2020 00:0041350013PZuoBAID5Wc2HD00…

你真的会写接口自动化测试脚本?0-1精通自动化测试实战,暴涨18K...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 开头,…

【Linux进阶命令 01】grep(文本的全局搜索与打印)

文章目录 一、grep命令(全局搜索与打印)1.1 语法1.2 主要参数1.3 测试准备1.4 grep命令使用示例1.5 应用示例 一、grep命令(全局搜索与打印) grep (缩写来自Globally search a Regular Expression and Print&#xff0…

一文解决eBpf在Android上的集成和调试

eBPF(Extended Berkeley Packet Filter )是一种新兴的linux内核功能扩展技术,可以无需修改内核代码,在保证安全的前提下,灵活的动态加载程序,实现对内核功能的扩展。 Android平台上也引入了对eBpf技术的支…

NC 人力薪酬管理薪资发放流程粗略整理

NC 人力薪酬管理薪资发放流程: 1、【公共薪资项目-集团/组织】节点新增公共新增项目 2、【薪资期间-集团/组织】节点设置薪资期间 3、根据公司实际情况,如果需要,则在【薪资标准设置-集团/组织】、【薪资规则-集团/组织】两个节点设置薪资标准和薪资规则 4、在【税率表-集…

【类和对象(下)】

文章目录 🍕前言一、🍕再谈构造函数1.1构造函数体赋值1.2初始化列表1.3explicit关键字 二、🍕static成员三、🍕友元四、🍕内部类五、🍕匿名对象六、🍕拷贝对象时编译器的一些优化七、&#x1f3…