点云数据——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 < 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()
# KEEPERSx_points = x_points[indices]y_points = y_points[indices]z_points = z_points[indices]

3.将点位置映射到像素位置——Mapping Point Positions to Pixel Positions

目前,我们有一堆带有实数值的点。为了映射这些值,将这些值映射到整数位置值。我们可以天真地将所有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轴交换,方向反转,以便我们现在可以开始处理图像坐标。

更改坐标原点——Shifting to New Origin

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

像素值——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-255Optionally 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-mostfwd_range = (-10., 10.), # back-most to forward-mostheight_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 dataEach point should be specified by at least 3 elements x,y,zres:        (float)Desired resolution in metres to use. Each output pixel willrepresent an square region res x res in size.side_range: (tuple of two floats)(-left, right) in metresleft and right limits of rectangle to look at.fwd_range:  (tuple of two floats)(-behind, front) in metresback 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, andthe same for values above max.Returns:2D numpy array representing an image of the birds eye view."""# 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 < 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()# KEEPERSx_points = x_points[indices]y_points = y_points[indices]z_points = z_points[indices]# 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# SHIFT PIXELS TO HAVE MINIMUM BE (0,0)# floor & 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))# 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])# 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])# 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_valuesreturn im

本文来源于CSDN博主——来路与归途的翻译作品(已授权),仅用于学术分享。

原文地址:http://ronny.rest/tutorials/module/pointclouds_01/point_cloud_data/(可点击阅读原文查看)

编辑:3D视觉开发者社区

本文仅做学术分享,如有侵权,请联系删文。

下载1

在「3D视觉工坊」公众号后台回复:3D视觉即可下载 3D视觉相关资料干货,涉及相机标定、三维重建、立体视觉、SLAM、深度学习、点云后处理、多视图几何等方向。

下载2

在「3D视觉工坊」公众号后台回复:3D视觉github资源汇总即可下载包括结构光、标定源码、缺陷检测源码、深度估计与深度补全源码、点云处理相关源码、立体匹配源码、单目、双目3D检测、基于点云的3D检测、6D姿态估计源码汇总等。

下载3

在「3D视觉工坊」公众号后台回复:相机标定即可下载独家相机标定学习课件与视频网址;后台回复:立体匹配即可下载独家立体匹配学习课件与视频网址。

重磅!3DCVer-学术论文写作投稿 交流群已成立

扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

同时也可申请加入我们的细分方向交流群,目前主要有3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、多传感器融合、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流、ORB-SLAM系列源码交流、深度估计等微信群。

一定要备注:研究方向+学校/公司+昵称,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,可快速被通过且邀请进群。原创投稿也请联系。

▲长按加微信群或投稿

▲长按关注公众号

3D视觉从入门到精通知识星球:针对3D视觉领域的知识点汇总、入门进阶学习路线、最新paper分享、疑问解答四个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近3000星球成员为创造更好的AI世界共同进步,知识星球入口:

学习3D视觉核心技术,扫描查看介绍,3天内无条件退款

圈里有高质量教程资料、可答疑解惑、助你高效解决问题

觉得有用,麻烦给个赞和在看~  

点云数据向图像数据转换(附源码)相关推荐

  1. flink sql 知其所以然(二)| 自定义 redis 数据维表(附源码)

    感谢您的关注  +  点赞 + 再看,对博主的肯定,会督促博主持续的输出更多的优质实战内容!!! 1.序篇-本文结构 背景篇-为啥需要 redis 维表 目标篇-做 redis 维表的预期效果是什么 ...

  2. 手把手教你做一个数据图表生成器(附源码)...

    我的需求:手动配置X轴.Y轴.图表标题等参数自动通过Pyecharts模块生成可视化的html数据图表,并将浏览器图表展示到UI界面上. [阅读全文] 制作出图表后的效果展示如下: 另外,生成后的图表 ...

  3. Vue使用ECharts完成2020年全国各地区GDP总量大数据可视化面板(附源码)

    就在上周全国各地区GDP总量上了热搜,一时兴起就想写个大数据面板展示 既然决定要写,那么就要考虑到图表和图标的使用,这里我是用了我最熟悉的两大框架ECharts和element-ui 一.我的构思步骤 ...

  4. leaflet 结合 d3.js 实现 geojson 数据地形剖面分析(附源码下载)

    前言 leaflet 入门开发系列环境知识点了解: leaflet api文档介绍,详细介绍 leaflet 每个类的函数以及属性等等 leaflet 在线例子 leaflet 插件,leaflet ...

  5. ssm通用数据展示系统 毕业设计-附源码200934

    SSM通用数据展示系统的设计与实现 摘 要 随着社会信息化的日益加强,传统的数据处理系统存在专用性太强.安全性不高.数据共享性差等缺陷,不利于企业应用.为了充分利用企业信息资产所带来的优势,企业迫切需 ...

  6. 前端vue显示柱状图_详解Vue2+Echarts实现多种图表数据可视化Dashboard(附源码)_旧址_前端开发者...

    数据可视化 将数据通过图表的形式展现出来将大大的提升可读性和阅读效率 本例包含柱状图.折线图.散点图.热力图.复杂柱状图.预览面板等 技术栈 vue2.x vuex 存储公共变量,如色值等 eleme ...

  7. easypoi 批量导出_POI导出大量数据的简单解决方案(附源码)

    说明:我的电脑 2.0CPU 2G内存 能够十秒钟导出 20W 条数据 ,12.8M的excel内容压缩后2.68M 我们知道在POI导出Excel时,数据量大了,很容易导致内存溢出.由于Excel ...

  8. 微信小程序云开发打车系统实现附源码

    功能介绍: 实现用户,司机端,用户微信授权登录,司机注册,在线约车,位置定位,订单管理,司机抢单等 部分功能截图: 主页预约界面: 预约界面 订单界面 个人界面 源代码获取: 毕业设计,一周类做完,毕 ...

  9. 【Java项目】讲讲我用Java爬虫获取LOL英雄数据与图片(附源码)

最新文章

  1. swift3.0三种反向传值
  2. Windows异常学习笔记(四)—— 编译器扩展SEH
  3. python tensorflow tf.layers.max_pooling2d() 2维输入(例如图像)的最大池化层
  4. QWT中Qdial的入门介绍
  5. 玩转控件:对Dev的GridControl控件扩展
  6. 嵌入式常见笔试题总结
  7. CV方向介绍 | 基于自然语言的跨模态行人re-id的SOTA方法简述(上)
  8. php mysql密码验证_php 连接数据库 验证用户名密码
  9. 重建Windows 7的图标缓存
  10. 19复变函数的积分(五)
  11. [转]Android ANR 分析解决方法
  12. php之$_SESSION的理解
  13. 全网首发:以字型为例,一维表示的二维数组矩阵,以易理解的方式旋转90、-90
  14. 开启cdn后无法显示字体图标——CDN服务器跨域问题
  15. android之exoplayer
  16. 盘点各专业到古代都能干些啥
  17. Unity Recorder 插件录屏--简单小记
  18. java.net.url 中文乱码_asp.net URL中包含中文参数造成乱码的解决方法
  19. DCA1000 Training Video(DCA1000培训视频)
  20. java 时间判断大小_java判断时间大小

热门文章

  1. 算法笔记_114:等额本金(Java)
  2. Python.h: No such file or directory
  3. SpringMVC-Mybatis学习总结整理(下)
  4. PHP中的urlencode,rawurlencode和JS中的encodeURI,encodeURIComponent
  5. postgresql基本操作
  6. 敏感数据,“一键脱敏”,Sharding Sphere 完美搞定
  7. 熬夜彻底搞懂Cookie Session Token JWT
  8. 揭秘百度微服务监控:百度游戏服务监控的演进
  9. 如何解决MySQL order by limit语句的分页数据重复问题?
  10. 某程序员女友抱怨:男朋友工作975,天天回家说好累,不肯亲热,倒头就睡,难怪程序员没女朋友!...