新闻中心
OpenCV中动态合并多个轮廓的实用指南

本文旨在介绍如何在OpenCV中高效地合并多个轮廓。针对`cv.findContours()`返回的可变数量轮廓,传统循环内直接使用`np.vstack`可能导致数据丢失。教程将详细阐述正确的合并策略,即先将所有待合并轮廓收集到一个列表中,然后利用NumPy的`vstack`函数一次性完成合并操作,从而确保所有轮廓数据被完整整合。
在图像处理任务中,我们经常需要使用OpenCV的cv.findContours()函数来检测图像中的轮廓。该函数返回一个轮廓列表,每个轮廓通常是一个NumPy数组,表示构成该轮廓的所有点。在某些应用场景下,我们可能需要将多个检测到的轮廓合并成一个单一的轮廓数组,例如,当多个轮廓属于同一个逻辑对象,或者需要对一组特定轮廓进行统一处理时。
理解OpenCV中的轮廓数据结构
cv.findContours()函数返回的轮廓通常是一个Python列表,列表中的每个元素都是一个NumPy数组。这些NumPy数组的形状通常是 (N, 1, 2),其中 N 是轮廓中的点数,1 是一个额外的维度(有时可以省略),2 表示每个点的 (x, y) 坐标。
例如,以下代码演示了如何获取并查看轮廓的基本信息:
import cv2 as cv
import numpy as np
# 假设 img 是一张二值图像
# 为了演示,我们创建一个简单的图像
img = np.zeros((100, 100), dtype=np.uint8)
cv.rectangle(img, (10, 10), (30, 30), 255, -1)
cv.circle(img, (70, 70), 20, 255, -1)
cv.rectangle(img, (15, 15), (25, 25), 0, -1) # 制造一个内部轮廓
# 查找轮廓
data_contours, _ = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
# 对轮廓按长度进行排序(从大到小)
data_cnt = sorted(data_contours, key=len, reverse=True)
# 打印轮廓数量和每个轮廓的长度
data_cnt_len = [len(cnt) for cnt in data_cnt]
print(f"检测到的轮廓总数: {len(data_contours)}")
print(f"排序后轮廓的长度: {data_cnt_len}")
# 示例输出可能为:
# 检测到的轮廓总数: 3
# 排序后轮廓的长度: [100, 80, 4] (具体数值取决于图像和轮廓查找模式)轮廓合并的常见误区与正确策略
当需要合并多个轮廓时,一个常见的错误是在循环中直接使用np.vstack()。例如,如果目标是合并前两个最大的轮廓:
# 错误的合并尝试
# contour_number = 2 # 假设要合并前两个轮廓
#
# merged_contours_incorrect = None
# for i in range(contour_number):
# # 每次循环都会创建一个新的 NumPy 数组,并覆盖前一个结果
# # 这导致最终只保留了循环中最后一个轮廓的数据
# merged_contours_incorrect = np.vstack(data_cnt[i])
#
# if merged_contours_incorrect is not None:
# print(f"错误合并后的形状: {merged_contours_incorrect.shape}")
# # 示例输出可能为: (407, 2) 或 (80, 2) - 只保留了最后一个轮廓的形状上述代码的问题在于,np.vstack()在每次循环中都会将当前的data_cnt[i]转换成一个NumPy数组(如果它本身不是),并将其赋值给merged_contours_incorrect。这意味着每次迭代都会覆盖前一次的结果,最终merged_contours_incorrect只包含了循环中最后一个轮廓的数据。
AdMaker AI
从0到爆款高转化AI广告生成器
65
查看详情
正确的合并策略是先将所有需要合并的轮廓收集到一个Python列表中,然后再对这个列表使用np.vstack()。np.vstack()函数能够接受一个NumPy数组的序列(如列表或元组),并将它们按垂直方向堆叠起来。
实施正确的轮廓合并
以下是合并多个轮廓的正确方法:
- 初始化一个空列表:用于存储所有待合并的轮廓。
- 遍历并追加轮廓:在循环中,将每个需要合并的轮廓追加到这个列表中。
- 一次性堆叠:循环结束后,使用np.vstack()函数对整个列表进行操作,完成所有轮廓的合并。
# 正确的轮廓合并方法
merged_contours_list = [] # 创建一个空列表来存储轮廓
contour_number_to_merge = 2 # 假设我们要合并前两个最大的轮廓
for i in range(contour_number_to_merge):
merged_contours_list.append(data_cnt[i])
# 使用 np.vstack 将列表中的所有轮廓一次性堆叠起来
if merged_contours_list: # 确保列表不为空
final_merged_contour = np.vstack(merged_contours_list)
print(f"正确合并后的形状: {final_merged_contour.shape}")
# 示例输出可能为: (180, 1, 2) 或 (180, 2) - 两个轮廓点数的总和
else:
print("没有轮廓可供合并。")通过这种方法,final_merged_contour将包含所有被追加到merged_contours_list中的轮廓点数据。其形状的第一个维度将是所有合并轮廓点数的总和。
完整示例
为了提供一个更完整的上下文,我们结合图像创建、轮廓查找、排序和合并的整个流程:
import cv2 as cv
import numpy as np
def merge_selected_contours(image_path, num_to_merge=2):
"""
加载图像,查找轮廓,排序,并合并指定数量的最大轮廓。
Args:
image_path (str): 图像文件路径。
num_to_merge (int): 要合并的最大轮廓数量。
Returns:
np.ndarray: 合并后的轮廓数组,如果无轮廓则返回 None。
"""
img = cv.imread(image_path, cv.IMREAD_GRAYSCALE)
if img is None:
print(f"错误: 无法加载图像 '{imag
e_path}'")
return None
# 对图像进行二值化处理,以便 cv.findContours 工作
_, binary_img = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# 查找轮廓
contours_found, _ = cv.findContours(binary_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
if not contours_found:
print("未检测到任何轮廓。")
return None
# 按轮廓点数从大到小排序
sorted_contours = sorted(contours_found, key=len, reverse=True)
print(f"检测到的轮廓总数: {len(sorted_contours)}")
print(f"排序后轮廓的长度: {[len(c) for c in sorted_contours]}")
# 准备合并
contours_to_merge = []
# 确保不会尝试合并超过实际存在的轮廓数量
actual_num_to_merge = min(num_to_merge, len(sorted_contours))
print(f"将合并前 {actual_num_to_merge} 个轮廓。")
for i in range(actual_num_to_merge):
contours_to_merge.append(sorted_contours[i])
if contours_to_merge:
final_merged_contour = np.vstack(contours_to_merge)
print(f"合并后的轮廓形状: {final_merged_contour.shape}")
return final_merged_contour
else:
print("没有轮廓被选中进行合并。")
return None
# 创建一个虚拟图像文件进行演示
dummy_image_path = "dummy_contours.png"
dummy_img = np.zeros((200, 200), dtype=np.uint8)
cv.rectangle(dummy_img, (20, 20), (80, 80), 255, -1)
cv.circle(dummy_img, (150, 150), 30, 255, -1)
cv.imwrite(dummy_image_path, dummy_img)
# 调用函数进行合并
merged_contour_result = merge_selected_contours(dummy_image_path, num_to_merge=2)
if merged_contour_result is not None:
# 可以在这里对合并后的轮廓进行进一步处理,例如绘制
output_img = np.zeros((200, 200, 3), dtype=np.uint8)
cv.drawContours(output_img, [merged_contour_result], -1, (0, 255, 0), 2)
cv.imshow("Merged Contours", output_img)
cv.waitKey(0)
cv.destroyAllWindows()注意事项与最佳实践
- 数据类型和维度一致性:np.vstack()要求所有待堆叠的数组在除第一个维度(行数)之外的所有维度上都保持一致。OpenCV返回的轮廓通常满足这一条件,即它们的形状都是 (N, 1, 2) 或 (N, 2)。
- 效率:对于大量的NumPy数组合并,先将它们收集到一个Python列表中,然后调用一次np.vstack()通常比在循环中反复调用np.vstack()更高效。这是因为后者可能涉及多次内存重新分配和数据复制。
- 内存管理:合并非常大的轮廓或大量轮廓可能会占用大量内存。在处理大型图像或复杂轮廓时,请注意内存使用情况。
- 轮廓排序:在合并之前对轮廓进行排序(例如按面积、长度或位置)是一个常见的预处理步骤,这取决于你的具体需求。本教程中的示例展示了按轮廓长度排序。
- 空轮廓列表处理:在调用np.vstack()之前,最好检查一下用于存储轮廓的列表是否为空,以避免在列表为空时引发错误。
总结
在OpenCV中合并多个轮廓时,核心策略是避免在循环中直接使用np.vstack()。正确的做法是创建一个Python列表来暂存所有需要合并的轮廓,然后在循环结束后,对这个列表调用一次np.vstack()函数。这种方法不仅能确保所有轮廓数据被完整地整合,而且在处理大量轮廓时也更为高效。通过理解轮廓的数据结构和np.vstack()的工作原理,开发者可以更准确、高效地处理图像中的复杂轮廓数据。
以上就是OpenCV中动态合并多个轮廓的实用指南的详细内容,更多请关注其它相关文章!
# windows
# 中华图库网站建设素材
# 谷歌关键词布局seo
# 服务网站建设多少钱
# 扬州营销网站优化联系人
# 怎么选网络关键词排名
# 哪个网站可以优化到首页
# 黄金推广营销话术大全
# 为空
# 先将
# 能为
# 都是
# 列表中
# 检测到
# 数据结构
# 创建一个
# 是一个
# 多个
# 数据丢失
# win
# ai
# app
# python
# 眉山seo是什么排名
# 新保健品推广营销方案
# 兰州seo优化收费
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
优化大型XML文件解析:基于Python流式处理的内存高效方案
C++的std::forward_list怎么用_C++ STL中单向链表容器的特点与应用
CSS条件样式无法按设备触发怎么排查_media条件语句正确设置解决触发问题
如何仅使用CSS更改登录界面背景图像图标的颜色
痛风发作了怎么办? 快速止痛和后期饮食调理
WordPress插件开发:正确注册卸载钩子与避免常见陷阱
反效果?《战地6》免费试玩开启后玩家数不升反降
Golang如何使用new_Go new分配内存机制讲解
如何将一个大型PHP应用拆分为多个Composer包_微服务与模块化架构的Composer实践
Node.js中HTML按钮与J*aScript函数交互的正确姿势
mc.js免安装版 mc.js一键畅玩入口
windows10怎么查看本机ip_windows10命令提示符ipconfig使用
Sublime怎么配置Nim语言环境_Sublime Nim代码高亮与补全
处理Kafka消费者会话超时:深入理解消息处理语义与幂等性
Golang如何测试channel通信行为_Golang channel通信测试与分析方法
如何使用spryker/configurable-bundles-products-resource-relationship模块解决复杂产品捆绑关系难题
126邮箱账号注册 电脑版登录入口
iwriter统一登录平台 iwrite账号密码登录页面
C++如何生成随机数_C++ random库使用方法与范围设置
Go语言中对Map值调用带指针接收者方法:原理与最佳实践
Golang如何安装Swagger工具_GoSwagger文档生成环境
解决macOS上安装pyhdf时‘hdf.h’文件缺失的编译错误
Golang如何实现Web文件静态资源服务器_Golang静态资源服务器开发与实践
J*aScript对象创建方式_J*aScript设计模式应用
12306选座如何查看座位示意图_12306座位示意图解读与使用
python3时间如何用calendar输出?
使用Pandas转换并合并DataFrame:多列映射至统一结构
J*aScript 字符串标签转换:使用正则表达式高效替换
如何提高微信支付的安全性_微信支付安全防护与设置建议
在J*aScript中复现SciPy的B样条拟合与求值:关键考量
UC浏览器网页版登录入口官网 电脑版网址入口
html怎么在cmd下运行php文件_cmd运行html中php文件方法【教程】
Go语言中高效处理x-www-form-urlencoded表单数据
Win10文件资源管理器“此电脑”分组怎么关 Win10恢复经典视图【技巧】
C++ explicit关键字防止隐式转换_C++构造函数安全规范
如何使用纯J*aScript判断Input元素是否在特定类容器内
VS Code远程开发时如何处理文件权限问题
黑猫投诉统一入口官网 消费者权益保护投诉平台
小米Civi 4录制视频过暗_小米Civi 4亮度优化
CSS布局中意外空白:解决padding-top导致的顶部间距问题
Composer的 "check-platform-reqs" 命令有什么用_在部署前检查生产环境是否满足Composer依赖需求
天猫2025双十一0点秒杀攻略 天猫爆款抢购时间
马斯克:Optimus 人形机器人复数形式为 Optimi
深入理解J*a合成构造器:何时以及为何阻止其生成
CSS子选择器:如何区分并样式化嵌套列表的子层级
如何在 Excel Online 和 Google 表格中更改日期格式
JUnit5/Mockito:优雅测试内部依赖与异常处理的实践
Win10系统服务哪些可以禁用 Win10安全优化服务列表【干货】
顺丰快递查询系统 官方正版查询入口
c++如何使用Meson构建系统_c++比CMake更快的构建工具


2025-12-08
浏览次数:次
返回列表
e_path}'")
return None
# 对图像进行二值化处理,以便 cv.findContours 工作
_, binary_img = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# 查找轮廓
contours_found, _ = cv.findContours(binary_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
if not contours_found:
print("未检测到任何轮廓。")
return None
# 按轮廓点数从大到小排序
sorted_contours = sorted(contours_found, key=len, reverse=True)
print(f"检测到的轮廓总数: {len(sorted_contours)}")
print(f"排序后轮廓的长度: {[len(c) for c in sorted_contours]}")
# 准备合并
contours_to_merge = []
# 确保不会尝试合并超过实际存在的轮廓数量
actual_num_to_merge = min(num_to_merge, len(sorted_contours))
print(f"将合并前 {actual_num_to_merge} 个轮廓。")
for i in range(actual_num_to_merge):
contours_to_merge.append(sorted_contours[i])
if contours_to_merge:
final_merged_contour = np.vstack(contours_to_merge)
print(f"合并后的轮廓形状: {final_merged_contour.shape}")
return final_merged_contour
else:
print("没有轮廓被选中进行合并。")
return None
# 创建一个虚拟图像文件进行演示
dummy_image_path = "dummy_contours.png"
dummy_img = np.zeros((200, 200), dtype=np.uint8)
cv.rectangle(dummy_img, (20, 20), (80, 80), 255, -1)
cv.circle(dummy_img, (150, 150), 30, 255, -1)
cv.imwrite(dummy_image_path, dummy_img)
# 调用函数进行合并
merged_contour_result = merge_selected_contours(dummy_image_path, num_to_merge=2)
if merged_contour_result is not None:
# 可以在这里对合并后的轮廓进行进一步处理,例如绘制
output_img = np.zeros((200, 200, 3), dtype=np.uint8)
cv.drawContours(output_img, [merged_contour_result], -1, (0, 255, 0), 2)
cv.imshow("Merged Contours", output_img)
cv.waitKey(0)
cv.destroyAllWindows()