0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

创建点云数据的Birdseye视图

新机器视觉 来源:CSDN 作者:CSDN 2021-04-01 14:39 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

点云数据——The Point Cloud Data

点云数据应表示为具有N行和至少3列的numpy数组。每行对应于单个点,其在空间(x,y,z)中的位置使用至少3个值表示。

d0be3e4c-92a7-11eb-8b86-12bb97331649.png

如果点云数据来自LIDAR传感器,那么它可能具有每个点的附加值,例如“反射率”,其是在该位置中障碍物反射多少激光光束的量度。在这种情况下,点云数据可能是Nx4阵列。

图像与点云坐标——Image vs Point Cloud Coordinates

点云的轴与图像中的轴具有完全不同的含义。下图显示了蓝色的图像轴和橙色的点云轴。

d0d158ba-92a7-11eb-8b86-12bb97331649.png

关于图像需要注意的一些重要事项:

图像中的坐标值始终为正。

原点位于左上角。

坐标是整数值。

有关点云坐标的注意事项:

点云中的坐标值可以是正数或负数。

坐标可以采用实数值。

正x轴表示向前。

正y轴表示左。

正z轴表示向上。

创建点云数据的Birdseye视图

鸟瞰图的相关轴
为了创建鸟眼视图图像,来自点云数据的相关轴将是x和y轴。

d0e948f8-92a7-11eb-8b86-12bb97331649.png

但是,正如我们从上图所示,我们必须小心并考虑以下事项:
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()

d0f64850-92a7-11eb-8b86-12bb97331649.jpg

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

import cv2 #通过cv2显示 cv2.imshow("im",im) cv2.waitKey() cv2.destroyAllWindows()

d1411b0a-92a7-11eb-8b86-12bb97331649.png

完整代码——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 < 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

责任编辑:lq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 传感器
    +关注

    关注

    2573

    文章

    54368

    浏览量

    786041
  • 激光光束
    +关注

    关注

    0

    文章

    16

    浏览量

    7067
  • 点云
    +关注

    关注

    0

    文章

    59

    浏览量

    4045

原文标题:点云数据详解——点云数据变为图像

文章出处:【微信号:vision263com,微信公众号:新机器视觉】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    自动驾驶模型是如何“看”懂信息的?

    [首发于智驾最前沿微信公众号]在自动驾驶中,是一个非常重要的感知信号,如激光雷达(LiDAR)生成的,其实就是三维空间里一堆带坐标、强度和时间戳的
    的头像 发表于 11-29 10:25 1571次阅读
    自动驾驶模型是如何“看”懂<b class='flag-5'>点</b><b class='flag-5'>云</b>信息的?

    知声入选国家知识产权示范企业创建推荐单位

    近日,上海市知识产权局公示了《2025-2027年知识产权强国建设示范创建工作推荐结果》。知声(上海)智能科技有限公司凭借其卓越的自主创新能力与知识产权,成功入选“国家知识产权示范企业创建推荐单位
    的头像 发表于 11-04 10:28 419次阅读

    AI 驱动三维逆向:降噪算法工具与机器学习建模能力的前沿应用

    在三维逆向工程领域,传统方法在处理复杂数据和构建高精度模型时面临诸多挑战。随着人工智能(AI)技术的发展,降噪算法工具与机器学习建模能力的应用,为三维逆向工程带来了创新性解决方案,显著提升
    的头像 发表于 08-20 10:00 449次阅读
    AI 驱动三维逆向:<b class='flag-5'>点</b><b class='flag-5'>云</b>降噪算法工具与机器学习建模能力的前沿应用

    HarmonyOS5服务技术分享--函数预加载文章整理

    ​​嗨,亲爱的开发者朋友们!​​? 今天咱们来聊聊如何使用​​端一体化方式开发函数​​,尤其针对华为的预加载服务。整个过程会手把手带你从零开始,涵盖创建工程、编写代码、调试到部署,帮你轻松掌握
    发表于 05-22 20:33

    HarmonyOS5服务技术分享--Serverless抽奖模板部署

    活动。不用写复杂代码,跟着步骤走就能搞定,文末还有部署避坑指南哦~ 一、前期准备 1️⃣ ​​注册账号+创建项目​​ 先到华为开发者平台注册账号 创建新项目时记得勾选\"开发
    发表于 05-22 20:25

    HarmonyOS5服务技术分享--存储指南

    Hi各位开发者伙伴们!今天咱们来聊一聊HarmonyOS存储的实战玩法,手把手教你实现文件上传、下载、元数据操作等核心功能。无需官方文档的严肃感,咱们用最接地气的方式搞懂这些API怎么用!(文末附
    发表于 05-22 19:17

    HarmonyOS5服务技术分享--ArkTS调用函数

    小白也能轻松上手!(文末有完整代码模板哦) ? 前期准备 1️⃣ 登录华为AppGallery Connect控制台 2️⃣ 确保已创建函数项目(还没有的话快去后台创建一个吧) ? 核心四步走
    发表于 05-22 18:22

    HarmonyOS5服务技术分享--ArkTS开发Node环境

    Studio 3.0+,配置HarmonyOS SDK。 ​​服务开通​​:在AGC(AppGallery Connect)控制台创建项目,开通函数服务,获取agconnect-services.json
    发表于 05-22 17:21

    HarmonyOS5服务技术分享--函数创建配置指南

    大家好!今天咱们来一起探索如何在华为HarmonyOS的AGC函数服务中创建和配置函数,手把手教你用HTTP触发器玩转函数。无论你是刚入门还是想优化现有功能,这篇指南都会给你清晰的指引,咱们直接
    发表于 05-22 17:08

    自动驾驶中常提的“”是个啥?

    啥?对自动驾驶有何影响? 是个啥? (Point Cloud)是一种在三维空间中由大量离散组成的
    的头像 发表于 05-21 09:04 821次阅读
    自动驾驶中常提的“<b class='flag-5'>点</b><b class='flag-5'>云</b>”是个啥?

    【第一章 透传策略】手把手教你玩转新版正点原子

    ,点击扩展功能-&gt;透传策略-&gt;创建透传策略 2.9 进行A端设备选择 2.10 进行B端透传目标选择,创建完成提示保存成功 2.11 至此
    发表于 03-05 16:52

    DLP6500获得的数目不多是什么原因造成的?

    我有一个疑问,我完成了DLP_LightCrafter_6500_3D_Scan_Application.exe中设定的步骤之后, 1、获得的扫描物体的并不多,这个是什么原因造成的呢? 2
    发表于 03-03 08:33

    C#通过Halcon实现3D重绘

    C# 通过 Halcon 实现 3D 重绘
    发表于 01-05 09:16 0次下载

    think-cell——Tableau数据

    到 Tableau 的要求 当然,您需要访问 Tableau 服务器才能将图表链接到 Tableau 数据。think-cell 支持 Tableau 的本地和托管版本。要链接到视图的筛选版本,您需要能够在托管
    的头像 发表于 01-03 11:47 883次阅读
    think-cell——Tableau<b class='flag-5'>数据</b>

    自动化创建UI并解析数据

    *附件:32960_auto.rar备注:Main.vi是ui自动化2.1.vi,配置文件为32960.B.ini。 目前可以实现根据配置文件自动化创建控件并布局,且可以自动解析接收到的数据内容写入
    发表于 12-10 08:41