OpenCV轮廓检测与绘制实战

目录

一、轮廓的基本概念

轮廓与边缘的区别

二、轮廓检测的基本流程

三、OpenCV中的轮廓检测函数

[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中,轮廓检测通常遵循以下流程:

  1. 图像预处理:将彩色图像转换为灰度图像

  2. 阈值分割:将灰度图像转换为二值图像

  3. 轮廓检测:从二值图像中提取轮廓

  4. 轮廓绘制:将检测到的轮廓绘制到原图像上

三、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. 技巧与注意事项:

做好图像预处理,减少噪声影响

根据需求选择合适的轮廓检索模式和近似方法

使用轮廓面积过滤小轮廓

对轮廓进行排序,方便后续处理

通过掌握轮廓检测技术,可以解决许多计算机视觉中的实际问题,为更高级的图像处理任务打下基础。

相关推荐
许泽宇的技术分享2 小时前
当AI开始“画“界面:A2UI协议如何让.NET应用告别写死的UI
人工智能·ui·.net·blazor·a2ui
程序员佳佳2 小时前
文章标题:彻底抛弃OpenAI官方Key?实测GPT-5.2与Banana Pro(Gemini 3):这才是开发者的终极红利!
开发语言·人工智能·python·gpt·ai作画·api·midjourney
行走的bug...2 小时前
利用计算机辅助数学运算
人工智能·算法·机器学习
大模型RAG和Agent技术实践2 小时前
从零构建:基于 LangGraph 的医疗问诊智能体实战(完整源代码)
人工智能·langchain·agent·langgraph
tiannian12202 小时前
如何选择适合企业的RFID系统解决方案?
大数据·人工智能
生成论实验室2 小时前
生成何以智能?——论道法术器贯通的生成式AGI新范式及其技术实现
人工智能·科技·神经网络·信息与通信·几何学
WhereIsMyChair2 小时前
BatchNorm、LayerNorm和RMSNorm的区别
人工智能·语言模型
噜~噜~噜~2 小时前
D-CBRS(Diverse Class-Balancing Reservoir Sampling )的个人理解
人工智能·深度学习·持续学习·cbrs·d-cbrs
Kiyra2 小时前
LinkedHashMap 源码阅读
java·开发语言·网络·人工智能·安全·阿里云·云计算