BEV,即bird’s eye view,鸟瞰图
BEV图为3D点云数据
鸟瞰图的计算需要用到地面数据,即空间上的点投影到某个平面,需要知道该平面的平面方程,平面方程的表达式为:
空间上的点到平面上的距离为:
读取KITTI数据集中的地面数据:
# 000274.txt# PlaneWidth 4Height 1-2.143976e-03 -9.997554e-01 2.201096e-02 1.707479e+00# 分别表示a, b, c, d四个参数值
点云数据转BEV时高度分辨率为0.5,根据点云数据可以得到x,y和z轴上的数据,即x_col,y_col,z_col,然后使用np.lexsort()对x轴进行排序:
sorted_order = np.lexsort((y_col, z_col, x_col))
# 对 x_col 进行排序
# 如果 x_col 中的数值一样,则比较 z_col 中相应索引下的值的大小
# 如果还相同,再比较 y_col 中的元素
将 y_col 中的元素置为0,即只保留x和z轴上的数据,然后使用np.view()对数值类型进行变换:
# 定义12字节的数据类型
dt = np.dtype((np.void, 12))
# 先使用np.ascontiguousarray将一个内存不连续存储的数组转换为内存连续存储的数组,使得运行速度更快
# 再使用np.view按指定方式对内存区域进行切割,来完成数据类型的转换
# discrete_pts(n, 3) --> contiguous_array(n, 1)
# itemsize输出array元素的字节数
contiguous_array = np.ascontiguousarray(discrete_pts).view(dtype=dt)
:::info 离散点云数据discrete_pts的shape为(n,3),其数值类型为np.int32,4字节,总字节为n_3_4Byte,现将其转为12Byte的数据,即保持总字节数不变:n_1_12,转换完成后的shape为(n,1)。 ::: 对上述的数据进行去重:
# 去除数组中的重复数字,并进行排序
_, unique_indices = np.unique(contiguous_array, return_index=True)
unique_indices.sort()
# 得到不重复的数据
# voxel 体素(三维)
# pixel 像素(二维)
voxel_coords = discrete_pts[unique_indices]
# 将索引值映射到原点
voxel_coords -= -400
计算每个体素中数据点的数量,即后一个索引值减去当前索引值:
num_points_in_voxel = np.diff(unique_indices)
num_points_in_voxel = np.append(num_points_in_voxel,
discrete_pts_2d.shape[0] -
unique_indices[-1])
计算每个体素中数据点的高度,通过计算点到平面方程的距离:
# voxel (x, y, z)
# 平面方程: ax + by + cz + d = 0
height_in_voxel = (a*x + b*y + c*z + d) / np.sqrt(a**2 + b**2 + c**2)
对高度信息进行缩放,height_in_voxel = height_in_voxel / 0.5,根据二维索引值voxel_coords(去除y轴)与高度信息即可得到BEV数据,密度信息的计算:
# N is num points in voxel
# x = 16
density_map = min(1.0, log(N+1)/log(x))
最终的BEV是由5个高度信息和1个密度信息组成,其shape为(700, 800, 6)。
