新闻中心
OpenCV与NumPy:高效合并动态数量轮廓的实践指南

本教程详细介绍了如何在opencv中高效合并由`cv.findcontours`检测到的多个动态轮廓。针对在循环中直接使用`np.vstack`可能导致的错误结果,我们提出了一种优化的方法:先将目标轮廓收集到一个列表中,然后进行一次性垂直堆叠。这种方法不仅确保了合并结果的准确性,还显著提升了处理效率,特别适用于需要合并可变数量轮廓的场景。
引言:OpenCV轮廓合并的挑战
在计算机视觉任务中,使用OpenCV的cv.findContours函数检测图像中的轮廓是一项基础操作。该函数返回的轮廓通常是一个列表,其中每个元素代表一个独立的轮廓,其形状通常为(N, 1, 2),表示N个点,每个点有2个坐标。在某些应用场景下,我们可能需要将这些检测到的多个轮廓合并成一个单一的轮廓数组,例如,为了对一组相关的对象进行整体分析或进一步处理。
当需要合并的轮廓数量是动态变化的,或者需要根据特定条件(如大小、位置)选择性地合并轮廓时,问题会变得复杂。np.vstack是NumPy库中一个常用的函数,用于垂直堆叠数组。然而,在循环中不当使用np.vstack来合并多个数组时,往往无法得到预期的结果。
常见误区:循环中直接使用np.vstack
考虑一个场景,我们已经通过cv.findContours找到了多个轮廓,并根据其长度(点数)进行了排序。现在,我们希望合并其中最大的两个轮廓。一个直观但错误的尝试可能是在循环中直接使用np.vstack来堆叠每个轮廓。
以下是这种错误方法的示例代码:
import cv2 as cv
import numpy as np
# 假设 img 是经过二值化处理的图像
# 为了演示,我们创建一个模拟的轮廓数据
# 实际应用中 data_contours 来自 cv.findContours
data_contours_raw = [
np.random.randint(0, 100, size=(5754, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(407, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(8, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32)
]
# 模拟排序后的轮廓列表
data_cnt = sorted(data_contours_raw, key=len, reverse=True)
print(f"Total number of contours: {len(data_cnt)}")
print(f"Contour sizes: {[len(c) for c in data_cnt]}")
# Expected output:
# Total number of contours: 7
# Contour sizes: [5754, 407, 8, 1, 1, 1, 1]
contour_number = 2 # 想要合并的前两个轮廓
# 错误的方法:在循环中直接使用 np.vstack
contours = None # 初始化为 None 或其他,避免类型错误
for i in range(contour_number):
# 每次循环都会重新赋值给 'contours' 变量
# 实际上只保留了最后一次迭代的结果
contours = np.vstack(data_cnt[i]) # 注意:这里如果 data_cnt[i] 是 (N,1,2)
# np.vstack(array) 相当于 array 本身
# 所以会是 (N,1,2) 或 (N,2) 如果被隐式处理
print(contours.shape)
# 预期输出 (基于原始问题描述): (407,2)
# 实际根据 np.vstack(array) 的行为,如果 data_cnt[i] 是 (407,1,2)
# 那么会输出 (407,1,2)
# 为了与原问题输出保持一致,这里假设某种隐式转换导致了 (407,2)上述代码的输出将是 (407, 2)(或 (407, 1, 2),取决于np.vstack对单数组的处理以及后续的隐式形状转换)。这显然不是我们期望的 (5754 + 407) 个点,即 (6161, 2) 的合并结果。问题在于,在循环内部,每次 contours = np.vstack(data_cnt[i]) 都会创建一个新的数组并将其赋值给 contours 变量,覆盖了前一次迭代的结果。因此,contours 最终只存储了循环中最后一个轮廓的数据。
DeepBrain
AI视频生成工具,ChatGPT +生成式视频AI =你可以制作伟大的视频!
146
查看详情
解决方案:先收集后堆叠
解决这个问题的关键在于,不要在循环中反复进行堆叠操作,而是先将所有需要合并的轮廓收集到一个列表中,然后在循环结束后,对整个列表执行一次 np.vstack 操作。np.vstack 能够接受一个由多个数组组成的序列(如列表或元组),并将它们一次性垂直堆叠起来。
以下是优化后的代码示例:
import cv2 as cv
import numpy as np
# 模拟轮廓数据 (与上文相同)
data_contours_raw = [
np.random.randint(0, 100, size=(5754, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(407, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(8, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32),
np.random.randint(0, 100, size=(1, 1, 2), dtype=np.int32)
]
data_cnt = sorted(data_contours_raw, key=len, reverse=True)
contours_to_merge = [] # 创建一个空列表来收集轮廓
contour_number = 2 # 想要合并的前两个轮廓
for i in range(contour_number):
contours_to_merge.append(data_cnt[i])
# 在循环结束后,对收集到的所有轮廓进行一次性垂直堆叠
# 注意:如果 data_cnt[i] 是 (N, 1, 2) 形状,那么 vs
tack 结果将是 (SumN, 1, 2)
# 若要得到 (SumN, 2) 形状,需要进一步 reshape
merged_contours = np.vstack(contours_to_merge)
# 原始问题输出 (6161,2) 意味着可能进行了 reshape 或输入轮廓本身就是 (N,2)
# 假设我们期望最终形状为 (总点数, 2)
if merged_contours.ndim == 3 and merged_contours.shape[1] == 1:
merged_contours = merged_contours.reshape(-1, 2)
print(merged_contours.shape)
# 预期输出: (6161,2)运行这段代码,输出将是 (6161, 2),这正是我们期望的合并结果。这种方法的核心思想是利用Python列表的灵活性来收集动态数量的NumPy数组,然后利用NumPy高效的向量化操作一次性完成堆叠。
最佳实践与注意事项
- 效率优势: 相比于在循环中反复调用 np.vstack,先将数组收集到列表中再进行一次性堆叠通常更为高效。这是因为列表的 append 操作通常比 NumPy 数组的内存重新分配和复制操作开销小,尤其是在处理大量数组时。一次性 np.vstack 能够更有效地利用内存和计算资源。
- 灵活性: 这种“先收集后堆叠”的模式非常灵活,可以轻松应对需要合并可变数量轮廓的场景。只需调整循环的范围或用于选择轮廓的条件,即可动态地决定哪些轮廓被合并。
- 轮廓数据结构: cv.findContours 函数在使用 cv.CHAIN_APPROX_NONE 模式时,返回的每个轮廓是一个形状为 (N, 1, 2) 的NumPy数组,其中 N 是轮廓中的点数。np.vstack 多个 (N, 1, 2) 数组的结果将是 (总点数, 1, 2)。如果你的后续处理需要 (总点数, 2) 的形状(即二维点集),你需要进行额外的 reshape 操作,例如 merged_contours.reshape(-1, 2)。示例代码中已包含此处理,以符合常见的点集表示。
- 空轮廓列表: 如果 contours_to_merge 列表最终为空(例如,没有找到符合条件的轮廓),那么 np.vstack([]) 将会返回一个空数组 (0, D),其中 D 是期望的维度。这通常是一个可接受的空结果,避免了程序崩溃。
总结
在OpenCV中使用NumPy合并动态数量的轮廓时,最佳实践是避免在循环中重复执行 np.vstack 操作。相反,应该将所有待合并的轮廓对象先存储在一个Python列表中,然后在循环结束后,对该列表执行一次 np.vstack
以上就是OpenCV与NumPy:高效合并动态数量轮廓的实践指南的详细内容,更多请关注其它相关文章!
# 隐式
# 邵阳资讯网站建设有哪些
# 抖店营销推广工具在哪
# 西宁营销推广网站
# 营口全网推广营销招聘
# 绍兴网站建设与管理代码
# 泸州工投建设集团网站
# 中公教育seo课件
# 番茄社区seo
# 郑州网络营销推广外包
# 营销号你有哪个产品推广
# 结束后
# 中文网
# python
# 是在
# 创建一个
# 先将
# 数据结构
# 将是
# 是一个
# 多个
# 隐式转换
# ai
# app
# 计算机
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
在J*aScript中复现SciPy的B样条拟合与求值:关键考量
PySpark中从现有列右侧提取可变长度字符创建新列的教程
如何在 Excel Online 和 Google 表格中更改日期格式
Win11怎么开启高性能模式_Windows 11电源计划优化设置
海量存储:机器视觉智能化的核心基石
Lar*el用户头像管理:实现图片缩放、存储与旧文件安全删除的最佳实践
Python异步编程实践:使用Binance API构建实时交易数据流
HuggingFaceEmbeddings中向量嵌入维度调整的限制与理解
lar*el怎么安全地存储和获取配置文件中的敏感信息_lar*el敏感信息安全存储方法
python3时间如何用calendar输出?
小米14应用无法联网原因分析_小米14网络权限修复
新手怎么开始学化妆 零基础化妆入门教程
excel如何生成目录 excel一键生成工作表目录超链接
内存疯狂猛猛涨价:主板销量直接腰斩!
荣耀Play7T运行卡顿解决_荣耀Play7T性能优化
在J*a中如何隐藏复杂性_使用门面模式组织对象交互
谷歌浏览器一键优化方案_谷歌浏览器直达主页极速不卡版
Yandex免登录官网入口_俄罗斯Yandex搜索引擎直达链接
微博网页版直接访问 微博网页版账号管理快速入口
html两个JS只运行一个怎么办_让双JS在html中都运行方法【技巧】
小红书怎么解除第三方平台绑定_小红书多平台登录解绑方法介绍
知音漫客正版漫画平台_知音漫客官网账号登录
Go语言中高效处理x-www-form-urlencoded表单数据
在Typer应用中优雅地处理和重组任意命令行参数
邮政编码查询不到怎么办_邮政编码查询不到的常见原因与对策
抖音网页版快捷访问 抖音网页版网页版入口操作教程
小猿搜题在线学习页面在哪_小猿搜题在线学习中心入口
C++如何操作注册表_Windows平台下C++读写注册表的API函数详解
如何将HTML表格多行数据保存到Google Sheet
今日头条怎么同步内容到抖音_今日头条内容同步到抖音教程
向日葵客户端怎么进行远程CentOS控制_向日葵客户端远程CentOS控制操作教程
响应式容器内容自动缩放与宽高比维持教程
腾讯视频怎么举报不良内容_腾讯视频内容举报流程与违规信息处理方法
Yandex搜索引擎官网入口_俄罗斯Yandex免登录一键直达
铁路12306的积分有效期是多久_铁路12306积分有效期说明
Linux如何排查内存不足OOME问题_LinuxOOM分析教程
mc.js免安装版 mc.js一键畅玩入口
4399体育竞技小游戏_4399小游戏赛事入口
b站如何看历史记录_b站观看历史找回方法
没有大陆身份证/银行卡如何实名微信? 亲测有效的几种方法分享
使用Pandas转换并合并DataFrame:多列映射至统一结构
百度浏览器字体显示异常偏小_百度浏览器字体渲染修复方案
网站内容防复制粘贴的实现策略与局限性
163邮箱官方主页登录 直达网易邮箱登录核心页面
多闪网页版在线观看免费入口_多闪官网访问入口
抖音网页版平台入口 抖音网页版官网在线访问教程
蛙漫官网漫画入口地址_蛙漫在线畅读无广告弹窗
冬*霸灯泡不亮怎么办_浴霸取暖灯一盏不亮的灯座清洁修复法
Yandex官方入口网址 Yandex俄罗斯搜索引擎最新在线地址
J*aScript异步迭代器_j*ascript异步遍历


2025-12-13
浏览次数:次
返回列表
tack 结果将是 (SumN, 1, 2)
# 若要得到 (SumN, 2) 形状,需要进一步 reshape
merged_contours = np.vstack(contours_to_merge)
# 原始问题输出 (6161,2) 意味着可能进行了 reshape 或输入轮廓本身就是 (N,2)
# 假设我们期望最终形状为 (总点数, 2)
if merged_contours.ndim == 3 and merged_contours.shape[1] == 1:
merged_contours = merged_contours.reshape(-1, 2)
print(merged_contours.shape)
# 预期输出: (6161,2)