首页 > 其他分享 >cv2中二值图轮廓与轮廓层级参数cv2.RETR_TREE

cv2中二值图轮廓与轮廓层级参数cv2.RETR_TREE

时间:2024-07-07 14:21:03浏览次数:11  
标签:cv2 image TREE contours 轮廓 contour 255

1. 二值图的轮廓

  在使用cv2.findContours时,黑白二值图(像素值只有0或255)的轮廓都是以白色像素作为前景,黑色像素作为背景。看下面两个图(左图与右图同样大小都是200x200,左图是四周为黑色,中间为白色,右图为四周为白色,中间为黑色)。

                                                               

  在使用cv2.findContours查找轮廓时,其得到的轮廓其轮廓像素均是白色255.这里把像素弄成格子更直观。

                                                                                      

                                                                                                 原始黑色区域                                                                                        轮廓为黑色区域外周围的像素

                                                                                            

                                                                                                    原始白色区域                                                                                          轮廓为最边缘白色像素

2. cv2.RETR_TREE参数解释

  cv2.findContours 函数有几个参数,下面是一些主要的参数:

  1. image: 输入图像,必须是单通道的二值图像。
  2. mode: 轮廓检索模式,常用的有:
    • cv2.RETR_EXTERNAL: 只检索最外层的轮廓。
    • cv2.RETR_LIST: 检索所有轮廓,但每个轮廓是独立的,不建立层次关系。
    • cv2.RETR_CCOMP: 检索所有轮廓,并建立层次结构。
    • cv2.RETR_TREE: 检索所有轮廓,并重建完整的轮廓树。
  3. method: 轮廓近似方法,常用的有:
    • cv2.CHAIN_APPROX_SIMPLE: 只保留轮廓的终点。
    • cv2.CHAIN_APPROX_NONE: 保留轮廓的所有点。

  看下面这个图,只有白色与黑色像素。

  使用cv2.drawContours后,得到轮廓contours后,绘制轮廓图如下:

  现在按索引一个个绘制单个轮廓,可以发现,对应的索引其轮廓在图中如下位置:

  contours[0]~contours[8]轮廓对应图中红色数字0~8的位置。现在通过代码分析各个轮廓之间的关系:

import cv2

image_path=r'test.png'
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# 将图像二值化
_, binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)

# 检测所有轮廓
contours, hierarchy  = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)#返回所有轮廓及层级关系
# contours, hierarchy  = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# contours, _ = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# print(hierarchy)
for i, contour in enumerate(contours):
    # print(f"Contour {i}: {contour}")
    print(f"contour {i}: {hierarchy[0][i]}")
# [next, previous, first_child, parent]

output_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)  # 转换为 BGR 以便显示彩色轮廓
cv2.drawContours(output_image, contours, -1, (0, 255, 0), 2)

cv2.imshow('Contours', output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.imwrite('contours_detected.png', output_image)

其层级结构打印的如下:

contour 0: [ 4 -1 1 -1]
contour 1: [-1 -1 2 0]
contour 2: [-1 -1 3 1]
contour 3: [-1 -1 -1 2]
contour 4: [ 7 0 5 -1]
contour 5: [-1 -1 6 4]
contour 6: [-1 -1 -1 5]
contour 7: [-1 4 8 -1]
contour 8: [-1 -1 -1 7]

分析:对于cv2.RETR_TREE参数,返回的层级信息中,每个轮廓层级的信息hierarchy包括四个部分,分别为[next, previous, first_child, parent],其代表意思如下:

  • next: 同级别的下一个轮廓的索引。
  • previous: 同级别的上一个轮廓的索引。
  • first_child: 第一个子轮廓的索引。
  • parent: 父轮廓的索引。

其中-1代表无该索引,我们分析几个轮廓(结合图来看)

contour 0为[ 4 -1 1 -1] ,其中next=4代表同级下一个轮廓为4,也就是contour 4,也对应图中的红色数字4对应的轮廓,previous=-1代表其没有同级别的上一个轮廓。first_child=1代表第一个子轮廓的索引为1,parent=-1表示没有父轮廓,与图中是一致的。

contour 3为 [-1 -1 -1 2],其中next=-1代表同级下,没有轮廓了,这里说的同级是同一父轮廓的情况下,后面的-1,-1,-1根据图可以看到是一致的。parent=2,代表其父轮廓为2,看图中,其被轮廓2直接包围。

  如果我们现在要寻找纯黑或者纯白的也就是不包括空洞的区域,该如何找?可以利用cv2.RETR_TREE得到hierarchy层级关系,然后满足first_child=-1的轮廓。代码如下:

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 过滤出没有子轮廓的轮廓
contours_without_children = []
for i in range(len(hierarchy[0])):
    if hierarchy[0][i][2] == -1:  # 检查first_child是否为-1
        contours_without_children.append(contours[i])

   如果我们想要上面绘制的红色轮廓中纯白色区域,该如何?通过前面,由于白色区域其轮廓所在像素为255,而黑色区域轮廓所在像素区域也为255,可以计算轮廓包围的区域的像素平均值,如果等于255则是白色区域,否则是黑色区域。代码如下:

mask = np.zeros_like(image)#image为单通道灰度图或二值图
filtered_contours=[]
for contour in contours_without_children:
    # 创建一个单通道掩码
    cv2.drawContours(mask, [contour], -1, 255, thickness=cv2.FILLED)
    # 计算轮廓区域的平均像素值
    mean_val = cv2.mean(image, mask=mask)[0]
    # 判断内部是否为纯白色
    if mean_val ==255:  # 255表示纯白
        filtered_contours.append(contour)
    # 清空掩码
    mask.fill(0)

 

  下面给出找出纯白色区域且没有子轮廓的轮廓(上图)的完整代码:

import cv2
import numpy as np

image_path=r'test.png'
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 将图像二值化
_, binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 检测所有轮廓
contours, hierarchy  = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)#返回所有轮廓及层级关系
output_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)  # 转换为 BGR 以便显示彩色轮廓
contours_without_children = []
for i in range(len(hierarchy[0])):
    if hierarchy[0][i][2] == -1:  # 检查first_child是否为-1
        contours_without_children.append(contours[i])

# 创建掩码
mask = np.zeros_like(image)#image为单通道灰度图或二值图
filtered_contours=[]
for contour in contours_without_children:
    # 创建一个单通道掩码
    cv2.drawContours(mask, [contour], -1, 255, thickness=cv2.FILLED)
    # 计算轮廓区域的平均像素值
    mean_val = cv2.mean(image, mask=mask)[0]
    # 判断内部是否为纯白色
    if mean_val ==255:  # 255表示纯白
        filtered_contours.append(contour)
    # 清空掩码
    mask.fill(0)
cv2.drawContours(output_image, filtered_contours, -1, (0, 0, 255), 2)
cv2.imwrite('result.png', output_image)

  代码中mean_val 的值加上打印语句时,会输出28.742 255.0,  8.813,显然平均值为255的为纯白色区域。

 

 

  小结:使用层级关系可以找到任意我们需要的轮廓,如果只需要计算外轮廓,可以设置参数为cv2.RETR_EXTERNAL。另外轮廓近似方法中,使用cv2.CHAIN_APPROX_SIMPLE可以减少获取轮廓点的数量,相比于cv2.CHAIN_APPROX_NONE保留所有轮廓像素点,前者只是在后者基础上像素点有所减少,没有增加新的像素点。

 

  若存在不足或错误之处,欢迎指出与评论。

标签:cv2,image,TREE,contours,轮廓,contour,255
From: https://www.cnblogs.com/wancy/p/18286965

相关文章

  • el-tree懒加载获取所有已经选的节点
    用于el-tree懒加载一个图层目录,勾选父级目录,需要得到该父级目录下所有叶子节点数据<el-tree:data="directoryDataLayer"show-checkboxnode-key="id":load="directoryDataLoad"......
  • 一文理解 Treelite,Treelite 为决策树集成模型的部署和推理提供了高效、灵活的解决方案
    ......
  • World of Warcraft [CLASSIC] Talent Tree
    WorldofWarcraft[CLASSIC] TalentTree 天赋树模拟器01)初始化整个页面,选择游戏职业,初始化3个天赋树02)初始化天赋树结构,层次为N层03)每层有4个技能,设置可显示,设置隐藏04)每个技能可配置图表,技能名称,备注说明,每一级说明不同05)每层的技能可支持最大技能点......
  • 静态 top tree 入门
    理论我们需要一个数据结构维护树上的问题,仿照序列上的问题,我们需要一个方法快速的刻画出信息。比如说线段树就通过分治的方式来通过将一个区间划分成\(\logn\)个区间并刻画出这\(\logn\)个区间的信息。然后我们考虑把这个东西放到树上类比。你发现线段树上每个非叶节点都......
  • CF915F Imbalance Value of a Tree
    达到今日更新量题目让我们求所有简单路径上最大值减去最小值的总和实际上就是所有简单路径的最大值总和减去所有简单路径上最小值总和然后分别求所以简单路径的极值,下面以最大值为例:我刚开始想到了非常SB的做法:枚举最大值x,设比x大的数为y,实际上有很多y,如果y是x的祖先,那么点对......
  • CF1039D You Are Given a Tree (树形 dp + 贪心 + 根号分治)
    CF1039DYouAreGivenaTree树形dp+贪心+根号分治题目是一个经典问题,可以用树形dp和贪心解决。设\(f_u\)表示以\(u\)节点为端点能够剩下的最长路径。考虑从叶子节点往上合并贪心,那么如果能够合并出包含\(u\)节点的大于等于\(k\)的路径,那么就合并,\(f_u=0\);否......
  • 在Java中,Map 接口的实现(如 HashMap,LinkedHashMap,TreeMap 等)并不保证遍历 keySet() 或
    在Java中,Map接口的实现(如HashMap,LinkedHashMap,TreeMap等)并不保证遍历keySet()或entrySet()时的顺序。但是,某些特定的Map实现确实提供了特定的遍历顺序。1、HashMap:它基于哈希表实现,并不保证映射的顺序,特别是遍历顺序。因此,当你使用map.keySet()遍历HashMap时,结果可......
  • vue elementUI el-tree 下拉树功能(包括搜索/默认高亮/展开下拉框默认定位于选中项的位
    <template><div><el-form:model="formData"ref="refFormData"label-width="180px"><el-form-itemlabel="景点"prop="location_id"><el-selectv-model="formData.location_name&qu......
  • elementui el-tree 勾选/取消勾选:子级关联,父级不关联
    :check-strictly="true"父子不关联,在方法里处理子级关联逻辑setChecked方法需要定义node-key="id"<el-treeref="tree":data="treeData":props="defaultProps"default-expand-allhighlight-current:expand......
  • The following untracked working tree files would be overwritten by merge/ git st
    背景给同学解决问题时,发现无法拉取远程的分支。解决他在C:\Users\用户名\路径下,建立了一个git仓库,然后在桌面上创建了一个文件夹,文件夹内部又新建了一个文件夹,导致gitstatus显示大量父级目录(多级父级)的文件。删除父级中的.git文件即可拉取前没有initgitpull用惯了......