实战项目实现以下功能:
对图片 hua.png
进行轮廓提取,并在同一窗口中完成以下两个绘制操作:
-
用红色 画出花的外部轮廓(即最外层轮廓)
-
用绿色 画出该轮廓的近似多边形 ,其中近似精度参数
ε
设置为轮廓周长的 0.005
项目完整代码:
python
import cv2
hua = cv2.imread('picture/hua.png')#读取原图
hua_gray = cv2.cvtColor(hua,cv2.COLOR_BGR2GRAY)#灰度图的处理
cv2.imshow('hua_b',hua_gray)
cv2.waitKey(0)
# hua_gray=cv2.imread('hua.png',0) #读取灰度图
ret, hua_binary = cv2.threshold(hua_gray, 240, 255, cv2.THRESH_BINARY_INV)#阈值处理为二值
cv2.imshow('hua_binary',hua_binary)
cv2.waitKey(0)
_,contours, hierarchy = cv2.findContours(hua_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #cv2.RETR_EXTERNAL:只检测外轮廓(黑底),所有子轮廓被忽略
# _,contours, hierarchy = cv2.findContours(hua_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# sortcnt=contours[0]
print(len(contours))
# image_copy = hua.copy()
# cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1,color=(0,255,0),thickness=2)
# cv2.imshow('Contours_show', image_copy)
# cv2.waitKey(0)
image_copy = hua.copy()
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # 选取最大面积的轮廓
image_contours = cv2.drawContours(image_copy,[sortcnt],contourIdx=-1,color=(0,0,255),thickness=3)#绘制轮廓
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
epsilon = 0.005 * cv2.arcLength(sortcnt,True) #设置近似精度 【h要<ε;ε越小,点越多,越精确】
approx = cv2.approxPolyDP(sortcnt, epsilon, True) #对轮廓进行近似
print(sortcnt.shape)
print(approx.shape)
image_contours = cv2.drawContours(image_copy,[approx],contourIdx=-1,color=(0,255,0),thickness=3)#绘制轮廓
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
整体功能概述
此代码实现了以下核心功能:
- 图像预处理 → 灰度转换 + 反向二值化
- 轮廓检测 → 提取所有轮廓并筛选最大面积轮廓
- 轮廓可视化 → 绘制原始轮廓与多边形逼近结果
- 几何简化 → 使用 Douglas-Peucker 算法对轮廓进行多点精简
模块 1: 图像加载与预处理
python
import cv2
hua = cv2.imread('picture/hua.png') # ① 读取原图
hua_gray = cv2.cvtColor(hua, cv2.COLOR_BGR2GRAY) # ② 转为灰度图
cv2.imshow('hua_b', hua_gray) # ③ 显示灰度图
cv2.waitKey(0) # ④ 暂停等待按键
关键点解析:
- 灰度转换必要性:减少颜色干扰,聚焦亮度信息,提升后续二值化效果
cv2.COLOR_BGR2GRAY
:OpenCV 默认 BGR 色彩空间,此处正确转换- 可视化验证:通过
imshow
确保灰度转换无误
模块 2: 二值化处理
python
ret, hua_binary = cv2.threshold(hua_gray, 240, 255, cv2.THRESH_BINARY_INV) # ⑤ 反向二值化
cv2.imshow('hua_binary', hua_binary) # ⑥ 显示二值图
cv2.waitKey(0)
关键参数详解:
参数 | 值 | 说明 |
---|---|---|
thresh=240 |
阈值 | 像素值 >240 → 设为255(白);≤240 → 设为0(黑) |
maxval=255 |
饱和值 | 超过阈值时的赋值上限 |
THRESH_BINARY_INV |
反向模式 | 关键! 将高亮区域变为黑色背景,低亮区域变为白色前景 |
为何选择反向模式?
如果花朵主体比背景更暗(常见情况),正向二值化会使背景变白,导致轮廓断裂。反向模式可保留暗色前景的完整性。
模块 3: 轮廓检测
python
_, contours, hierarchy = cv2.findContours(hua_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # ⑦ 检测所有轮廓
print(len(contours)) # ⑧ 输出轮廓数量
核心概念解析:
-
三个返回值:
_
:忽略的retval
(通常无用)contours
:存储所有轮廓点的列表(每个轮廓是一个 numpy 数组)hierarchy
:轮廓层级关系矩阵(父子兄弟关系)
-
关键参数:
cv2.RETR_TREE
:检索所有轮廓并建立完整层级树(包括嵌套轮廓)cv2.CHAIN_APPROX_SIMPLE
:压缩水平/垂直方向连续点,仅保留端点
典型轮廓数量 :若输出
n
,表示检测到n
个独立轮廓(含嵌套结构)
模块 4: 筛选最大面积轮廓
python
image_copy = hua.copy() # ⑨ 创建原图副本用于绘图
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # ⑩ 按面积降序排序,取最大轮廓
关键操作:
cv2.contourArea()
:计算单个轮廓的面积(像素数)sorted(..., reverse=True)
:按面积从大到小排序[0]
:取排序后的第一个元素(最大面积轮廓)
注意事项 :
如果图像存在多个相似大小的物体,此方法可能无法稳定选取目标。建议结合位置/形状特征进一步过滤。
模块 5: 绘制原始轮廓
python
image_contours = cv2.drawContours(image_copy, [sortcnt], contourIdx=-1, color=(0,0,255), thickness=3) # ⑪ 绘制红色轮廓
cv2.imshow('image_contours', image_contours) # ⑫ 显示结果
cv2.waitKey(0)
绘图参数详解:
参数 | 值 | 说明 |
---|---|---|
image |
image_copy |
在原图副本上绘制 |
contours=[sortcnt] |
仅绘制最大轮廓 | 注意传入的是单元素列表 |
contourIdx=-1 |
绘制全部层级 | -1 表示绘制轮廓的所有层级 |
color=(0,0,255) |
纯红色 | BGR 格式 |
thickness=3 |
线宽 | 较大的线宽使轮廓更醒目 |
模块 6: 多边形逼近(几何简化)
python
epsilon = 0.005 * cv2.arcLength(sortcnt, True) # ⑬ 计算近似精度(基于轮廓周长)
approx = cv2.approxPolyDP(sortcnt, epsilon, True) # ⑭ 执行多边形逼近
print(sortcnt.shape) # ⑮ 输出原始轮廓点数
print(approx.shape) # ⑯ 输出逼近后点数
核心算法解析:
-
近似精度计算:
cv2.arcLength(sortcnt, True)
:计算轮廓总长度(闭合路径)epsilon = 0.005 * 周长
:控制逼近误差容忍度(经验值)- 原理:ε 越小,保留的细节越多;越大,简化程度越高
-
Douglas-Peucker 算法:
- 递归删除偏离直线距离小于 ε 的中间点
closed=True
:保证首尾相连形成闭合多边形
点数对比示例 :
假设原始轮廓有 1000 个点 → 逼近后可能只剩 10-20 个顶点,大幅减少数据量
模块 7: 绘制逼近轮廓
python
image_contours = cv2.drawContours(image_copy, [approx], contourIdx=-1, color=(0,255,0), thickness=3) # ⑰ 绘制绿色逼近轮廓
cv2.imshow('image_contours', image_contours) # ⑱ 显示结果
cv2.waitKey(0)
可视化对比:
颜色 | 含义 | 特点 |
---|---|---|
红色 | 原始轮廓 | 显示实际边缘细节 |
绿色 | 多边形逼近结果 | 展示几何简化后的顶点分布 |
应用场景 :
可用于形状识别(如判断是否为多边形)、尺寸测量、碰撞检测等需要简化几何表示的场景
常见问题与优化建议
-
二值化失效怎么办?
- 调整阈值(尝试
127
或自适应阈值adaptiveThreshold
) - 添加高斯模糊去噪:
blurred = cv2.GaussianBlur(hua_gray, (5,5), 0)
- 调整阈值(尝试
-
轮廓断裂如何处理?
- 提高阈值使前景更连贯
- 使用形态学操作连接断开部分:
kernel = np.ones((3,3), np.uint8); closed = cv2.morphologyEx(hua_binary, cv2.MORPH_CLOSE, kernel)
-
多目标场景改进方案:
python# 过滤小面积噪声 min_area = 100 # 根据实际调整 valid_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area]
完整流程总结
python
读取图像 → 灰度转换 → 反向二值化 → 检测所有轮廓 → 筛选最大轮廓 → 绘制原始轮廓 → 多边形逼近 → 绘制简化轮廓