一、引言
在图像处理领域,轮廓是一个非常重要的概念。它能够帮助我们分析物体的形态,计算物体的周长、面积等信息。本文将详细介绍什么是轮廓,如何在 OpenCV 中寻找轮廓以及如何绘制轮廓。
二、什么是轮廓?
轮廓是一系列相连的点组成的曲线,代表了物体的基本外形。与边缘不同,轮廓是连续的,而边缘不一定连续。边缘主要作为图像的特征使用,例如可以用边缘特征区分脸和手;而轮廓主要用于分析物体的形态。可以说,边缘包括轮廓。
三、寻找轮廓
3.1 原理概述
在 OpenCV 中,使用 cv2.findContours()
函数来寻找轮廓。其原理较为复杂,这里简单介绍一下。寻找轮廓需要将图像进行二值化处理,根据图像的不同选择合适的二值化方法,将图像中要绘制轮廓的部分置为白色,其余部分置为黑色。也就是说,需要对原始图像进行灰度化、二值化处理,使目标区域显示为白色,其他区域显示为黑色。
之后,对图像中的像素进行遍历,当一个白色像素相邻(上下左右及两条对角线)位置有黑色像素存在,或者一个黑色像素相邻(上下左右及两条对角线)位置有白色像素存在时,该像素点就会被认定为边界像素点,轮廓就是由无数个这样的边界点组成的。
3.2 cv2.findContours()
函数介绍
该函数的原型为:
contours, hierarchy = cv2.findContours(image, mode, method)
contours
:表示获取到的轮廓点的列表。检测到有多少个轮廓,该列表就有多少子列表,每一个子列表都代表了一个轮廓中所有点的坐标。hierarchy
:表示轮廓之间的关系。对于第i
条轮廓,hierarchy[i][0]
、hierarchy[i][1]
、hierarchy[i][2]
、hierarchy[i][3]
分别表示其后一条轮廓、前一条轮廓、(同层次的第一个)子轮廓、父轮廓的索引(如果没有对应的索引,则为负数)。该参数的使用情况相对较少。image
:表示输入的二值化图像。mode
:表示轮廓的检索模式。method
:轮廓的表示方法。
3.3 mode
参数详解
mode
参数共有四个选项:
- RETR_LIST:列出所有的轮廓。并且在
hierarchy
里的轮廓关系中,每一个轮廓只有前一条轮廓与后一条轮廓的索引,而没有父轮廓与子轮廓的索引。 - RETR_EXTERNAL:只列出最外层的轮廓。并且在
hierarchy
里的轮廓关系中,每一个轮廓只有前一条轮廓与后一条轮廓的索引,而没有父轮廓与子轮廓的索引。 - RETR_CCOMP:列出所有的轮廓。并且在
hierarchy
里的轮廓关系中,轮廓会按照成对的方式显示。 - RETR_TREE:列出所有的轮廓。并且在
hierarchy
里的轮廓关系中,轮廓会按照树的方式显示,其中最外层的轮廓作为树根,其子轮廓是一个个的树枝。
3.4 method
参数详解
method
参数有三个选项:
- CHAIN_APPROX_NONE:将所有的轮廓点都进行存储。
- CHAIN_APPROX_SIMPLE:只存储有用的点,比如直线只存储起点和终点,四边形只存储四个顶点,默认使用这个方法。
- CHAIN_APPROX_TC89_L1:使用 Teh - Chin 链逼近算法进行轮廓逼近。这种方法使用的是 Teh - Chin 链码,它是一种边缘检测算法,可以对轮廓进行逼近,减少轮廓中的冗余点,从而更加准确地表示轮廓的形状。该方法适用于需要较高精度的轮廓表示的情况。
一般情况下,mode
和 method
这两个参数使用 RETR_EXTERNAL
和 CHAIN_APPROX_SIMPLE
这两个选项。
四、绘制轮廓
轮廓找出来后,返回的是一个轮廓点坐标的列表,我们需要根据这些坐标将轮廓画出来,这时就用到了 cv2.drawContours()
函数。
cv2.drawContours(image, contours, contourIdx, color, thickness)
image
:原始图像,一般为单通道或三通道的 numpy 数组。contours
:包含多个轮廓的列表,每个轮廓本身也是一个由点坐标构成的二维数组(numpy 数组)。contourIdx
:要绘制的轮廓索引。如果设为-1
,则会绘制所有轮廓。color
:绘制轮廓的颜色,可以是 BGR 值或者是灰度值(对于灰度图像)。thickness
:轮廓线的宽度,如果是正数,则画实线;如果是负数,则填充轮廓内的区域。
五、代码示例
import cv2
import numpy as np
def test001():
img = cv2.imread('./src/num.png')
# 使用cv2.cvtColor函数将彩色图像img转换为灰度图像。cv2.COLOR_BGR2GRAY是颜色空间转换代码,表示从 BGR(蓝、绿、红)颜色空间转换为灰度颜色空间
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 使用cv2.threshold函数对灰度图像gray进行二值化处理。
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
# 使用cv2.findContours函数在二值化图像binary中查找轮廓。
contours,hierarchy=cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 打印检测到的轮廓数量和轮廓的层次关系
print(len(contours),hierarchy)
# 打印第一个轮廓的点坐标
print(contours[0])
# 遍历每个轮廓,使用cv2.line函数在原图像img上依次连接轮廓上的点,绘制出轮廓的线条。线条颜色为红色(0, 0, 255),线宽为 2。
# for counter in contours:
# first_contour = counter[0][0]
# for el in counter:
# # print(first_contour,el[0],"==========")
# # break
# cv2.line(img,first_contour,el[0],(0,0,255),2)
# first_contour=el[0]
cv2.drawContours(img,contours,1,(0,255,0),2)
cv2.imshow('img', img)
cv2.imshow('binary', binary)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
test001()
六、总结
本文详细介绍了图像轮廓的概念、寻找轮廓的原理和方法,以及如何使用 OpenCV 绘制轮廓。通过合理选择 cv2.findContours()
函数的 mode
和 method
参数,我们可以根据实际需求获取不同类型的轮廓信息。同时,利用 cv2.drawContours()
函数可以将轮廓可视化,方便后续的分析和处理。希望本文能对大家在图像处理领域的学习和实践有所帮助。