目录
[1. cv2.findContours()](#1. cv2.findContours())
[2. cv2.drawContours()](#2. cv2.drawContours())
[1. 基本轮廓检测与绘制](#1. 基本轮廓检测与绘制)
[2. 检测最外层轮廓](#2. 检测最外层轮廓)
[3. 轮廓层次结构](#3. 轮廓层次结构)
[4. 轮廓近似](#4. 轮廓近似)
[1. 形状识别](#1. 形状识别)
[2. 物体计数](#2. 物体计数)
[1. 图像预处理的重要性](#1. 图像预处理的重要性)
[2. 轮廓检索模式的选择](#2. 轮廓检索模式的选择)
[3. 轮廓近似方法的选择](#3. 轮廓近似方法的选择)
[4. 过滤小轮廓](#4. 过滤小轮廓)
[5. 轮廓排序](#5. 轮廓排序)
[1. 轮廓检测流程:](#1. 轮廓检测流程:)
[2. 关键函数:](#2. 关键函数:)
[3. 应用场景:](#3. 应用场景:)
[4. 技巧与注意事项:](#4. 技巧与注意事项:)
一、轮廓的基本概念
轮廓是图像中具有相同颜色或灰度的连续曲线,代表了图像中物体的边界。轮廓检测是计算机视觉中的重要任务,广泛应用于物体识别、图像分割、形状分析等领域。
轮廓与边缘的区别
边缘:是图像中灰度值急剧变化的像素点的集合,代表了图像中亮度变化的位置。
轮廓:是连续的边缘点的集合,代表了物体的完整边界。
简单来说,轮廓是连接起来的边缘,形成了物体的完整形状。
二、轮廓检测的基本流程
在OpenCV中,轮廓检测通常遵循以下流程:
图像预处理:将彩色图像转换为灰度图像
阈值分割:将灰度图像转换为二值图像
轮廓检测:从二值图像中提取轮廓
轮廓绘制:将检测到的轮廓绘制到原图像上
三、OpenCV中的轮廓检测函数
1. cv2.findContours()
cv2.findContours()函数用于从二值图像中检测轮廓:
//python
contours, hierarchy = cv2.findContours(image, mode, method)
参数说明:
image:输入图像(必须是二值图像)
mode:轮廓检索模式
cv2.RETR_EXTERNAL:只检测最外层轮廓
cv2.RETR_LIST:检测所有轮廓,不建立层次关系
cv2.RETR_CCOMP:检测所有轮廓,建立两层层次关系
cv2.RETR_TREE:检测所有轮廓,建立完整的层次关系
method:轮廓近似方法
cv2.CHAIN_APPROX_NONE:存储所有轮廓点
cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角线方向的点,只保留端点
cv2.CHAIN_APPROX_TC89_L1:使用TehChin链近似算法
cv2.CHAIN_APPROX_TC89_KCOS:使用TehChin链近似算法
返回值:
contours:轮廓列表,每个轮廓是一个Numpy数组,包含轮廓点的坐标
hierarchy:轮廓层次信息,是一个Numpy数组,包含轮廓的父、子、前、后轮廓索引
2. cv2.drawContours()
cv2.drawContours()函数用于将轮廓绘制到图像上:
python
image = cv2.drawContours(image, contours, contourIdx, color, thickness)
参数说明:
image:目标图像
contours:轮廓列表
contourIdx:要绘制的轮廓索引(1表示绘制所有轮廓)
color:绘制颜色(BGR格式)
thickness:绘制线宽(1表示填充轮廓内部)
四、轮廓检测与绘制实战
1. 基本轮廓检测与绘制
//python
import cv2
import numpy as np
from matplotlib import pyplot as plt
读取图像
img = cv2.imread('shapes.jpg')
转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
高斯模糊,减少噪声
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
阈值分割,生成二值图像
ret, thresh = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY)
检测轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
绘制所有轮廓
img_contours = cv2.drawContours(img.copy(), contours, 1, (0, 255, 0), 3)
显示结果
plt.figure(figsize=(12, 6))
plt.subplot(131), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('原图')
plt.subplot(132), plt.imshow(thresh, cmap='gray'), plt.title('二值图像')
plt.subplot(133), plt.imshow(cv2.cvtColor(img_contours, cv2.COLOR_BGR2RGB)), plt.title('轮廓检测结果')
plt.tight_layout()
plt.show()
2. 检测最外层轮廓
//python
import cv2
import numpy as np
from matplotlib import pyplot as plt
读取图像
img = cv2.imread('shapes.jpg')
转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
阈值分割
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
检测最外层轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
绘制最外层轮廓
img_contours = cv2.drawContours(img.copy(), contours, 1, (0, 0, 255), 2)
显示结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('原图')
plt.subplot(122), plt.imshow(cv2.cvtColor(img_contours, cv2.COLOR_BGR2RGB)), plt.title('最外层轮廓')
plt.tight_layout()
plt.show()
3. 轮廓层次结构
//python
import cv2
import numpy as np
from matplotlib import pyplot as plt
读取图像
img = cv2.imread('hierarchy.jpg')
转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
阈值分割
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
检测轮廓并获取层次信息
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
绘制所有轮廓
img_contours = img.copy()
为不同层次的轮廓分配不同颜色
colors = [(0, 255, 0), (0, 0, 255), (255, 0, 0), (255, 255, 0)]
for i, contour in enumerate(contours):
color = colors[i % len(colors)]
cv2.drawContours(img_contours, [contour], 1, color, 2)
#在轮廓中心绘制层次信息
M = cv2.moments(contour)
if M['m00'] > 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
cv2.putText(img_contours, str(i), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
#显示结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('原图')
plt.subplot(122), plt.imshow(cv2.cvtColor(img_contours, cv2.COLOR_BGR2RGB)), plt.title('轮廓层次结构')
plt.tight_layout()
plt.show()
#打印层次信息
print('层次信息:', hierarchy)
4. 轮廓近似
//python
import cv2
import numpy as np
from matplotlib import pyplot as plt
#读取图像
img = cv2.imread('hand.jpg')
#转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#阈值分割
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
#检测轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#获取最大轮廓
max_contour = max(contours, key=cv2.contourArea)
#轮廓近似
epsilon1 = 0.01 cv2.arcLength(max_contour, True)
approx1 = cv2.approxPolyDP(max_contour, epsilon1, True)
epsilon2 = 0.05 cv2.arcLength(max_contour, True)
approx2 = cv2.approxPolyDP(max_contour, epsilon2, True)
#绘制结果
img_original = cv2.drawContours(img.copy(), [max_contour], 1, (0, 255, 0), 2)
img_approx1 = cv2.drawContours(img.copy(), [approx1], 1, (0, 0, 255), 2)
img_approx2 = cv2.drawContours(img.copy(), [approx2], 1, (255, 0, 0), 2)
#显示结果
plt.figure(figsize=(15, 5))
plt.subplot(131), plt.imshow(cv2.cvtColor(img_original, cv2.COLOR_BGR2RGB)), plt.title('原始轮廓')
plt.subplot(132), plt.imshow(cv2.cvtColor(img_approx1, cv2.COLOR_BGR2RGB)), plt.title('近似轮廓 (epsilon=0.01)')
plt.subplot(133), plt.imshow(cv2.cvtColor(img_approx2, cv2.COLOR_BGR2RGB)), plt.title('近似轮廓 (epsilon=0.05)')
plt.tight_layout()
plt.show()
print('原始轮廓点数:', len(max_contour))
print('近似轮廓1点数:', len(approx1))
print('近似轮廓2点数:', len(approx2))
五、实际应用案例
1. 形状识别
//python
import cv2
import numpy as np
from matplotlib import pyplot as plt
读取图像
img = cv2.imread('shapes.jpg')
转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
高斯模糊
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
阈值分割
ret, thresh = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY)
检测轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
形状识别函数
def get_shape(contour):
shape = "unidentified"
peri = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.04 peri, True)
根据顶点数量判断形状
if len(approx) == 3:
shape = "三角形"
elif len(approx) == 4:
检查是否为矩形
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
shape = "正方形" if ar >= 0.95 and ar <= 1.05 else "矩形"
elif len(approx) == 5:
shape = "五边形"
elif len(approx) == 6:
shape = "六边形"
else:
shape = "圆形"
return shape
识别并标记形状
img_shapes = img.copy()
for contour in contours:
计算轮廓面积,过滤小轮廓
area = cv2.contourArea(contour)
if area < 100:
continue
获取形状名称
shape = get_shape(contour)
计算轮廓中心
M = cv2.moments(contour)
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
绘制轮廓和形状名称
cv2.drawContours(img_shapes, [contour], 1, (0, 255, 0), 2)
cv2.putText(img_shapes, shape, (cx 50, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
显示结果
plt.figure(figsize=(10, 6))
plt.imshow(cv2.cvtColor(img_shapes, cv2.COLOR_BGR2RGB))
plt.title('形状识别结果')
plt.axis('off')
plt.show()
2. 物体计数
//python
import cv2
import numpy as np
from matplotlib import pyplot as plt
#读取图像
img = cv2.imread('coins.jpg')
#转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#高斯模糊
blurred = cv2.GaussianBlur(gray, (11, 11), 0)
Canny边缘检测
canny = cv2.Canny(blurred, 30, 150)
#膨胀操作,连接断裂的边缘
dilated = cv2.dilate(canny, None, iterations=2)
#检测轮廓
contours, hierarchy = cv2.findContours(dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#绘制轮廓
img_coins = cv2.drawContours(img.copy(), contours, 1, (0, 255, 0), 2)
#在图像上显示计数结果
cv2.putText(img_coins, f"硬币数量: {len(contours)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
#显示结果
plt.figure(figsize=(12, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('原图')
plt.subplot(122), plt.imshow(cv2.cvtColor(img_coins, cv2.COLOR_BGR2RGB)), plt.title('硬币计数结果')
plt.tight_layout()
plt.show()
print(f"检测到的硬币数量: {len(contours)}")
六、轮廓检测的技巧与注意事项
1. 图像预处理的重要性
噪声处理:使用高斯模糊(cv2.GaussianBlur())减少图像噪声
阈值分割:选择合适的阈值方法(全局阈值或自适应阈值)
边缘检测:对于复杂图像,可以使用Canny边缘检测代替直接阈值分割
2. 轮廓检索模式的选择
cv2.RETR_EXTERNAL:只需要最外层轮廓时使用
cv2.RETR_LIST:不需要层次信息时使用
cv2.RETR_CCOMP:需要两层层次信息时使用
cv2.RETR_TREE:需要完整层次信息时使用
3. 轮廓近似方法的选择
cv2.CHAIN_APPROX_NONE:需要保留所有轮廓点时使用
cv2.CHAIN_APPROX_SIMPLE:需要减少数据量时使用,通常能满足大多数需求
4. 过滤小轮廓
通过计算轮廓面积(cv2.contourArea())可以过滤掉噪声或不需要的小轮廓:
//python
min_area = 100 设置最小面积阈值
filtered_contours = [contour for contour in contours if cv2.contourArea(contour) > min_area]
5. 轮廓排序
可以根据轮廓的位置或面积对轮廓进行排序:
//python
#根据x坐标排序
contours = sorted(contours, key=lambda c: cv2.boundingRect(c)[0])
#根据面积排序
contours = sorted(contours, key=cv2.contourArea, reverse=True)
七、总结
轮廓检测是OpenCV中图像处理的重要功能,通过cv2.findContours()和cv2.drawContours()函数可以方便地实现轮廓的检测和绘制。
主要内容回顾
1. 轮廓检测流程:
图像预处理 → 阈值分割 → 轮廓检测 → 轮廓绘制
2. 关键函数:
cv2.findContours():检测轮廓
cv2.drawContours():绘制轮廓
cv2.approxPolyDP():轮廓近似
3. 应用场景:
形状识别
物体计数
图像分割
目标检测
4. 技巧与注意事项:
做好图像预处理,减少噪声影响
根据需求选择合适的轮廓检索模式和近似方法
使用轮廓面积过滤小轮廓
对轮廓进行排序,方便后续处理
通过掌握轮廓检测技术,可以解决许多计算机视觉中的实际问题,为更高级的图像处理任务打下基础。