系列文章目录
前言
轮廓可以简单地理解为连接所有连续点(沿物体边界)的曲线,这些点通常具有相同的颜色或强度。 轮廓在图像分析中具有重要意义,是物体形状分析和对象检测和识别的有用工具,是理解图像语义信息的重要依据。
本文主要介绍了在 opencv 中,一些重要的用于处理物体轮廓的方法。
1. cv2.findContours()
1.1. 方法概述
cv2.findContours()
函数是 OpenCV 库的一部分,它被广泛用于计算机视觉任务。这个函数用于检测和寻找图像中的轮廓(边界)。
通常,为了提高物体轮廓检测的准确率,首先要将彩色图像或者灰度图像处理成二值图像(灰度图)或者使用 Canny
边缘检测算法对原图像进行一次滤波处理,这样可以在不丢失轮廓信息的前提下降低图像语义信息的复杂度,更有助于我们准确地分析物体轮廓。
简而言之,寻找轮廓的过程更像是在黑色的背景中,寻找白色物体边界的过程。
1.2. cv2.findContours()
contours, hierarchy = cv2.findContours(image, mode, method)
- 参数含义
-
imgae
:单通道二值图像,白色是前景(二值图像或经过Canny算法处理之后的图像) -
mode
:轮廓检索模式 -
method
:轮廓逼近方法
-
- 返回值含义
-
contours
- 一个包含了图像中所有轮廓的list对象。其中每一个独立的轮廓信息以边界点坐标 ( x , y ) (x, y) (x,y) 的形式储存在 numpy 数组中
-
[1, 712, 1, 2]
-
contours.shape[0]
:这代表了在图像中发现的轮廓线的总数 -
contours[i].shape[0]
:这代表第 i i i 个轮廓的点的数量;每个轮廓线由多个点组成,这个维度给出了轮廓线的长度 -
contours[i].shape[1]
:这代表用于表示轮廓线中每个点的维度(坐标)的数量;在大多数情况下,这将是 2 2 2,表示每个点的 ( x , y ) (x, y) (x,y) 坐标
-
-
hierarchy
- 一个包含有关轮廓层级的 4 个值的数组
[Next, Previous, First Child, Parent]
-
Next
:与当前轮廓处于同一层级的下一条轮廓 -
Previous
:与当前轮廓处于同一层级的上一条轮廓 -
First_Child
:当前轮廓的第一条子轮廓 -
Parent
:当前轮廓的父轮廓
-
- 一个包含有关轮廓层级的 4 个值的数组
-
1.2.1. 轮廓检索模式
轮廓检索模式的详细概念如下所示:
轮廓检索模式 | 作用 |
---|---|
cv2.RETR_LIST |
这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级;以 List 形式输出轮廓信息 |
cv2.RETR_TREE |
完整建立轮廓的层级从属关系,以树结构输出轮廓信息 |
cv2.RETR_EXTERNAL |
只寻找最高层级的轮廓,即外轮廓 |
cv2.RETR_CCOMP |
把所有的轮廓只分为 2 个层级,不是外层的就是里层的;输出两层轮廓信息,即内外两个边界,上面一层为外边界,里面一层为内孔的边界信息 |
基于下面的图,来解释图像的轮廓层级:
图中总共有 8 条轮廓,2 和 2a 分别表示外层和里层的轮廓,3 和 3a 同理,从图中可以发现:
- 轮廓 0、1、2 是最外层的轮廓,我们可以说它们处于同一轮廓等级:0级
- 轮廓 2a 是轮廓 2 的子轮廓,反过来说 2 是 2a 的父轮廓;轮廓 2a 算一个等级:1级
- 同样 3 是 2a 的子轮廓,轮廓 3 处于一个等级:2级
- 类似的,3a 是 3 的子轮廓
-
cv2.RETR_RETR_LIST
- 这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级
- hierarchy 中的后两个值 [First_Child, Parent] 都为 − 1 -1 −1
- 因为没有从属关系,所以轮廓 0 0 0 的下一条是 1 1 1,$14 的下一条是 2 2 2
- 如果不需要轮廓层级信息的话,更推荐使用
cv.RETR_LIST
,因为性能更好
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, 2)
print(hierarchy)
# 结果如下
[[[ 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]]]
-
cv2.RETR_TREE
-
Next
- 对轮廓 0 来说,与 0 处于同一层级的下一条轮廓是 1,所以 Next = 1
- 同理,对轮廓 1 来说,Next=2
- 对轮廓 2 来说,没有与它同一层级的下一条轮廓了,此时 Next = -1
-
Previous
- 对轮廓 1 来说,Previous = 0
- 对轮廓 2 来说,Previous = 1
- 对轮廓 2a 来说,没有上一条轮廓了,所以 Previous = -1
-
First_Child
- 对轮廓 2 来说,第一条子轮廓就是轮廓 2a,所以 First_Child = 2a
- 对轮廓 3 来说,First_Child = 3a
-
Parent
- 对轮廓 2a 来说,父轮廓是 2,Parent = 2
- 对轮廓 2 来说,没有父轮廓,Parent = 0
-
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, 2)
print(hierarchy)
# 结果如下
[[[ 6 -1 1 -1]
[-1 -1 2 0]
[-1 -1 3 0]
[-1 -1 4 2]
[ 5 -1 -1 3]
[-1 4 -1 3]
[ 7 5 -1 -1]
[-1 6 -1 -1]]]
-
cv2.RETR_EXTERNAL
- 这种方式只寻找最高层级的轮廓,也就是它只会找到前面我们所说的 3 条 0 级轮廓
contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, 2)
print(len(contours), hierarchy, sep='\n')
# 结果如下
3
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[-1 1 -1 -1]]]
-
cv2.RETR_CCOMP
- 把所有的轮廓只分为 2 个层级,不是外层的就是里层
- 上图中,括号里面 1 代表外层轮廓,2 代表里层轮廓
- 对于轮廓 2
- Next 就是 4,Previous 是 1
- 它有里层的轮廓 3,所以 First_Child = 3
- 但因为只有两个层级,它本身就是外层轮廓,所以 Parent = -1
contours, hierarchy = cv.findContours(thresh, cv.RETR_CCOMP, 2)
print(hierarchy)
# 结果如下
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[ 4 1 3 -1]
[-1 -1 -1 2]
[ 6 2 5 -1]
[-1 -1 -1 4]
[ 7 4 -1 -1]
[-1 6 -1 -1]]]
1.2.2. 轮廓逼近方法
轮廓逼近方法 | 作用 |
---|---|
cv2.CHAIN_APPROX_NONE |
存储所有边界点 |
cv2.CHAIN_APPROX_SIMPLE |
不保存轮廓中水平、垂直、对角的线段,只保存轮廓的角点 |
cv2.CHAIN_APPROX_TX89_L1 |
使用 teh-Chini 近似算法 |
cv2.CHAIN_APPROX_TC89_KCOS |
使用 teh-Chini 近似算法 |
- 一般来说,后面两种方法不常见
-
cv2.CHAIN_APPROX_NONE
见下图左;cv2.CHAIN_APPROX_SIMPLE
见下图右
2. cv2.drawContours()
2.1. 方法概述
在通过 cv2.findContours()
找到物体的轮廓后,有时候需要可视化物体的轮廓以判断是否符合预期,这个时候就需用到 opencv 包含的另一个重要方法 cv2.drawContours()
。cv2.drawContours()
可以在 image 上用指定的颜色和线的宽度画出使用 cv2.findContours()
获得的物体轮廓。
2.2. cv2.drawContours()
cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
- 参数含义
-
image
- 这是要绘制轮廓线的输入图像
- 通常是一个 RGB 格式的图像,或者是一个灰度图
-
contours
- 是你想在图像上绘制的轮廓线的列表
- 它应该是一个 Python 列表,其中每个轮廓被表示为一个 ( x , y ) (x, y) (x,y) 坐标的 NumPy 数组
-
contourIdx
- 这个参数指定要画的轮廓的索引
- 通常使用 -1 来绘制所有的轮廓
-
color
- 这个参数指定了轮廓线的颜色
- 对于彩色图像,颜色应该被指定为代表 BGR 值的三个整数的元组(需要注意是 BGR 而不是 RGB,与使用
cv2.imread()
方法读进来的彩色图像通道相同) - 对于灰度图像,颜色值是一个代表强度的标量
-
thickness
- 这个参数指定轮廓线的厚度,单位是像素
- 使用一个负值来填充轮廓线
-
3. cv2.contourArea()
3.1. 方法概述
cv2.contourArea()
函数是 OpenCV 提供的一个实用函数,用于计算一个轮廓的面积。它接受一个轮廓作为输入,并返回该轮廓的面积。
3.2. cv2.contourArea()
area = cv2.contourArea(contours, oriented=false)
- 参数含义
-
contours
- 要计算面积的输入轮廓
- 应该是一个由 ( x , y ) (x, y) (x,y) 坐标组成的 NumPy 数组表示的单一轮廓
-
oriented
- 面向区域标识符。有默认值 false
- 若为 true,该函数返回一个带符号的面积值,正负取决于轮廓的方向(顺时针还是逆时针)
- 若为 false,表示以绝对值返回。
-
- 返回值
-
area
- 是输出变量,用于存储计算出的轮廓所围成的面积
- 它是一个浮点值,以像素为单位表示面积
-
3.3. 存在的问题
需要注意的是 cv2.contourArea()
计算得到的面积,可能会比预期的面积稍微小一点,这是因为 cv2.contourArea()
通过取连通域边界像素中心点,连接起来,成为一个轮廓,导致一周得边界像素点丢失,即求得得面积比真实得面积少了一圈
比如下图,真实面积
4
×
4
=
16
4 \times 4 = 16
4×4=16,而 cv2.contourArea()
则只计算红线内的面积,只有
3
×
3
=
9
3 \times 3 = 9
3×3=9
因此,cv2.contourArea()
可能将一些轮廓的面积计算为 0,如下图所示
一个简单的解决方法为
rect = cv2.minAreaRect(contours) #最小外接矩形
box = cv2.boxPoints(rect)
box = np.int0(box)
area = cv2.contourArea(box)
总结
本文主要记录了一些博主最近在实验过程中遇到的与 label 轮廓相关的处理函数,如果后面有需要,会继续更新相关的函数。文章来源:https://www.toymoban.com/news/detail-503241.html
参考博客
参考博客文章来源地址https://www.toymoban.com/news/detail-503241.html
到了这里,关于图像预处理 Tricks【1】:Contours的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!