OpenCV 图像轮廓查找与绘制全攻略:从函数使用到实战应用详解

摘要:本文详细介绍了 OpenCV 中用于查找图像轮廓的 cv2.findContours() 函数以及绘制轮廓的 cv2.drawContours() 函数的使用方法。涵盖 cv2.findContours() 各参数(如 mode 不同取值对应不同轮廓检索模式)及返回值的详细解析,搭配多幅示例图片与丰富代码示例展示不同模式下的效果差异,同时展示了 cv2.drawContours() 的用法,并通过案例讲解如何利用轮廓绘制功能获取前景对象,助力读者全面掌握图像轮廓相关操作要点及应用场景。

如果您觉得我的文章对您有帮助,可以点赞收藏关注,持续学习更多与OpenCV相关的知识

OpenCV 图像轮廓查找与绘制全攻略:从函数使用到实战应用详解

查找并绘制轮廓

边缘检测能检测出边缘,但是边缘不连续,但不是一个整体。OpenCV提供了cv2.findContours()去查找图像轮廓,cv2.drawContours()将轮廓绘制起来。

cv2.findContours()函数的使用

该函数的语法如下:

image与被处理图像一致,contours返回的轮廓 ,hierarchy 图像的拓普信息(轮廓层次) = cv2.findContours(image原始图像,mode轮廓检索模式,method轮廓的近似方法)

这里有一个补充:当OpenCV版本的大于4.x,返回值没有image

我通过画图板画了一个例子,存储在和代码同一个文件夹下,大家可以复制我的图片,去作为练习,(补充一个重要的前提:OpenCV中,都是从黑色背景中找白色对象,对象是白的,背景是黑的,图片必须是灰度二值图像,对于彩色图像要做好阈值处理 ):

我将这张图片命名为countours.JPG,这是我的文件夹(我用的是 jupyter notebook 大家用pycharm的就把代码和图片放在同一个文件夹下就可以运行我的代码了):

返回值countours的属性

他的类型是list:

python 复制代码
import numpy as np
import cv2
img = cv2.imread("countours.JPG",cv2.IMREAD_GRAYSCALE)
countours , hierarchy = cv2.findContours(img,mode = cv2.RETR_EXTERNAL,method = cv2.CHAIN_APPROX_NONE)
print(type(countours))

每一条轮廓的shape与内容都用列表的方法访问:
一定要注意 必须先用高斯滤波进行平滑处理去除噪声 再用二值化阈值处理,才能计算出正确的轮廓

python 复制代码
import numpy as np
import cv2
img = cv2.imread("countours.JPG",cv2.IMREAD_GRAYSCALE)

# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours , hierarchy = cv2.findContours(img,mode = cv2.RETR_EXTERNAL,method = cv2.CHAIN_APPROX_NONE)
print(len(countours))
for i in range(len(countours)):
    print(countours[i].shape)
    print(countours[i])

通过代码找出来十条轮廓,与我们手动标注的数量一致:

参数mode与返回值hierarchy

我将这张图片命名为mode.JPG,这是我的文件夹(我用的是 jupyter notebook 大家用pycharm的就把代码和图片放在同一个文件夹下就可以运行我的代码了):

不同的mode参数对应着不同的hierarchy,分为四种:

第一种:cv2.RETR_EXTERNAL(只检测外轮廓)

python 复制代码
import numpy as np
import cv2
img = cv2.imread("mode.JPG",cv2.IMREAD_GRAYSCALE)

# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours , hierarchy = cv2.findContours(img,mode = cv2.RETR_EXTERNAL,method = cv2.CHAIN_APPROX_NONE)
print(len(countours))
print(hierarchy)

输出值[1 -1 -1 -1]代表 第0个轮廓的后一个轮廓是第一个轮廓,他没有前一个轮廓 所以第二个元素是-1 他没有子轮廓 和父轮廓 所以第三第四个元素都是 - 1

输出值[-1 0 -1 -1]代表第1个轮廓 没有后面轮廓了 所以是-1 他的前一个轮廓 是0,他没有子轮廓和父轮廓 所以第三个第四个元素都是 -1

找到了两条轮廓,原因是cv2.RETR_EXTERNAL会导致只搜索外轮廓:

第二种:cv2.RETR_LIST

检测到的轮廓不考虑父子关系

在上面的代码中修改mode = cv2.RETR_LIST,运行结果如下:

这个结果前两行的第一个元素都是 1 2 说明他们有后一个轮廓 ,因为没有考虑父子关系,所以每一行的三四元素都是-1 第1 第2 轮廓都有前一个轮廓 分别是 0 和1 ,所以第二列 是 -1 0 1

第三种:cv2.RETR_CCOMP

检查所有轮廓并组织称一个两级层次结构,修改参数mode = cv2.RETR_CCOMP,继续运行得到如下结果:

可以看到第1 轮廓 和 第2 轮廓存在了 父子 关系 :

对于1,2轮廓来说他们是父子关系,所以不存在后一个轮廓所以是-1:

第四种:cv2.RETR_TREE

生成一个等级树,为了验证第四个和第三个的区别,我重新用画图板画了一张图起名为tree.JPG,大家可以复制到自己的文件夹下:

python 复制代码
import numpy as np
import cv2
img = cv2.imread("tree.JPG",cv2.IMREAD_GRAYSCALE)
# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours_comp , hierarchy_comp = cv2.findContours(img,mode = cv2.RETR_CCOMP,method = cv2.CHAIN_APPROX_NONE)
countours_tree , hierarchy_tree = cv2.findContours(img,mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_NONE)
print(hierarchy_comp)
print(hierarchy_tree)

详细分析一下,运行结果:

显而易见的是 cv2.RETR_CCOMP时,最多产生一个两级的父子结构:

但是cv2.RETR_TREE产生了一个多级的父子结构树:

cv2.drawContours()函数的使用

语法:

cou_image待绘制的轮廓图像 = cv2.drawCountours(image待处理图像,countours需要绘制的轮廓 list类型,countourIdx绘制的边缘索引,color绘制的颜色,thickness绘制轮廓的粗细,lineType画笔类型,hierarchy拓普信息,maxLevel层次的深度,offset偏移参数,使得轮廓偏移多少位置)

使用tree.JPG做案例来用代码验证一下效果:

python 复制代码
import numpy as np
import cv2
img = cv2.imread("tree.JPG",cv2.IMREAD_GRAYSCALE)
# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours_tree , hierarchy_tree = cv2.findContours(img,mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_NONE)

n = len(countours_tree)
contourImg = []
cv2.imshow("orig",img)
for i in range(n):
    temp = np.zeros(img.shape,np.uint8)
    contourImg.append(temp)
    contourImg[i] = cv2.drawContours(contourImg[i],countours_tree,i,(255,255,255),5)
    cv2.imshow(f"contour[ {i}]",contourImg[i])
cv2.waitKey()
cv2.destroyAllWindows()

利用轮廓绘制功能,获取前景对象

这部分的案例使用pig.JPG图片,需要的可以直接复制,跟代码放在同一文件夹之下:

先讲解一下代码的思路,首先转化成灰度值和二值图像,然后获取轮廓,绘制整个轮廓,利用按位与运算与原图像进行按位与运算,提取前景对象。

python 复制代码
import numpy as np
import cv2
bgr_img = cv2.imread("pig.JPG")
img = cv2.cvtColor(bgr_img,cv2.COLOR_BGR2GRAY)
# 先用高斯滤波平滑处理
img = cv2.GaussianBlur(img, (5, 5), 0)
# 再用阈值处理转化为二值图像
_,img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
countours_tree , hierarchy_tree = cv2.findContours(img,mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_NONE)
mask = np.zeros(bgr_img.shape,np.uint8)
mask = cv2.drawContours(mask,countours_tree,-1,(255,255,255),-1)
loc = cv2.bitwise_and(mask,bgr_img)
cv2.imshow("mask",mask)
cv2.imshow("loc",loc)
cv2.waitKey()
cv2.destroyAllWindows()

致谢

本文参考了一些博主的文章,博取了他们的长处,也结合了我的一些经验,对他们表达诚挚的感谢,使我对 图像轮廓查找与绘制 有更深入的了解,也推荐大家去阅读一下他们的文章。纸上学来终觉浅,明知此事要躬行:
python+opencv基础篇------实现提取轮廓

相关推荐
狗窝超厉害4 分钟前
研0找实习【学nlp】15---我的后续,总结(暂时性完结)
人工智能·pytorch·python·自然语言处理·bert
企业通用软件开发7 分钟前
大语言模型提示词工程学习--写小说系列(文心一言&豆包&通义千问):1~创作前的准备工作
人工智能·学习·语言模型
龙的爹233310 分钟前
论文翻译 | BERTese: Learning to Speak to BERT
人工智能·深度学习·自然语言处理·prompt·bert
神奇的布欧12 分钟前
BERT的工作原理
人工智能·深度学习·bert
孤单网愈云20 分钟前
11.22Pytorch_自动微分
人工智能·pytorch·python
weixin_4662027835 分钟前
第32周:猴痘病识别(Tensorflow实战第四周)
人工智能·python·tensorflow
Mr.Winter`1 小时前
优化求解 | 非线性最小二乘优化器Ceres安装教程与应用案例
人工智能·机器人·自动驾驶·ros·数值优化
美狐美颜sdk1 小时前
从源码到平台:基于第三方视频美颜SDK开发实时直播美颜系统
深度学习·计算机视觉·音视频·直播美颜sdk·视频美颜sdk·美颜api·主播美颜插件
Deepcong1 小时前
yolov11的目标检测理论、tensorrt实现推理
人工智能·yolo·目标检测
爱喝白开水a2 小时前
基于Transformer的图像处理预训练模型
图像处理·人工智能·深度学习·transformer·预训练·ai大模型·计算机技术