在图像处理与计算机视觉领域,轮廓检测是一项极为关键的技术。轮廓作为物体边界的重要表征,承载了图像中物体的形状、尺寸和位置等关键信息。通过轮廓检测,我们能够提取出图像中物体的轮廓,为后续的物体识别、图像分割、形状分析等任务提供有力支持。OpenCV 作为一款强大的计算机视觉库,提供了丰富且高效的轮廓检测工具。接下来,本文将深入探讨轮廓检测的原理,并结合 OpenCV 的代码示例,帮助大家更好地掌握这一技术。
一、轮廓检测简介
轮廓检测旨在寻找并提取图像中物体的边界,以一系列相连的点表示物体的轮廓。相较于边缘检测侧重于检测像素值的急剧变化,轮廓检测更关注物体的整体形状,通常作用于二值图像。通过将图像转换为二值图,凸显前景和背景的差异,为轮廓提取创造条件。在实际应用中,轮廓检测广泛应用于工业检测、安防监控、医学图像处理等领域。
二、轮廓检测流程
1. 图像预处理
在进行轮廓检测之前,通常需要对图像进行预处理,提升检测效果。常见的预处理操作包括灰度化、降噪、二值化等。灰度化将彩色图像转换为灰度图,简化后续处理;降噪通过滤波操作去除图像中的噪声干扰;二值化则将图像像素值划分为两个类别,生成前景和背景分明的二值图像。
2. 轮廓提取
OpenCV 提供了cv2.findContours()函数用于提取轮廓。该函数基于图像的像素值差异,寻找图像中的连续边界,将其转换为轮廓数据结构,为后续的轮廓分析提供数据支持。
3. 轮廓分析与绘制
提取轮廓后,我们可以对轮廓进行分析,如计算轮廓面积、周长,拟合形状等。此外,OpenCV 提供cv2.drawContours()函数,用于在原始图像或新图像上绘制轮廓,直观展示检测结果。
三、OpenCV 轮廓检测函数详解
查找轮廓的API:image, contours, hierarchy = cv2.findContours(img, mode, method)#
参数:img:需要实现轮廓检测的原图
mode: 轮廓的检索模式,主要有四种方式:
cv2.RETR_EXTERNAL:只检测外轮廓,所有子轮廓被忽略
cv2.RETR_LIST:检测的轮廓不建立等级关系,所有轮廓属于同一等级
cv2.RETR_CCOMP:返回所有的轮廓,只建立两个等级的轮廓。一个对象的外轮廓为第1级组织结构。
而对象内部中空洞的轮廓为第2级组织结构,空洞中的任何对象的轮廓又是第 1 级组织结构。
cv2.RETR_TREE:返回所有的轮廓,建立一个完整的组织结构的轮廓。
method:轮廓的近似方法,主要有以下两种:
cv2.CHAIN_APPROX_NONE:存储所有的轮廓点。
cv2.CHAIN_APPROX_SIMPLE:压缩模式,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。
提取轮廓
import cv2
phone = cv2.imread('phone.png')#读取原图
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)#灰度图的处理
cv2.imshow('phone_b',phone_gray)
cv2.waitKey(0)
# phone_gray=cv2.imread('phone.png',0) #读取灰度图
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)#阈值处理为二值
# cv2.imshow('phone_binary',phone_binary)
# cv2.waitKey(0)
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#这两行代码分别输出轮廓的层级结构和检测到的轮廓数量。
#hierarchy:它是一个形状为 (1, n, 4) 的三维数组,其中 n 代表检测到的轮廓数量
#contours:它是一个列表,列表中的每个元素代表一个检测到的轮廓。
# 通过 len(contours) 就能得到检测到的轮廓的数量。
#n就是len(contours)
print(hierarchy)
print(contours)
print(len(contours))
绘制轮廓
cv2.drawContours(image, contours, contourIdx, color, thickness=None,
lineType=None, hierarchy=None, maxLevel=None, offset=None)
轮廓的特征
获取轮廓后,通常基于轮廓的特征进行筛选、识别和处理。例如,基于轮廓的周长和面积对轮廓进行筛选,然后绘制筛选的目标轮廓或其最小外接矩形。
import cv2
phone = cv2.imread('phone.png')#读取原图
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)#灰度图的处理
cv2.imshow('phone_b',phone_gray)
cv2.waitKey(0)
# phone_gray=cv2.imread('phone.png',0) #读取灰度图
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)#阈值处理为二值
cv2.imshow('phone_binary',phone_binary)
cv2.waitKey(0)
#查找轮廓
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#轮廓面积
area_0=cv2.contourArea(contours[0])
print(area_0)
area_1=cv2.contourArea(contours[1])
print(area_1)
#轮廓周长
length=cv2.arcLength(contours[0],closed=True)
print(length)
轮廓的近似
approx = cv2.approxPolyDP(curve, epsilon, closed)
参数说明:
curve:输入轮廓。
epsilon:近似精度,即两个轮廓之间最大的欧式距离。该参数越小,得到的近似结果越接近实际轮廓;反之,得到的近似结果会更加粗略。
closed:布尔类型的参数,表示是否封闭轮廓。如果是 True,表示输入轮廓是封闭的,近似结果也会是封闭的;否则表示输入轮廓不是封闭的,近似结果也不会是封闭的。
返回值:approx:近似结果,是一个ndarray数组,为1个近似后的轮廓,包含了被近似出来的轮廓上的点的坐标
import cv2
phone=cv2.imread('duola.jpg')
phone_gray=cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
#二值化处理
ret,phone_thresh=cv2.threshold(phone_gray,180,255,cv2.THRESH_BINARY)
# cv2.imshow('phone_thresh',phone_thresh)
# cv2.waitKey(0)
#获取轮廓
contours=cv2.findContours(phone_thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[-2]
a_list=[]
for i in contours:
if cv2.contourArea(i)>10000:
a_list.append(i)
image=cv2.drawContours(image=phone,contours=a_list,contourIdx=-1,color=(0,255,0),thickness=2)
cv2.imshow('contours_show_10000',image)
cv2.waitKey(0)
print(len(contours))
epsilon=0.01*cv2.arcLength(contours[0],True)
approx=cv2.approxPolyDP(contours[0],epsilon,True)
print(contours[0].shape)
print(approx.shape)
phone_new=phone.copy()
image_contours=cv2.drawContours(phone_new,[approx],contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('phone',phone)
cv2.waitKey(0)
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)