0

0

在三维包围盒中高效采样点:基于NumPy mgrid 的实现指南

碧海醫心

碧海醫心

发布时间:2025-11-25 13:59:17

|

276人浏览过

|

来源于php中文网

原创

在三维包围盒中高效采样点:基于NumPy mgrid 的实现指南

本文旨在提供一个高效且专业的教程,指导如何在三维(3d)包围盒内部以指定步长均匀采样点,并为每个采样点分配对应的标签。我们将探讨如何利用numpy库中的`mgrid`函数,结合其强大的网格生成能力,实现对多个包围盒的矢量化处理,从而简化代码并提升性能。

1. 引言与问题定义

计算机视觉、机器人学或模拟等领域,经常需要对三维空间中的物体进行离散化或体素化处理。一个常见的任务是在给定的三维包围盒(Bounding Box)内部,按照特定的空间步长(step_size)生成一系列均匀分布的采样点。每个包围盒通常还带有一个关联的标签(例如,表示物体类别)。

我们的输入数据结构是一个 NumPy 数组,包含 n 个包围盒。每个包围盒由其8个顶点定义,每个顶点包含三维坐标 (x, y, z) 和一个关联的标签 l。为了清晰起见,我们假设每个包围盒的所有顶点都共享相同的标签。因此,一个包围盒的数据可以表示为形状 (8, 4) 的数组,其中每行是 [x, y, z, label]。

例如,一个包围盒的数据可能如下所示:

box_example = np.array([
    [0.0, 0.0, 0.0, 1],
    [2.0, 0.0, 0.0, 1],
    [2.0, 3.0, 0.0, 1],
    [0.0, 3.0, 0.0, 1],
    [0.0, 0.0, 1.0, 1],
    [2.0, 0.0, 1.0, 1],
    [2.0, 3.0, 1.0, 1],
    [0.0, 3.0, 1.0, 1]
])

我们的目标是,对于每个包围盒,在其内部生成 step_size 间隔的 (x, y, z) 坐标点,并为这些点分配该包围盒的标签。

2. 传统方法及其局限性

一种直观的方法是首先确定每个包围盒在 x, y, z 轴上的最小和最大坐标。然后,为每个维度计算需要生成的点数,并使用循环遍历每个包围盒。在每个包围盒内部,可以使用 np.mgrid 生成三维网格,再将坐标展平并附加标签。

初始实现思路可能如下:

import numpy as np

# 假设 boxes 是一个 (N, 8, 4) 的数组
# 其中 N 是包围盒的数量,8 是每个包围盒的顶点数,4 是 (x, y, z, label)
# 示例数据(仅用于演示结构,实际数据应根据需求生成)
num_boxes = 2
boxes_data = np.array([
    # Box 1
    [[0.0, 0.0, 0.0, 1], [2.0, 0.0, 0.0, 1], [2.0, 3.0, 0.0, 1], [0.0, 3.0, 0.0, 1],
     [0.0, 0.0, 1.0, 1], [2.0, 0.0, 1.0, 1], [2.0, 3.0, 1.0, 1], [0.0, 3.0, 1.0, 1]],
    # Box 2
    [[5.0, 5.0, 5.0, 2], [6.0, 5.0, 5.0, 2], [6.0, 7.0, 5.0, 2], [5.0, 7.0, 5.0, 2],
     [5.0, 5.0, 6.0, 2], [6.0, 5.0, 6.0, 2], [6.0, 7.0, 6.0, 2], [5.0, 7.0, 6.0, 2]]
])

# 提取每个包围盒的最小/最大坐标 (x,y,z)
mins = np.min(boxes_data[:, :, :3], axis=1) # shape (N, 3)
maxs = np.max(boxes_data[:, :, :3], axis=1) # shape (N, 3)

# 提取每个包围盒的标签 (假设所有顶点标签相同,取第一个)
box_labels = boxes_data[:, 0, 3].astype(int) # shape (N,)

step_size = 0.5 # 采样步长

sampled_points_list = []
sampled_labels_list = []

for i in range(num_boxes):
    x_min, y_min, z_min = mins[i]
    x_max, y_max, z_max = maxs[i]
    current_label = box_labels[i]

    # 使用 np.mgrid 生成网格
    # 注意:这里使用 start:stop:step_size 语法,它会生成 [start, stop) 范围内的点
    # 如果希望包含 stop,需要适当调整 stop 值或使用 num_points*1j 语法
    # 但对于采样,通常 [start, stop) 是合理的
    x_coords, y_coords, z_coords = np.mgrid[x_min:x_max:step_size,
                                            y_min:y_max:step_size,
                                            z_min:z_max:step_size]

    points = np.vstack([x_coords.ravel(), y_coords.ravel(), z_coords.ravel()]).T
    labels = np.full(points.shape[0], current_label, dtype=int)

    sampled_points_list.append(points)
    sampled_labels_list.append(labels)

# 将所有包围盒的点和标签合并
all_sampled_points = np.vstack(sampled_points_list)
all_sampled_labels = np.concatenate(sampled_labels_list)

# print("采样点示例 (前5个):")
# print(all_sampled_points[:5])
# print("对应标签示例 (前5个):")
# print(all_sampled_labels[:5])

这种方法虽然可行,但在循环内部创建 x_coords, y_coords, z_coords 后再进行 vstack 和 ravel 操作,对于大量包围盒可能会引入额外的开销。更重要的是,它将坐标和标签分开处理,增加了代码的复杂性。

Pic Copilot
Pic Copilot

AI时代的顶级电商设计师,轻松打造爆款产品图片

下载

3. 基于 np.mgrid 的优化解决方案

NumPy 的 mgrid 函数提供了一种更简洁、更高效的方式来生成多维网格,并且我们可以巧妙地将标签维度直接集成到 mgrid 的生成过程中。

np.mgrid 的语法 start:stop:step 用于生成等间隔的数值序列,类似于 np.arange,但它会为每个维度生成一个一维数组,然后通过广播机制形成多维网格。如果 step 是一个浮点数,它表示步长;如果是一个复数(例如 10j),则表示在 start 和 stop 之间包含 stop 在内生成 10 个点。

核心思想: 我们可以将标签视为第四个维度,并利用 mgrid 的特性,为这个“标签维度”指定一个只包含单个值的范围 label:label+1。这样,mgrid 就会自动生成包含 x, y, z, label 四个维度的网格,且标签维度上的所有值都将是该包围盒的唯一标签。

步骤详解:

  1. 确定包围盒的边界和标签: 对于每个包围盒,我们需要其 x, y, z 轴的最小值、最大值,以及其对应的标签。
  2. 构建 np.mgrid 表达式:
    • 对于 x 轴:x_min:x_max:step_size
    • 对于 y 轴:y_min:y_max:step_size
    • 对于 z 轴:z_min:z_max:step_size
    • 对于标签轴:current_label:current_label + 1 (这将创建一个只包含 current_label 的一维数组)
  3. 重塑结果: np.mgrid 返回的是多个维度数组的元组。我们需要将其 reshape 成 (4, -1),然后转置 (.T),得到形状为 (N_points, 4) 的数组,其中每行是 [x, y, z, label]。

示例代码

让我们使用一个具体的例子来演示这种优化方法:

import numpy as np
from itertools import product, repeat

# 定义一个示例包围盒及其标签
step_size = 0.6
label_val = 7

# 创建一个单位立方体作为示例包围盒的顶点,并附加标签
# 这里假设包围盒由 (0,0,0) 到 (1,1,1)
box_single = np.hstack([np.array(list(product(*repeat(range(2), 3)))), np.ones((8,1)) * label_val])

print("示例包围盒顶点数据:")
print(box_single)
print("-" * 30)

# 提取包围盒的最小/最大坐标
min_x, max_x = np.min(box_single[:, 0]), np.max(box_single[:, 0])
min_y, max_y = np.min(box_single[:, 1]), np.max(box_single[:, 1])
min_z, max_z = np.min(box_single[:, 2]), np.max(box_single[:, 2])

# 使用 np.mgrid 一次性生成所有点和标签
# 注意:mgrid 的 start:stop:step 语法通常是左闭右开 [start, stop)
# 如果 stop - start 恰好是 step 的整数倍,则 stop-step 会是最后一个点
# 例如 0:1:0.6 会生成 0.0, 0.6
sampled_points_with_labels = np.mgrid[
    min_x:max_x:step_size,
    min_y:max_y:step_size,
    min_z:max_z:step_size,
    label_val:label_val + 1  # 巧妙地将标签作为第四个维度
].reshape(4, -1).T # 重塑为 (N_points, 4)

print("采样点及对应标签:")
print(sampled_points_with_labels)
print(f"生成的点数量: {sampled_points_with_labels.shape[0]}")

输出示例:

示例包围盒顶点数据:
[[0. 0. 0. 7.]
 [0. 0. 1. 7.]
 [0. 1. 0. 7.]
 [0. 1. 1. 7.]
 [1. 0. 0. 7.]
 [1. 0. 1. 7.]
 [1. 1. 0. 7.]
 [1. 1. 1. 7.]]
------------------------------
采样点及对应标签:
[[0.  0.  0.  7. ]
 [0.  0.  0.6 7. ]
 [0.  0.6 0.  7. ]
 [0.  0.6 0.6 7. ]
 [0.6 0.  0.  7. ]
 [0.6 0.  0.6 7. ]
 [0.6 0.6 0.  7. ]
 [0.6 0.6 0.6 7. ]]
生成的点数量: 8

4. 处理多个包围盒

对于包含多个包围盒的场景,我们仍然需要遍历每个包围盒。然而,在循环内部,np.mgrid 的使用方式更加简洁高效。

import numpy as np

# 假设 boxes_data 是一个 (N, 8, 4) 的数组,如上文所示
num_boxes = boxes_data.shape[0]

# 提取每个包围盒的最小/最大坐标 (x,y,z)
mins = np.min(boxes_data[:, :, :3], axis=1) # shape (N, 3)
maxs = np.max(boxes_data[:, :, :3], axis=1) # shape (N, 3)

# 提取每个包围盒的标签 (假设所有顶点标签相同,取第一个)
box_labels = boxes_data[:, 0, 3].astype(int) # shape (N,)

step_size = 0.5 # 采样步长

all_sampled_data = []

for i in range(num_boxes):
    x_min, y_min, z_min = mins[i]
    x_max, y_max, z_max = maxs[i]
    current_label = box_labels[i]

    # 使用 np.mgrid 生成包含坐标和标签的采样点
    current_box_sampled_data = np.mgrid[
        x_min:x_max:step_size,
        y_min:y_max:step_size,
        z_min:z_max:step_size,
        current_label:current_label + 1
    ].reshape(4, -1).T

    all_sampled_data.append(current_box_sampled_data)

# 将所有包围盒的采样数据合并
final_sampled_array = np.vstack(all_sampled_data)

print("所有包围盒合并后的采样数据 (前10行):")
print(final_sampled_array[:10])
print(f"总共采样点数量: {final_sampled_array.shape[0]}")

5. 注意事项与最佳实践

  • np.mgrid 的步长行为: 当 step 是浮点数时,np.mgrid[start:stop:step] 生成的序列类似于 np.arange(start, stop, step),即左闭右开 [start, stop)。这意味着生成的最大值可能略小于 stop。如果严格要求包含 stop,可以考虑将 stop 略微增加 step_size / 2,或者使用 num_points*1j 的复数步长语法来指定点

相关专题

更多
treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

534

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

17

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

13

2026.01.06

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

18

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

34

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SciPy 教程
SciPy 教程

共10课时 | 1.1万人学习

R 教程
R 教程

共45课时 | 4.9万人学习

SQL 教程
SQL 教程

共61课时 | 3.4万人学习

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

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