利用点云数据生成鸟瞰图

网友投稿 323 2022-09-24

利用点云数据生成鸟瞰图

文章目录

​​鸟瞰图的相关轴​​​​限制矩形查看​​​​将点位置映射到像素位置​​​​转移到新的原点​​​​像素值​​​​创建图像数组​​​​查看​​​​完整代码​​​​参考​​

鸟瞰图的相关轴

x 和 y 轴的意思相反。x 轴和 y 轴指向相反的方向。您必须移动这些值,以便 (0,0) 是图像中的最小可能值。

限制矩形查看

只关注点云的特定区域通常很有用。因此,我们希望创建一个过滤器,只保留我们感兴趣的区域内的点。

由于我们正在查看顶部的数据并且我们有兴趣将其转换为图像,因此我将使用与图像轴更加一致的方向。下面,我指定了我想要关注的相对于原点的值范围。原点左侧的任何内容都将被视为负数,而右侧的任何内容都将被视为正数。点云的 x 轴将被解释为向前方向(这将是我们鸟瞰图像的向上方向)。

下面的代码将感兴趣的矩形设置为在原点两侧跨度 10m,在其前面跨度 20m。

side_range = ( -10 , 10 ) # 从最左边到最右边fwd_range = ( 0 , 20 ) # 从最后到最前面

接下来,我们创建一个过滤器,只保留实际位于我们指定的矩形内的点。

# 提取每个轴的点x_points = points[:, 0]y_points = points[:, 1]z_points = points[:, 2]# 过滤器 - 仅返回所需立方体内点的索引# 三个过滤器:前后、左右和高度范围# 注意左侧是激光雷达坐标中的正 y 轴f_filt = np.logical_and((x_points > fwd_range[0]), (x_points < fwd_range[1]))s_filt = np.logical_and((y_points > -side_range[1]), (y_points < -side_range[0]))filter = np.logical_and(f_filt, s_filt)indices = np.argwhere(filter).flatten()# 保持者 x_points = x_points[indices]y_points = y_points[indices]z_points = z_points[indices]

将点位置映射到像素位置

目前,我们有一堆具有实数值的点。为了映射那些将这些值映射到整数位置值。我们可以天真地将所有 x 和 y 值类型转换为整数,但我们最终可能会失去很多分辨率。例如,如果这些点的测量单位是米,那么每个像素将代表点云中一个 1x1 米的矩形,我们将丢失任何小于这个的细节。如果您有像山景这样的点云,这可能没问题。但如果你想捕捉更精细的细节,识别人类、汽车,甚至更小的东西,那么这种方法就不行了。

但是,可以稍微修改上述方法,以便我们获得所需的分辨率级别。在类型转换为整数之前,我们可以先缩放数据。例如,如果测量单位是米,而我们想要 5 厘米的分辨率,我们可以执行以下操作:

res = 0.05# CONVERT TO PIXEL POSITION VALUES - Based on resolutionx_img = (-y_points / res).astype(np.int32) # x axis is -y in LIDARy_img = (-x_points / res).astype(np.int32) # y axis is -x in LIDAR

你会注意到 x 和 y 轴交换了,方向颠倒了,这样我们现在可以开始处理图像坐标了。

转移到新的原点

x 和 y 数据还没有完全准备好映射到图像。我们可能仍然有负的 x 和 y 值。所以我们需要移动数据使 (0,0) 成为最小值。

# SHIFT PIXELS TO HAVE MINIMUM BE (0,0)# floor and ceil used to prevent anything being rounded to below 0 after shiftx_img -= int(np.floor(side_range[0] / res))y_img += int(np.ceil(fwd_range[1] / res))

我们可以探索数据以向自己证明这些值现在都是正的,例如:

>>> x_img.min()7>>> x_img.max()199>>> y_img.min()1>>> y_img.max()199

像素值

所以我们使用点数据来指定图像中的 x 和 y 位置。我们现在需要做的是指定我们想要填充这些像素位置的值。一种可能性是用高度数据填充它。 但要记住的两件事是:

像素值应该是整数。像素值应介于 0-255 之间。

我们可以从数据中获取最小和最大高度值,并重新调整该范围以适应 0-255 的范围。此处将使用的另一种方法是设置我们想要关注的高度值范围,任何高于或低于该范围的值都会被剪裁为最小值和最大值。这很有用,因为它允许我们从感兴趣的区域获得最大量的细节。

在以下代码中,我们将范围设置为原点下方 2 米,原点上方半米。

height_range = (-2, 0.5) # bottom-most to upper-most# CLIP HEIGHT VALUES - to between min and max heightspixel_values = np.clip(a = z_points, a_min=height_range[0], a_max=height_range[1])

接下来,我们将这些值重新缩放到 0-255 之间,并将类型转换为整数。

def scale_to_255(a, min, max, dtype=np.uint8): """ Scales an array of values from specified min, max range to 0-255 Optionally specify the data type of the output (default is uint8) """ return (((a - min) / float(max - min)) * 255).astype(dtype)# RESCALE THE HEIGHT VALUES - to be between the range 0-255pixel_values = scale_to_255(pixel_values, min=height_range[0], max=height_range[1])

创建图像数组

现在我们已准备好实际创建图像,我们只需初始化一个数组,其维度取决于我们在矩形中所需的值范围和我们选择的分辨率。然后我们使用转换为像素位置的 x 和 y 点值来指定数组中的索引,并将我们在上一小节中选择作为像素值的值分配给这些索引。

# INITIALIZE EMPTY ARRAY - of the dimensions we wantx_max = 1+int((side_range[1] - side_range[0])/res)y_max = 1+int((fwd_range[1] - fwd_range[0])/res)im = np.zeros([y_max, x_max], dtype=np.uint8)# FILL PIXEL VALUES IN IMAGE ARRAYim[y_img, x_img] = pixel_values

查看

目前,图像存储为一个 numpy 数组。如果我们希望将其可视化,我们可以将其转换为 PIL 图像并查看。

# CONVERT FROM NUMPY ARRAY TO A PIL IMAGEfrom PIL import Imageim2 = Image.fromarray(im)im2.show()

它实际上编码的信息量与 PIL 绘制的图像完全相同,因此机器学习算法仍然能够区分高度的差异,即使我们人类无法非常清楚地看到这些差异。

完整代码

为方便起见,我将上述所有代码放在一个函数中,该函数将鸟瞰图作为 numpy 数组返回。然后,您可以选择使用您喜欢的任何方法对其进行可视化,或者将 numpy 数组插入机器学习算法中。

import numpy as np# ==============================================================================# SCALE_TO_255# ==============================================================================def scale_to_255(a, min, max, dtype=np.uint8): """ Scales an array of values from specified min, max range to 0-255 Optionally specify the data type of the output (default is uint8) """ return (((a - min) / float(max - min)) * 255).astype(dtype)# ==============================================================================# POINT_CLOUD_2_BIRDSEYE# ==============================================================================def point_cloud_2_birdseye(points, res=0.1, side_range=(-10., 10.), # left-most to right-most fwd_range = (-10., 10.), # back-most to forward-most height_range=(-2., 2.), # bottom-most to upper-most ): """ Creates an 2D birds eye view representation of the point cloud data. Args: points: (numpy array) N rows of points data Each point should be specified by at least 3 elements x,y,z res: (float) Desired resolution in metres to use. Each output pixel will represent an square region res x res in size. side_range: (tuple of two floats) (-left, right) in metres left and right limits of rectangle to look at. fwd_range: (tuple of two floats) (-behind, front) in metres back and front limits of rectangle to look at. height_range: (tuple of two floats) (min, max) heights (in metres) relative to the origin. All height values will be clipped to this min and max value, such that anything below min will be truncated to min, and the same for values above max. Returns: 2D numpy array representing an image of the birds eye view. """ # EXTRACT THE POINTS FOR EACH AXIS x_points = points[:, 0] y_points = points[:, 1] z_points = points[:, 2] # FILTER - To return only indices of points within desired cube # Three filters for: Front-to-back, side-to-side, and height ranges # Note left side is positive y axis in LIDAR coordinates f_filt = np.logical_and((x_points > fwd_range[0]), (x_points < fwd_range[1])) s_filt = np.logical_and((y_points > -side_range[1]), (y_points < -side_range[0])) filter = np.logical_and(f_filt, s_filt) indices = np.argwhere(filter).flatten() # KEEPERS x_points = x_points[indices] y_points = y_points[indices] z_points = z_points[indices] # CONVERT TO PIXEL POSITION VALUES - Based on resolution x_img = (-y_points / res).astype(np.int32) # x axis is -y in LIDAR y_img = (-x_points / res).astype(np.int32) # y axis is -x in LIDAR # SHIFT PIXELS TO HAVE MINIMUM BE (0,0) # floor & ceil used to prevent anything being rounded to below 0 after shift x_img -= int(np.floor(side_range[0] / res)) y_img += int(np.ceil(fwd_range[1] / res)) # CLIP HEIGHT VALUES - to between min and max heights pixel_values = np.clip(a=z_points, a_min=height_range[0], a_max=height_range[1]) # RESCALE THE HEIGHT VALUES - to be between the range 0-255 pixel_values = scale_to_255(pixel_values, min=height_range[0], max=height_range[1]) # INITIALIZE EMPTY ARRAY - of the dimensions we want x_max = 1 + int((side_range[1] - side_range[0]) / res) y_max = 1 + int((fwd_range[1] - fwd_range[0]) / res) im = np.zeros([y_max, x_max], dtype=np.uint8) # FILL PIXEL VALUES IN IMAGE ARRAY im[y_img, x_img] = pixel_values return im

参考

​​http://ronny.rest/tutorials/module/pointclouds_01/point_cloud_birdseye/​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SocialMarketing:综艺植入下半场,品牌还能怎么玩?
下一篇:Vue使用基于element-ui的el-tree-fransfer树形穿梭框组件
相关文章

 发表评论

暂时没有评论,来抢沙发吧~