0

0

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

花韻仙語

花韻仙語

发布时间:2025-12-13 20:43:22

|

203人浏览过

|

来源于php中文网

原创

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 最终只存储了循环中最后一个轮廓的数据。

魔术橡皮擦
魔术橡皮擦

智能擦除、填补背景内容

下载

解决方案:先收集后堆叠

解决这个问题的关键在于,不要在循环中反复进行堆叠操作,而是先将所有需要合并的轮廓收集到一个列表中,然后在循环结束后,对整个列表执行一次 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) 形状,那么 vstack 结果将是 (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高效的向量化操作一次性完成堆叠。

最佳实践与注意事项

  1. 效率优势: 相比于在循环中反复调用 np.vstack,先将数组收集到列表中再进行一次性堆叠通常更为高效。这是因为列表的 append 操作通常比 NumPy 数组的内存重新分配和复制操作开销小,尤其是在处理大量数组时。一次性 np.vstack 能够更有效地利用内存和计算资源。
  2. 灵活性: 这种“先收集后堆叠”的模式非常灵活,可以轻松应对需要合并可变数量轮廓的场景。只需调整循环的范围或用于选择轮廓的条件,即可动态地决定哪些轮廓被合并。
  3. 轮廓数据结构: 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)。示例代码中已包含此处理,以符合常见的点集表示。
  4. 空轮廓列表: 如果 contours_to_merge 列表最终为空(例如,没有找到符合条件的轮廓),那么 np.vstack([]) 将会返回一个空数组 (0, D),其中 D 是期望的维度。这通常是一个可接受的空结果,避免了程序崩溃。

总结

在OpenCV中使用NumPy合并动态数量的轮廓时,最佳实践是避免在循环中重复执行 np.vstack 操作。相反,应该将所有待合并的轮廓对象先存储在一个Python列表中,然后在循环结束后,对该列表执行一次 np.vstack

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

760

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

639

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

762

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

619

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1285

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

549

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

579

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

709

2023.08.11

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

3

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 4.7万人学习

Django 教程
Django 教程

共28课时 | 3.2万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号