这篇博文是对博文https://blog.csdn.net/wenhao_ir/article/details/125537919和博文https://blog.csdn.net/wenhao_ir/article/details/51798533的延伸。
为了搞清楚轮廓检测函数findContours()得到的轮廓拓扑结构(hiararchy)的含义,我们用下面的两个例子来实际测试,并对结果进行分析就能完全搞清楚其意义了。
在运行例子前我们先回顾一下我在博文https://blog.csdn.net/wenhao_ir/article/details/51798533中对函数findContours()的参数hiararchy的介绍。
hiararchy—这是一个可选输出参数,它用来存储检测到的轮廓的拓扑结构信息。检测到的每个轮廓都对应一个拓扑结构信息。对于每个轮廓contours[i], hierarchy[i][0]、 hiearchy[i][1] 、 hierarchy[i][2] 、hiearchy[i][3]的意义分别如下:
好,回顾完上面这个知识点之后我们开始用实例来说明。
首先是下面这张图片:
上面这张图片的名字为ring_03.bmp,
百度网盘下载链接:https://pan.baidu.com/s/1OfFYBY92tN1I6Bf8SUHlNg?pwd=cb3p
检测其轮廓的代码如下:
# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也请联系博主
# 图像处理技术交流QQ群 271891601
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1
import cv2 as cv
import sys
image = cv.imread('F:/material/images/2022/2022-06/ring_03.bmp')
if image is None:
print('Error: Could not load image')
sys.exit()
# cv.imshow('Source Image', image)
# 原图像转化为灰度图
img_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# cv.imshow('img_gray', img_gray)
# 灰度图进行二值化处理,并不是函数findContours要求输入图像为二值图像,
# 而是函数findContours在进行轮廓提取前会把原图中的非0值全部当成1处理。
_, img_B = cv.threshold(img_gray, 71, 255, cv.THRESH_BINARY)
# cv.imshow('img_B', img_B)
# 轮廓检测
cnts, harch = cv.findContours(img_B, mode=cv.RETR_TREE, method=cv.CHAIN_APPROX_SIMPLE)
我利用博文https://blog.csdn.net/wenhao_ir/article/details/125573892中的代码将各轮廓单独输出成图像,结果如下:
我把上面几张图进行了打包,并上传到百度网盘,方便大家查看,链接 https://pan.baidu.com/s/1pAmwa-KCBMkx4yMX8EvXRg?pwd=qqet
回到正题,上面的代码检测到的轮廓拓扑结构如下:
这个轮廓拓扑结构的每一行代表一个轮廓的拓扑结构信息,分别表示与其同一个父轮廓的前一个轮廓的索引、与其同一个父轮廓的后一个轮廓的索引、子轮廓的索引、父轮廓的索引。
根据上面的轮廓拓扑结构我可以绘制出其轮廓拓扑结构图如下:
怎么绘制出来的呢?博主录了个视频把这个过程进行详细讲述,视频在线观看和下载链接:
https://pan.baidu.com/s/1mBWeDhYw6tcBBOdc-lvH5Q?pwd=2mh0
通过上面这个视频讲解,相信大家就能掌握怎么样根据轮廓拓扑结构矩阵绘制轮廓拓扑结构图了。有了这个基础之后,我们就可以来更具体的探究轮廓拓扑结构矩阵的意义了。
在上面的简单示例中,第二层的轮廓①、③、⑤虽然处于是同一层,但因为没有共同的父轮廓,所以它们的轮廓矩阵结构中的上一个轮廓和下一个轮廓的值都为-1。在轮廓结构图上体现出来为轮廓①、③、⑤的左右两边都没有轮廓。
仔细点的朋友要问了,那0、2、4轮廓也没有共同的父轮廓啊,怎么它们的共同父轮廓的下一个索引值和上一个索引值不为-1呢?答案是这样的:因为它们处于第一层,我们可以想像它们有共同的根(root),所以它们有共同的父轮廓root,而轮廓①、③、⑤的父轮廓就很明显是不同的了。
接下来我们看拓扑结构图中第二层(包括第二层)以下的层级轮廓存在共同父轮廓的例子。
原图如下:
上面这张图的名字为:img_300_320.jpg,百度网盘下载链接:https://pan.baidu.com/s/1IaJ8nrQzGuHt3RA8jbu0GQ?pwd=bjkm
检测其轮廓的代码如下:
# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也请联系博主
# 图像处理技术交流QQ群 271891601
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1
import cv2 as cv
import sys
image = cv.imread('F:/material/images/2022/2022-06/img_300_320.jpg')
if image is None:
print('Error: Could not load image')
sys.exit()
# cv.imshow('Source Image', image)
# 原图像转化为灰度图
img_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# cv.imshow('img_gray', img_gray)
# 灰度图进行二值化处理,并不是函数findContours要求输入图像为二值图像,
# 而是函数findContours在进行轮廓提取前会把原图中的非0值全部当成1处理。
_, img_B = cv.threshold(img_gray, 71, 255, cv.THRESH_BINARY)
# cv.imshow('img_B', img_B)
# 轮廓检测
cnts, harch = cv.findContours(img_B, mode=cv.RETR_TREE, method=cv.CHAIN_APPROX_SIMPLE)
博文https://blog.csdn.net/wenhao_ir/article/details/125573892中有这幅图像轮廓检测结果的每个轮廓的单独输出图像。
检测结果如下:
我们可以根据上面这个拓扑结构矩阵画出下面的拓扑结构图:
至于怎么画出来的,我想大家看了第一幅的绘制讲解视频,是很容易画出来的,所以这里就不再录演示视频了。
从上面这个轮廓拓扑结构图我们可以清晰的看出:
这个例子的第1号轮廓有12个子轮廓,索引号分别为2、3、4、5、6、7、8、9、10、11、12、25,即这些轮廓有共同的父轮廓1,所以轮廓2、3、4、5、6、7、8、9、10、11、12、25的结构矩阵中的上一个和下一个轮廓的值不为-1,在轮廓结构中体现出来为它们处于同一层并且左右两边有轮廓。
这里就有一个问题,1号轮廓有12个子轮廓,那么其轮廓结构中的第3个表示子轮廓索引的值为多少呢?我们看到,是2,即其第1个子轮廓。同样,12号轮廓有11个子轮廓,那么其轮廓结构中的第3个表示子轮廓索引的值为多少呢?我们看到,是13,也是其第1个子轮廓。所以通过这个例子,我们就明白了当一个轮廓有多个子轮廓值时,其子轮廓的索引值填的是哪个子轮廓。
至此,通过这两个例子,我们就把轮廓拓扑结构(hiararchy)矩阵的意义彻底搞清楚了。