点云数据详解——点云数据变为图像

点云数据——the point cloud data 点云数据应表示为具有n行和至少3列的numpy数组。每行对应于单个点,其在空间(x,y,z)中的位置使用至少3个值表示。
如果点云数据来自lidar传感器,那么它可能具有每个点的附加值,例如“反射率”,其是在该位置中障碍物反射多少激光光束的量度。在这种情况下,点云数据可能是nx4阵列。
图像与点云坐标——image vs point cloud coordinates 点云的轴与图像中的轴具有完全不同的含义。下图显示了蓝色的图像轴和橙色的点云轴。
关于图像需要注意的一些重要事项:
图像中的坐标值始终为正。
原点位于左上角。
坐标是整数值。
有关点云坐标的注意事项:
点云中的坐标值可以是正数或负数。
坐标可以采用实数值。
正x轴表示向前。
正y轴表示左。
正z轴表示向上。
创建点云数据的birdseye视图 鸟瞰图的相关轴 为了创建鸟眼视图图像,来自点云数据的相关轴将是x和y轴。
但是,正如我们从上图所示,我们必须小心并考虑以下事项:      x和y轴意味着相反的事情。      x和y轴指向相反的方向。      您必须移动值,以便(0,0)是图像中可能的最小值。 
1.限制矩形看——limiting rectangle to look at
仅关注点云的特定区域通常很有用。因此,我们希望创建一个仅保留我们感兴趣的区域内的点的过滤器。 由于我们正在查看顶部的数据,并且我们有兴趣将其转换为图像,因此我将使用与图像轴更加一致的方向。下面,我指定我想要集中在相对于原点的值的范围。原点左侧的任何内容都将被视为负数,而右侧的任何内容都将被视为正数。点云的x轴将被解释为向前方向(这将是我们的鸟眼图像的向上方向)。 下面的代码将感兴趣的矩形设置为在原点的两侧跨越10米,并在其前面20米处。
side_range=(-10, 10) # left-most to right-mostfwd_range=(0, 20) # back-most to forward-most 2.接下来,我们创建一个过滤器,仅保留实际位于我们指定的矩形内的点。
# extract the points for each axisx_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 coordinatesf_filt = np.logical_and((x_points > fwd_range[0]), (x_points -side_range[1]), (y_points >> x_img.min()7>>> x_img.max()199>>> y_img.min()1>>> y_img.max()199 像素值——pixel values 
我们已经使用点数据来指定图像中的x和y位置。我们现在需要做的是指定我们想要用这些像素位置填充的值。一种可能性是用高度数据填充它。要做的两件事
请记住:
     像素值应为整数。
     像素值应该是0-255范围内的值。
我们可以从数据中获取最小和最大高度值,并重新缩放该范围以适应0-255的范围。另一种方法,这里将使用的方法是设置我们想要集中的高度值范围,并且高于或低于该范围的任何内容都被剪切为最小值和最大值。这很有用,因为它允许我们从感兴趣的区域获得最大量的细节。
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]) 在下面的代码中,我们将范围设置为原点下方2米,高于原点半米。接下来,我们将这些值重新缩放到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]) 创建图像阵列——create the image array
现在我们准备实际创建图像,我们只是初始化一个数组,其尺寸取决于我们在矩形中所需的值范围和我们选择的分辨率。然后我们使用我们转换为像素位置的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 预览——viewing  目前,图像存储为numpy数组。如果我们希望将其可视化,我们可以将其转换为pil图像,并查看它。
# convert from numpy array to a pil imagefrom pil import imageim2 = image.fromarray(im)im2.show() 我们作为人类并不善于分辨灰色阴影之间的差异,因此它可以帮助我们使用光谱颜色映射来让我们更容易分辨出价值差异。我们可以在matplotlib中做到这一点。(实际无法正常显示)
import matplotlib.pyplot as pltplt.imshow(im, cmap=spectral, vmin=0, vmax=255)plt.show()
它实际上编码与pil绘制的图像完全相同的信息量,因此机器学习学习算法例如仍然能够区分高度差异,即使我们人类不能非常清楚地看到差异。
import cv2 #通过cv2显示 cv2.imshow(im,im) cv2.waitkey() cv2.destroyallwindows()
  完整代码——complete code 为方便起见,我将上面的所有代码放在一个函数中,它将鸟瞰视图作为一个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 -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  


浅谈嵌入式轻量级Wi-Fi模块|_GDM_WIFI_01
QNX推出全新技术概念车重新构想驾驶体验
智能电表现场诊断的进展
汽车电源架构设计需要遵循哪些原则?
安全FPGA技术实现无人机通信和控制
点云数据详解——点云数据变为图像
AR眼镜-基于Genio 700|MT8390芯片的AR智能眼镜
盘点紫光展锐8大创新技术
轻奢风格的三星w2017,处处流露着尊贵的气息!
机器人技术融合了哪几种前沿技术的生态综合体?
可植入大脑的微型芯片,有助病人恢复视听觉
鸿蒙OS 2.0对普通消费者有什么影响
磁翻板液位计的使用建议
小米手机Creo升级项目与微创医疗PLM升级案例分享
MS2109:HDMI→HDMI&amp;USB 2.0是一款高清视频采集芯片
罗德与施瓦茨实现亚太赫兹超宽带信号分析
第三代半导体热潮“带货”沉积设备需求,供应链与服务本地化成关键考量
央行:数字人民币和手机号码绑定是误解,手机号是代表信息认证
变压器局部监测系统的电磁干扰技术
高防CDN是什么,它有什么样的优势