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基础篇------实现提取轮廓

相关推荐
小成晓程1 小时前
PyQt6+OpenCV 项目练习
人工智能·opencv·计算机视觉
奔波儿灞爱霸波尔奔2 小时前
人工智能之基于阿里云进行人脸特征检测部署
人工智能·阿里云·云计算
董董灿是个攻城狮2 小时前
003:如何理解 CNN 中的 RGB 图像和通道?
人工智能·计算机视觉·cnn
小码贾2 小时前
OpenCV-Python实战(11)——边缘检测
人工智能·opencv·计算机视觉
CM莫问3 小时前
tokenizer、tokenizer.encode、tokenizer.encode_plus比较
人工智能·python·深度学习·语言模型·大模型·tokenizer·文本表示
nn_305 小时前
利用 deepin-IDE 的 AI 能力,我实现了文件加密扩展
ide·人工智能
沐雨风栉5 小时前
Windows电脑部署SD 3.5结合内网穿透随时随地生成高质量AI图像
人工智能·电脑
没学上了5 小时前
加强版十六章视频读写
opencv·计算机视觉
mazhafener1236 小时前
5G/4G工业边缘网关 边缘计算 硬核配置强算力
人工智能·5g·边缘计算
百家方案7 小时前
「下载」京东数科-数字孪生智慧园区解决方案:打通园区数据、融合园区业务、集成园区服务、共建园区生态,实现真实与数字孪生园区
人工智能·云计算·智慧园区·数智化园区