3D点云数据转为俯瞰图Python实现代码

news2024/12/25 12:52:49

我主要是参考了英文博客来撰写本篇文章,仅作为个人学习笔记参考使用。

文章目录

    • 一、点云数据
    • 二、图像与点云坐标
    • 三、创建点云数据的鸟瞰视图
      • 3.1 鸟瞰图的相关坐标轴
      • 3.2 限制点云数据范围
      • 3.3 将点位置映射到像素位置
      • 3.4 切换到新的零点
      • 3.5 像素值
      • 3.6 创建图像矩阵
      • 3.7 显示

一、点云数据

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

在这里插入图片描述

如果点云数据来自激光雷达传感器,那么它可能会为每个点提供额外的值,例如“反射率”,这是对该位置障碍物反射回多少激光光束的测度。在这种情况下,点云数据可能是一个 N × 4 N\times 4 N×4 数组。

二、图像与点云坐标

在这里插入图片描述

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

  • 图像中的坐标值总是正的。

  • 原点位于左上角。

  • 坐标是整数值。

关于点云坐标需要注意的事项:

  • 点云中的坐标值可以是正的,也可以是负的。

  • 坐标可以取实数。

  • 正x轴代表向前。

  • 正y轴代表左。

  • 正z轴代表向上。

三、创建点云数据的鸟瞰视图

3.1 鸟瞰图的相关坐标轴

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

在这里插入图片描述

然而,正如我们从上图中看到的,我们必须小心,并考虑到以下几点:

  • x轴和y轴的意思正好相反。

  • x和y轴指向相反的方向。

  • 你必须移动这些值,使 ( 0 , 0 ) (0,0) (0,0) 成为图像中最小的坐标值。

3.2 限制点云数据范围

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

因为我们是从顶部看数据,我们目的在于将其转换为图像,我将使用与图像轴更一致的方向。下面,我指定了相对于原点我想要关注的值范围。原点左边的任何东西都被认为是负的,右边的任何东西都被认为是正的。点云的x轴将被解释为向前方向(这将是我们的鸟瞰图像的向上方向)。

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

side_range=(-10, 10)     # left-most to right-most
fwd_range=(0, 20)       # back-most to forward-most

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

# 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]

3.3 将点位置映射到像素位置

现在,我们有一堆取实数值的点。以便将这些值映射到整型位置值。我们可以简单地将所有的x和y值类型转换为整数,但最终可能会失去很多分辨率。例如,如果这些点的测量单位是米,那么每个像素将代表点云中 1 × 1 1\times1 1×1 米的矩形,我们将失去比这更小的细节。如果你有一个像山景一样的点云,这可能很好。但如果你想捕捉更精细的细节,识别人类、汽车,甚至更小的东西,那么这种方法就不好了。

然而,上面的方法可以稍微修改一下,这样我们就可以得到我们想要的分辨率。在类型转换为整数之前,我们可以先缩放数据。例如,如果测量单位是米,我们想要5cm的分辨率,我们可以这样做:

res = 0.05
# 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

你可能已经注意到x轴和y轴被交换了,方向颠倒了,这样我们就可以开始处理图像坐标了。

3.4 切换到新的零点

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 shift
x_img -= int(np.floor(side_range[0] / res))
y_img += int(np.ceil(fwd_range[1] / res))
height_range = (-2, 0.5)  # bottom-most to upper-most

# 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])

3.5 像素值

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

  • 像素值应该是整数。

  • 像素值应该是介于0-255之间的值。

我们可以从数据中获取最小和最大高度值,并重新缩放该范围以适应 0~255 的范围。另一种方法,这里将使用的是设置我们想要集中的高度值的范围,并且高于或低于该范围的任何东西都被剪辑为最小和最大值。这是有用的,因为它允许我们从感兴趣的区域获得最大数量的细节。

height_range = (-2, 0.5)  # bottom-most to upper-most

# 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])

接下来,我们将这些值重新调整为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-255
pixel_values  = scale_to_255(pixel_values, min=height_range[0], max=height_range[1])

3.6 创建图像矩阵

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

# 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

3.7 显示

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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/560191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IOS最新版开通GPT-PLUS方法

前提&#xff0c;美国IP魔法 不多说了 1.拥有一个美区apple id账号 可以买&#xff0c;也可以自己申请 自己申请就打开魔法到apple官网注册&#xff0c;用gmail邮箱&#xff0c;然后地址用美国地址生成器&#xff0c;记得选免税州 2.充值礼品卡 支付宝可以充值礼品卡&…

大模型总是「胡说八道」怎么办?手把手教你如何应对!

随着 ChatGPT 的出现&#xff0c;「AI 幻觉」一词被频繁提及。那么&#xff0c;什么是 AI 幻觉&#xff1f;简单来说&#xff0c;就是大模型在一本正经地胡说八道。 不止 ChatGPT&#xff0c;其他大语言模型也经常如此&#xff0c;究其根本是大语言模型在训练的过程中存在数据偏…

驱动开发-----io模型总结(2023-5-23)

1.非阻塞模型 在我们使用open函数时&#xff0c;将打开的驱动设置为O_NONBLOCK时&#xff0c;当我们用read函数去读取硬件数据时&#xff0c;无论硬件是否有数据&#xff0c;都会往下执行&#xff0c;不会被阻塞在这里 2.阻塞模型 在我们使用open函数时&#xff0c;没有设置…

C++学习之路-变量和基本内置类型

变量和基本内置类型 一、基本内置类型1.1 算数类型1.2 带符号类型和无符号类型1.3 类型转换含有无符号类型的表达式 1.4 字面值常量整形和浮点型字面值字符和字符串字面值转义序列指定字面值的类型 二、变量2.1 变量的定义初始化列表初始化默认初始化 2.2 变量声明和定义的关系…

斐波那契数列数列相关简化1

斐波那契数列问题介绍&#xff1a; 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;因数学家莱昂纳多斐波那契&#xff08;Leonardo Fibonacci&#xff09;以兔子繁殖为例子而引入&#xff0c;故又称为“兔子数列”&#xf…

包管理工具详解npm、yarn、cnpm、npx、pnpm

目录&#xff1a; 1 npm包管理工具 2 package配置文件 3 npm install原理 4 yarn、cnpm、npx 5 发布自己的开发包 6 pnpm使用和原理 当我们使用npm install xxxx 的时候会添加一个node_module和2个json文件&#xff1a; package.json是配置信息文件&#xff0c;  这个配…

Go完整即时通讯项目及Go的生态介绍

Go完整即时通讯项目 项目架构&#xff1a; 1 编写基本服务端-Server server.go package mainimport ("fmt""net" )// 定义服务端 type Server struct {ip stringport int }// 创建一个Server func NewServer(ip string, port int) *Server {return …

Jenkins + docker-compose 在 Centos 上搭建部署

一、前期准备 1. 检查 CentOS上 是否安装 docker 可以使用以下命令&#xff1a; sudo docker version 如果已经安装了Docker&#xff0c;它将显示有关Docker版本和构建信息的输出。如果未安装Docker&#xff0c;将收到有关命令未找到的错误消息。 2. 检查是否安装 docker-…

cookie-机制

目录 一、基础概念 二、cookie的处理方式 一、基础概念 1、cookie是存储在客户端的一组键值对 2、web中cookie的典型应用&#xff1a;免密登陆 3、cookie和爬虫之间的关联 有时&#xff0c;对一张页面进行请求的时候&#xff0c;如果请求的过程中不携带cookie的话&#xf…

Openai+Coursera: ChatGPT Prompt Engineering(四)

想和大家分享一下最近学习的Coursera和openai联合打造ChatGPT Prompt Engineering在线课程.以下是我写的关于该课程的前两篇博客&#xff1a; ChatGPT Prompt Engineering(一)ChatGPT Prompt Engineering(二)ChatGPT Prompt Engineering(三) 今天我们来学习第三部分内容&…

Java on Azure Tooling 4月更新|路线图更新及 Azure Toolkit for IntelliJ 增强

作者&#xff1a;Jialuo Gan - Program Manager, Developer Division at Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到 Java on Azure 工具产品的4月更新。让我们首先来谈谈我们对未来几个月的 Java on Azure 开发工具的投资。在这次更新中&#xff0c;我们…

js - 闭包

1、闭包的概念 闭包&#xff1a;函数嵌套函数&#xff0c;内层函数访问了外层函数的局部变量。 // 闭包 function func1() {let a 9;let b 8;function func2() {console.log("a", a); // a 9}func2(); } func1(); 分析&#xff1a; 需要访问的变量会被放到闭包…

【云原生|Kubernetes】05-Pod的存储卷(Volume)

【云原生Kubernetes】05-Pod的存储卷&#xff08;Volume) 文章目录 【云原生Kubernetes】05-Pod的存储卷&#xff08;Volume)简介Volume类型解析emptyDirHostPathgcePersistentDiskNFSiscsiglusterfsceph其他volume 简介 Volume 是Pod 中能够被多个容器访问的共享目录。 Kubern…

ChatGPT可以帮助开发人员的8种方式...

“适应或灭亡”是科技界的口头禅&#xff0c;如果您是开发人员&#xff0c;则尤其如此。 由于技术的动态发展&#xff0c;开发人员面临着比大多数人更大的压力&#xff0c;他们要领先于适应和精通最好的工具。ChatGPT 是最新的此类工具。 虽然有人说 ChatGPT 是“工作杀手”&…

比Figma更丝滑的“Figma网页版“

随着互联网的全面普及和全球化&#xff0c;设计协作工具逐渐成为团队协作中不可或缺的一部分。设计师们常需要通过在线设计协作工具来完成设计任务&#xff0c;而 Figma 作为协作工具的佼佼者&#xff0c;成为了许多设计师心中的首选。但是&#xff0c;对于国内设计师来说&…

Leetcode406. 根据身高重建队列

Every day a Leetcode 题目来源&#xff1a;406. 根据身高重建队列 解法1&#xff1a;贪心 题解&#xff1a;根据身高重建队列 我们先按照身高从大到小排序&#xff08;身高相同的情况下K小的在前面&#xff09;&#xff0c;这样的话&#xff0c;无论哪个人的身高都小于等于…

kubeadm安装集群的时候kube-proxy是如何安装的

背景 最近升级k8s集群时遇到这个问题&#xff0c;集群是使用kuberadm自动化脚本安装的&#xff0c;之前一直认为kubeadm安装的集群这些组件除了kubelet都是静态pod跑起来的。 其实kube-proxy并不是. kube-proxy是如何安装的 在使用kubeadmin安装Kubernetes集群时&#xff0c…

Echarts通过Jquery添加下拉列表动态改变展示的数据和图表

前言 在项目中&#xff0c;有时候我们会一些需求&#xff0c;比如要用Echarts绘制一个饼状图&#xff0c;并且要设置一个下拉列表&#xff0c;当我点击某个选项的时候&#xff0c;饼状图里面的数据会改变&#xff0c;图表样式也会发生改变。我们可以配合Jquery来实现这个功能。…

数字电路基础

目录 一、不同进制之间的转换 二、逻辑代数基础 三、门电路 四、组合逻辑电路 五、半导体存储电路 六、时序电路 一、不同进制之间的转换 二-十转换&#xff1a; 十-二转换&#xff1a; 二-十六转换 十六-二转换 八-二转换 二-八转换 十六-十转换&#xff1a; 先转换成…

python绘制气泡图|随机生成数据

python绘图系列文章目录 往期python绘图合集: python绘制简单的折线图 python读取excel中数据并绘制多子图多组图在一张画布上 python绘制带误差棒的柱状图 python绘制多子图并单独显示 python读取excel数据并绘制多y轴图像 python绘制柱状图并美化|不同颜色填充柱子 python随机…