如何判断点是否在box中
在处理 KITTI 等自动驾驶数据集时,我们经常需要进行数据清洗。其中一个最基础但也最重要的操作,就是判断一个 3D 点(Point)是否位于某个 3D 边界框(Bounding Box)的内部。
例如,在训练 PointNet++ 进行分类或计数之前,我需要根据标签把前景点(在vehicles,pedestrians,cyclist内的点)提取出来。这里记录两种在我的 DistantCount 项目中用到的实现方法。
方法一:向量投影法 (Vector Projection)
这种方法的思路来源于数学中的向量投影。只要我们知道 Box 的 8 个角点,我们就可以利用点积(Dot Product)将目标点投影到 Box 的三个主轴上,判断投影长度是否在轴长范围内。
这种方法比较通用,适用于任意方向的 Box,前提是你已经算出了 Box 的 8 个角点。
代码实现:
1 | import numpy as np |
方法二:坐标系逆变换法 (V2X / LiDAR 坐标系)
在 V2X 或纯点云任务中,我们通常使用 LiDAR 坐标系(Z 轴向上)。这与 KITTI 的相机坐标系(Y 轴向下)不同,因此旋转矩阵是绕 Z 轴 进行的。
这种方法的核心思想是:与其把 Box 的 8 个角点算出来(比较麻烦),不如把所有点云变换到Box 的局部坐标系中。一旦在这个局部坐标系下,判断点是否在 Box 内就变成了简单的 abs(x) < length/2 的范围判断。
代码实现:
我的 DistantCount 项目是基于 V2X 数据的,因此采用如下的 Z 轴旋转逻辑:
import numpy as np
def in_box_mask(pts, o):
"""
判断点 pts 是否在由对象 o 定义的 3D Box 中 (V2X/LiDAR Coordinate)
:param pts: 点云数据 <np.array: N, 3>
:param o: 包含 box 信息的字典 (cx, cy, cz, l, w, h, ry)
:return: boolean mask
"""
# 1. 平移 (Translation)
# 将点云的坐标原点平移到 Box 的中心
# o['x'], o['y'], o['z'] 是 Box 在世界坐标系下的中心点
rel = pts - np.array([o['x'], o['y'], o['z']])
# 2. 旋转 (Rotation)
# V2X 场景下,物体通常绕 Z 轴旋转
# 我们要把点云“反向”转回与坐标轴对齐的状态,所以使用 -ry
ry = o['ry']
c, s = np.cos(-ry), np.sin(-ry)
# 构建绕 Z 轴旋转的矩阵
R = np.array([
[c, -s, 0],
[s, c, 0],
[0, 0, 1]
])
# 执行旋转变换
# rel (N,3) dot R.T (3,3) -> (N,3)
loc = rel.dot(R.T)
# 3. 范围判断 (Check Boundaries)
# 在局部坐标系下,Box 的中心就是 (0,0,0)
# 只需要判断点的 x, y, z 是否在 Box 长宽高的范围内
# 注意:需确认 dataset 中 l, w, h 对应局部坐标系的哪个轴,通常 x对应l, y对应w
return (
(np.abs(loc[:, 0]) <= o['l'] / 2) & # x 轴方向 (Length)
(np.abs(loc[:, 1]) <= o['w'] / 2) & # y 轴方向 (Width)
(np.abs(loc[:, 2]) <= o['h'] / 2) # z 轴方向 (Height)
)
---
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Comments