Python----计算机视觉处理(Opencv:绘制图像轮廓:寻找轮廓,findContours()函数)

ops/2025/3/26 0:45:17/

一、轮廓

        轮廓是图像中目标物体或区域的外部边界线或边界区域,由一系列相连的像素构成封闭形状,代表了物体的基本外形。与边缘不同,轮廓是连续的,而边缘则不一定是连续的。

轮廓与边缘的区别:

        轮廓是一组连续的点或线,而边缘不连续。并且边缘更多的是作为图像的特征使用,比如可以用边缘特征来区分脸和手,而轮廓主要用来分析物体的形态,比如物体的周长、面积等 。

轮廓的作用:

1. 形状分析:通过轮廓,可以分析物体的形状,比如是圆形、矩形还是更复杂的形状。

2. 目标识别:在识别特定物体时,轮廓可以作为物体的一个重要特征。

3. 图像分割:利用轮廓,可以将图像分割成多个区域,每个区域代表一个物体或者物体的一个部分。 

二、寻找轮廓

2.1、 RETR_LIST

        表示列出所有的轮廓。并且在hierarchy里的轮廓关系中,每一个轮廓只有前一条轮廓与后一条轮廓的索 引,而没有父轮廓与子轮廓的索引。

2.2、 RETR_EXTERNAL

        表示只列出最外层的轮廓。并且在hierarchy里的轮廓关系中,每一个轮廓只有前一条轮廓与后一条轮廓 的索引,而没有父轮廓与子轮廓的索引。

2.3、 RETR_CCOMP

        表示列出所有的轮廓。并且在hierarchy里的轮廓关系中,轮廓会按照成对的方式显示。

2.4、 RETR_TREE

        表示列出所有的轮廓。并且在hierarchy里的轮廓关系中,轮廓会按照树的方式显示,其中最外层的轮廓 作为树根,其子轮廓是一个个的树枝。

        除此之外还有method参数,该参数有三个选项:

                CHAIN_APPROX_NONE、

                CHAIN_APPROX_SIMPLE、

                CHAIN_APPROX_TC89_L1。

        其中,CHAIN_APPROX_NONE表示将所有的轮廓点都进行存储;CHAIN_APPROX_SIMPLE表示只存储 有用的点,比如直线只存储起点和终点,四边形只存储四个顶点,默认使用这个方法; CHAIN_APPROX_TC89_L1表示使用Teh-Chin链逼近算法进行轮廓逼近。这种方法使用的是Teh-Chin链 码,它是一种边缘检测算法,可以对轮廓进行逼近,减少轮廓中的冗余点,从而更加准确地表示轮廓的 形状。CHAIN_APPROX_TC89_L1是一种较为精确的轮廓逼近方法,适用于需要较高精度的轮廓表示的情 况。

        对于mode和method这两个参数来说,一般使用RETR_EXTERNAL和CHAIN_APPROX_SIMPLE这两个选 项。

三、findContours()函数

        该函数所依据的算法论文名称是:

Topological structural analysis of digitized binary images by border following.

作者是:Satoshi Suzuki

在介绍该算法的实现步骤之前,需要先介绍一些该论文中所用到的符号:

 

frame:框架,一张图片的最上行、最下行、最左列、最右列。

0-pixel:灰度值为0的像素。(背景)

1-pixel:灰度值为1的像素。默认图像是以0-pixel填充,目标图像为1-pixel。(目标物体)

(i, j):表示图像中第i行,第j列的像素点。

f_{i,j}表示像素点(i, j)的灰度值。那么一张图片就可以表示为F={f_{i,j}}

由1像素组成的连通域称为1-component(1连通域),由0像素组成的连通域称为0-component(0连通域)。如果0连通域S包含了frame,那么称S为background(背景),否则称为孔洞。 

borderpoint(边界点):如果一个1像素周围的8连通区域内有0像素存在,那么这个1像素就是一个边界点。边界是由许多的边界点共同组成的。

环绕连通域:在一个二值图中有两个连通域S1和S2,如果S1中任何一个像素点从任何一个方向(4方向)到达frame的路径上都存在S2的像素点,那么就称为S2环绕S1,如果S1环绕S1且S2和S1之间存在边界点,就称为S2直接环绕S1。

外边界和孔边界:假设现有1连通域S1,0连通域S2,如果S2直接环绕S1,则S2和S1之间的边界称为外边界;如果S1直接环绕S2,则S2和S1之间的边缘称为孔边界。

父边界:假设现有1连通域S1和S3,0连通域S2,S2直接环绕S1,S3直接环绕S2,S1与S2之间的边界为B1,S2与S3之间的边界为B2,则B2为B1的父边界。如果S2是background,那么B1的父边界是frame。

光栅扫描(RasterScan):是指从左往右,由上往下,先扫描完一行,再移至下一行起始位置继续扫描。

NBD:从边界开始点以边界跟踪算法可以得到一条边界,为每条新找到的边界B赋予一个新的唯一的编号,NBD表示当前跟踪的边界的编号。

LNBD:在光栅扫描的过程中,我们也保存最近遇到(上一个)的边界B'的编号,记为LNBD。

经过上面的定义后,对于一个二值化图像来说,可以表示为:

四 、绘制图像轮廓

导入模块

python">import cv2

输入图像

python">img = cv2.imread('img.png')

灰度化

python">img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

二值化

python">ret, img_threshold = cv2.threshold(img_gray, 127, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)

寻找轮廓

python">contours, hierarch = cv2.findContours(img_threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

绘制轮廓

python">img_contours = cv2.drawContours(img, contours, -1, (0, 0, 255), thickness=2)

输出图像

python">cv2.imshow('img', img)
cv2.waitKey(0)

完整代码 

python">import cv2  img = cv2.imread('img.png')  # 将图像从 BGR(蓝绿红)色彩空间转换为灰度。  
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 使用 Otsu 的二值化方法对灰度图像进行二值化处理。  
# ret 返回使用的阈值。  
# img_threshold 是二值化后的图像。  
ret, img_threshold = cv2.threshold(img_gray, 127, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)  # 在二值化图像中寻找轮廓。  
# contours 是一个轮廓列表。  
# hierarch 是轮廓的层级结构。  
contours, hierarch = cv2.findContours(img_threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 在原始图像上绘制轮廓。  
# img 是要绘制轮廓的图像。  
# contours 是要绘制的轮廓列表。  
# -1 表示绘制所有轮廓。  
# (0, 0, 255) 是轮廓的颜色(红色)。  
# thickness=2 是轮廓的粗细。  
img_contours = cv2.drawContours(img, contours, -1, (0, 0, 255), thickness=2)  # 显示带有轮廓的图像。  
cv2.imshow('img', img)  # 等待用户按下按键。  
cv2.waitKey(0)  

 五、库函数

5.1、findContours()

python">cv.findContours(	image, mode, method[, contours[, hierarchy[, offset]]]	) ->	contours, hierarchy
方法描述
image源,一个 8 位单通道图像。非零像素被视为 1。零像素仍为 0,因此图像被视为 binary 。您可以使用 compare、inRange、threshold、adaptiveThreshold、Canny 等创建灰度或彩色图像的二进制图像。如果 mode 等于 RETR_CCOMP 或 RETR_FLOODFILL,则输入也可以是标签的 32 位整数图像 (CV_32SC1)。
contours检测到的轮廓。每个轮廓都存储为点向量(例如 std::vector<std::vector<cv::P oint> >)。
hierarchy可选的输出向量(例如 std::vector<cv::Vec4i>),包含有关图像拓扑的信息。它的单元数与等值线的数量一样多。对于每个第 i 个轮廓轮廓[i],元素 hierarchy[i][0] 、hierarchy[i][1] 、hierarchy[i][2] 和 hierarchy[i][3] 在同一层次结构级别的下一个和上一个轮廓的轮廓中分别设置为从 0 开始的索引,即第一个子轮廓和父轮廓。如果轮廓 i 没有 next、previous 、parent 或 nested 等值线,则 hierarchy[i] 的相应元素将为负数。
mode等值线检索模式,参见 RetrievalModes
method等值线近似方法,请参阅 ContourApproximationModes
offset每个等值线点移动的可选偏移量。如果轮廓是从图像 ROI 中提取的,然后应该在整个图像上下文中对其进行分析,这将非常有用。
RETR_EXTERNAL

Python:cv.RETR_EXTERNAL

仅检索极端外轮廓。它为所有轮廓设置。hierarchy[i][2]=hierarchy[i][3]=-1

RETR_LIST

Python:cv.RETR_LIST

检索所有等值线,而不建立任何分层关系。

RETR_CCOMP

Python:cv.RETR_CCOMP

检索所有等值线并将它们组织到一个两级层次结构中。在顶层,有组件的外部边界。在第二层,有孔的边界。如果已连接零部件的孔内有其他轮廓,则仍将其置于顶层。

RETR_TREE

Python:cv.RETR_TREE

检索所有等值线并重新构建嵌套等值线的完整层次结构。

RETR_FLOODFILL

Python:cv.RETR_FLOODFILL

CHAIN_APPROX_NONE

Python:cv.CHAIN_APPROX_NONE

绝对存储所有等高线点。也就是说,等值线的任意 2 个后续点 (x1,y1) 和 (x2,y2) 将是水平、垂直或对角线相邻点,即 max(abs(x1-x2),abs(y2-y1))==1。

CHAIN_APPROX_SIMPLE

Python:cv.CHAIN_APPROX_SIMPLE

压缩水平、垂直和对角线段,仅保留其端点。例如,一个直立的矩形轮廓用 4 个点编码。

CHAIN_APPROX_TC89_L1

Python:cv.CHAIN_APPROX_TC89_L1

应用了 Teh-Chin 链近似算法的一种风格 [266]

CHAIN_APPROX_TC89_KCOS

Python:cv.CHAIN_APPROX_TC89_KCOS

应用了 Teh-Chin 链近似算法的一种风格 opencv.org/4.11.0/d0/de3/citelist.html#CITEREF_tehchin89" rel="nofollow" title="[266]">[266]

5.2、drawContours()

python">cv.drawContours(	image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]	) ->	image
方法描述
image目标图像。
contours所有输入等值线。每个等值线都存储为点向量。
contourIdx指示要绘制的轮廓的参数。如果为负数,则绘制所有等值线。
color等值线的颜色。
thickness绘制等高线时使用的线条粗细。如果为负数(例如, thickness=FILLED ),则绘制等值线内部
lineType线路连接。请参阅线型
hierarchy有关层次结构的可选信息。仅当您只想绘制部分等值线时才需要它(请参阅 maxLevel )。
maxLevel绘制轮廓的最大级别。如果为 0,则仅绘制指定的轮廓。如果为 1,则函数绘制 contour 和所有嵌套 contour。如果为 2,则函数绘制等值线、所有嵌套等值线、所有嵌套到嵌套的等值线,依此类推。仅当有可用的层次结构时,才会考虑此参数。
offset可选的轮廓偏移参数。将所有绘制的轮廓移动指定

注意

当 thickness=FILLED 时,该函数旨在正确处理具有孔的连通零部件,即使未提供层次结构数据也是如此。这是通过使用奇偶规则一起分析所有轮廓来完成的。如果您有单独检索的轮廓的联合集合,这可能会产生不正确的结果。为了解决这个问题,你需要为每个 contour 的子组单独调用 drawContours,或者使用 contourIdx 参数迭代集合。


http://www.ppmy.cn/ops/169794.html

相关文章

Android Compose 基础布局之 Box 和 Stack 源码深度剖析(九)

Android Compose 基础布局之 Box 和 Stack 源码深度剖析 一、引言 1.1 Android 开发中布局的重要性 在 Android 应用开发里&#xff0c;布局是构建用户界面&#xff08;UI&#xff09;的关键环节。良好的布局设计能够提升用户体验&#xff0c;使应用界面更加美观、易用且具有…

蓝桥备赛(24)算法篇【前缀和】

一、前缀和 前缀和与差分的核心思想是 预处理 &#xff0c; 可以再暴力枚举的过程中 &#xff0c; 快速给出查询结果 &#xff0c; 从而优化时间复杂度。 是经典的用空间替换时间的做法 。 二、 一维前缀和 登录—专业IT笔试面试备考平台_牛客网 注意&#xff1a; 使用前缀和…

Qt的内存管理机制

在Qt中&#xff0c;显式使用new创建的对象通常不需要显式调用delete来释放内存&#xff0c;这是因为Qt提供了一种基于对象树(Object Tree)和父子关系(Parent-Child Relationship)的内存管理机制。这种机制可以自动管理对象的生命周期&#xff0c;确保在适当的时候释放内存&…

网络知识编-数据链路层(以太网 局域网通信 ARP协议 ARP 欺骗 DDos 攻击)

一、认识数据链路层 数据链路层位于物理层和网络层之间&#xff0c;其主要作用是将源自物理层的数据可靠地传输到相邻节点的目标主机的网络层。数据链路层通过物理介质&#xff08;如以太网、Wi-Fi等&#xff09;将数据分割成帧&#xff0c;并在相邻节点之间进行传输。其主要功…

从技术架构和生态考虑,不是单纯的配置优化,还有哪些方式可以提高spark的计算性能

从技术架构和生态系统层面提升Spark的计算性能&#xff0c;可采取以下核心策略&#xff1a; 一、计算模型重构与执行引擎升级 1. 弹性分布式数据集&#xff08;RDD&#xff09;的血统优化 通过RDD的Lineage&#xff08;血统&#xff09;机制实现容错时&#xff0c;采用增量式…

mysql数据实时全量+增量迁移

对mysql数据库实时全量增量迁移 在数据库管理中&#xff0c;实时全量增量迁移是一种常见的需求&#xff0c;特别是在数据库维护、备份恢复、数据迁移或数据同步等场景中。MySQL数据库提供了多种工具和方法来实现这一需求。以下是几种常见的方法来实现MySQL数据库的实时全量增量…

阿里qwen大模型AI智能分析实时对话生成病例的DEMO

Qwen大模型根据医患对话录音生成病例 业务背景涉及前端技术涉及后端技术阿里云文档完整代码&#xff08;复制即可运行&#xff09; 业务背景 在HIS或者其他医疗系统中&#xff0c;为了提高医生的现场或者线上问诊工作效率&#xff0c;在系统的开病例这块可以通过对话录音&…

AI小白的第七天:必要的数学知识(概率)

概率 Probability 1. 概率的定义 概率是一个介于 0 和 1 之间的数&#xff0c;表示某个事件发生的可能性&#xff1a; 0&#xff1a;事件不可能发生。1&#xff1a;事件必然发生。0 到 1 之间&#xff1a;事件发生的可能性大小。 例如&#xff0c;掷一枚公平的硬币&#xf…