OpenCV的轮廓检测
1. 轮廓检测的基本概念
轮廓是图像中连续的、闭合的曲线段,代表物体的边界(如圆形的轮廓是一条闭合曲线)。OpenCV 的轮廓检测通过 cv2.findContours()
实现,可用于形状识别、物体计数、图像分割等场景。
2. 核心函数与参数
(1)cv2.findContours()
:检测轮廓
contours, hierarchy = cv2.findContours(image, # 输入图像(必须为二值图,需提前灰度化+二值化)mode, # 轮廓检索模式(如RETR_EXTERNAL、RETR_TREE)method # 轮廓逼近方法(如CHAIN_APPROX_SIMPLE、CHAIN_APPROX_NONE)
)
返回值:
contours
:检测到的轮廓列表,每个轮廓是一个点的集合(ndarray
类型)。
hierarchy
:轮廓的层次结构(如嵌套关系),若无需层次可忽略。
- 参数说明:
mode
(轮廓检索模式):模式 含义 RETR_EXTERNAL
仅检测最外层轮廓(忽略内部嵌套的轮廓,适合简单物体计数)。 RETR_LIST
检测所有轮廓,但不建立层次关系(最快,适合无需嵌套分析的场景)。 RETR_CCOMP
检测所有轮廓,组织为两级结构(外层为连通域,内层为孔洞)。 RETR_TREE
检测所有轮廓,并建立完整的树状层次(适合嵌套轮廓,如 “矩形内的圆形”)。 method
(轮廓逼近方法):方法 含义 CHAIN_APPROX_NONE
存储轮廓的所有像素点(最详细,速度慢、占用内存大)。 CHAIN_APPROX_SIMPLE
压缩轮廓,仅保留关键顶点(如矩形仅存 4 个角点,速度快、内存小)。 CHAIN_APPROX_TC89_L1
使用 Teh-Chin 链逼近算法(适合曲线轮廓)。
(2)cv2.drawContours()
:绘制轮廓
cv2.drawContours(image, # 要绘制轮廓的目标图像contours, # 轮廓列表(来自findContours的输出)contourIdx, # 要绘制的轮廓索引(-1表示绘制所有轮廓)color, # 轮廓颜色(如(0,255,0)表示绿色)thickness=2, # 轮廓线宽度(-1表示“填充轮廓”)
)
3. 完整流程:从预处理到轮廓检测
轮廓检测对图像质量敏感,需先进行预处理(灰度化、二值化、降噪)。以下是完整代码示例:
import cv2
import numpy as np# 1. 读取并预处理图像
img = cv2.imread("shape.jpg") # 读取彩色图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度化
blur = cv2.GaussianBlur(gray, (5,5), 0) # 高斯降噪(可选,视情况而定)
_, binary = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY) # 二值化(>127设为255,否则0)# 2. 检测轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, # 检测所有轮廓并建立树状层次cv2.CHAIN_APPROX_SIMPLE # 压缩轮廓,保留关键顶点
)# 3. 绘制轮廓(绿色,线宽2)
cv2.drawContours(img, contours, -1, (0,255,0), 2) # 4. 显示结果
cv2.imshow("Contours", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 关键注意事项
预处理必须到位:
轮廓检测要求输入为二值图像(仅有黑 / 白)。若原图是彩色,需先转灰度(cv2.COLOR_BGR2GRAY
),再二值化(cv2.threshold
);若有噪声,可先高斯模糊(cv2.GaussianBlur
)。
版本兼容性:
(contours, hierarchy)
(2 个值);
OpenCV 3/4 返回 (img, contours, hierarchy)
(3 个值)。
若代码需兼容多版本,可按以下方式处理:
ret = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if len(ret) == 3:img, contours, hierarchy = ret # OpenCV 3/4
else:contours, hierarchy = ret # OpenCV 2
轮廓筛选与分析:
若需筛选特定轮廓(如面积最大的轮廓),可遍历 contours
并计算面积:
max_area = 0
max_contour = None
for cnt in contours:area = cv2.contourArea(cnt)if area > max_area:max_area = areamax_contour = cnt
5. 应用场景形状识别
通过轮廓的周长、面积、近似多边形(cv2.approxPolyDP
)判断形状(如矩形、圆形)。
物体计数:统计图像中轮廓的数量(需配合RETR_EXTERNAL
排除内部孔洞)。
图像分割:用轮廓包围区域,提取目标物体。
6.实例:
import cv2
phone =cv2.imread('phone.png')#波收颇图
phone_gray =cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
cv2.imshow('phone_gray',phone_gray)
cv2.waitKey(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)image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1,color=(0,255,0),thickness=4)
cv2.imshow('Contours_show',image_copy)
cv2.waitKey(0)# cv2.contourArea(contour[, oriented]) -> retval 轮廓面积
# oriented: 定向区域标志,默认值为 False,返回面积的绝对值,True 时则根据轮廓方向返回带符号的数值area_0 = cv2.contourArea(contours[0])
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)# arcLength(InputArray curve, bool closed) 轮廓周长
# curve: 输入的二维点集(轮廓顶点),可以是 vector 或 Mat 类型。
# closed: 用于指示曲线是否封闭。length = cv2.arcLength(contours[0], closed=True)
print(length)# 根据面积筛选显示轮廓
a_list = []
for i in contours:if cv2.contourArea(i) > 10000:a_list.append(i)image_copy = phone.copy()
image_copy = cv2.drawContours(image=image_copy, contours=a_list, contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2.imshow('Contours_show_10000', image_copy)
cv2.waitKey(0)# '''轮廓定位方法 根据轮廓面积进行排序'''
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # 选取最大面积的轮廓
image_contours = cv2.drawContours(phone.copy(), contours=[sortcnt], contourIdx=-1, color=(0, 0, 255), thickness=3) # 绘制轮廓
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
这段代码是基于 OpenCV 的图像轮廓检测与分析示例,主要实现了从图像读取、预处理到轮廓提取、筛选和排序的完整流程。以下是代码解析:
1. 导入库与读取图像
import cv2
phone = cv2.imread('phone.png') # 读取原始图像(假设为手机相关图像)
导入 OpenCV 库(cv2
),用于图像处理。
使用cv2.imread()
读取本地图像phone.png
,返回的phone
是 BGR 格式的彩色图像(OpenCV 默认读取格式)。
2. 图像预处理(灰度化与二值化)
# 灰度化:将彩色图像转为单通道灰度图
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
cv2.imshow('phone_gray', phone_gray) # 显示灰度图
cv2.waitKey(0) # 等待用户按键(0表示无限等待)# 二值化:将灰度图转为黑白二值图(轮廓检测的前提)
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
cv2.imshow('phone_binary', phone_binary) # 显示二值图
cv2.waitKey(0)
灰度化:通过cv2.cvtColor()
将 BGR 彩色图转为灰度图(单通道),减少计算量,为后续处理简化图像。
二值化:使用cv2.threshold()
将灰度图转为黑白二值图:
阈值设为 120,即灰度值 > 120 的像素设为 255(白色),≤120 的设为 0(黑色)。cv2.THRESH_BINARY
是二值化模式,返回ret
(阈值)和二值化结果phone_binary
。
cv2.imshow()
和cv2.waitKey(0)
用于显示图像并暂停,等待用户确认后继续。
3. 轮廓检测与绘制
# 检测轮廓(OpenCV 3/4版本返回3个值,用_忽略第一个)
_, contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)# 复制原始图像,在副本上绘制所有轮廓
image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=contours, # 轮廓列表contourIdx=-1, # -1表示绘制所有轮廓color=(0,255,0), # 轮廓颜色(绿色,BGR格式)thickness=4 # 轮廓线宽
)
cv2.imshow('Contours_show', image_copy) # 显示所有轮廓
cv2.waitKey(0)
轮廓检测:cv2.findContours()
从二值图中提取轮廓:
输入:二值图phone_binary
(背景为黑,目标为白)。
参数cv2.RETR_TREE
:检测所有轮廓,并保留完整的层次关系(如嵌套轮廓的父子关系)。
参数cv2.CHAIN_APPROX_NONE
:存储轮廓的所有像素点(不压缩,最完整但内存占用大)。
返回:contours
(轮廓列表,每个轮廓是像素坐标的数组)、hierarchy
(轮廓层次信息)。
绘制轮廓:cv2.drawContours()
在图像副本上绘制轮廓,避免修改原图。
4. 轮廓特征计算(面积与周长)
# 计算轮廓面积(第一个和第二个轮廓)
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) # 打印第一个轮廓的周长
轮廓面积:cv2.contourArea(contour)
计算单个轮廓的面积(单位:像素 ²)。
轮廓周长:cv2.arcLength(curve, closed)
计算轮廓周长:closed=True
表示轮廓是闭合的(如圆形、矩形)。
5. 按面积筛选轮廓
# 筛选面积>10000的轮廓(去除小面积噪声)
a_list = []
for i in contours:if cv2.contourArea(i) > 10000:a_list.append(i)# 绘制筛选后的轮廓
image_copy = phone.copy()
image_copy = cv2.drawContours(image=image_copy, contours=a_list, # 仅包含大面积轮廓的列表contourIdx=-1, color=(0, 255, 0), thickness=3
)
cv2.imshow('Contours_show_10000', image_copy) # 显示筛选结果
cv2.waitKey(0)
实际场景中,检测到的轮廓可能包含噪声(如小斑点),通过面积阈值(10000)筛选出有意义的大轮廓。
遍历所有轮廓,将面积大于 10000 的轮廓存入a_list
,再绘制这些轮廓。
6. 按面积排序并提取最大轮廓
# 按面积降序排序轮廓,取最大面积的轮廓
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0]# 绘制最大面积的轮廓(红色)
image_contours = cv2.drawContours(phone.copy(), contours=[sortcnt], # 注意:contours参数需传入列表,这里用[sortcnt]包装contourIdx=-1, color=(0, 0, 255), # 红色(BGR格式)thickness=3
)
cv2.imshow('image_contours', image_contours) # 显示最大轮廓
cv2.waitKey(0)
sorted(contours, key=cv2.contourArea, reverse=True)
:按轮廓面积降序排序,reverse=True
表示从大到小。
[0]
取排序后的第一个元素(面积最大的轮廓)。
绘制时,contours
参数需传入列表,因此用[sortcnt]
包装单个轮廓。
总结
这段代码完整演示了轮廓检测的典型流程:
读取图像 → 灰度化 → 二值化 → 检测轮廓 → 分析轮廓特征(面积、周长) → 筛选 / 排序轮廓 → 可视化结果。
核心目的是从图像中提取目标的边界(轮廓),并通过面积等特征筛选出感兴趣的目标)。