【点云处理教程】02从 Python 中的深度图像估计点云

news/2024/5/18 18:59:46

一、说明

        这是“点云处理”教程的第二篇文章。“点云处理”教程对初学者友好,我们将在其中简单地介绍从数据准备到数据分割和分类的点云处理管道。在本教程中,我们将学习如何在不使用 Open3D 库的情况下从深度图像计算点云。我们还将展示如何优化代码以获得更好的性能。

【点云处理教程】00计算机视觉的Open3D简介

【点云处理教程】01如何创建和可视化点云 

【点云处理教程】02从 Python 中的深度图像估计点云 

【点云处理教程】03使用 Python 实现地面检测 

 【点云处理教程】04 Python 中的点云过滤

【点云处理教程】05-Python 中的点云分割 

二. 深度图像

        深度图像(也称为深度图)是每个像素提供其相对于传感器坐标系的距离值的图像。深度图像可以通过结构光或飞行时间传感器捕获。为了计算深度数据,结构光传感器(如 Microsoft Kinect V1)会比较投影光和接收光之间的失真。至于像Kinect V2 Microsoft这样的飞行时间传感器,它们投射光线,然后计算投影和接收这些光线之间的时间。

        除了深度图像外,一些传感器还提供相应的RGB图像以形成RGB-D图像。后者使得计算彩色点云成为可能。本教程将使用Microsoft Kinect V1 RGB-D 图像作为示例。

        让我们从导入库开始:

import imageio.v3 as iio
import numpy as np
import matplotlib.pyplot as plt
import open3d as o3d

        现在,我们可以导入深度图像并打印其分辨率和类型:

# Read depth image:
depth_image = iio.imread('data/depth.png')# print properties:
print(f"Image resolution: {depth_image.shape}")
print(f"Data type: {depth_image.dtype}")
print(f"Min value: {np.min(depth_image)}")
print(f"Max value: {np.max(depth_image)}")

Image resolution: (480, 640)
Data type: int32
Min value: 0
Max value: 2980

        深度图像是大小为 640×480 的矩阵,其中每个像素是一个 32(或 16)位整数,表示以毫米为单位的距离,因此深度图像在打开时显示为黑色(见下图)。最小值 0 表示噪点(没有距离),而最大值 2980 表示最远像素的距离。

        由 Microsoft Kinect V1 生成的深度图像。

为了获得更好的可视化效果,我们计算其灰度图像:

depth_instensity = np.array(256 * depth_image / 0x0fff, dtype=np.uint8)
iio.imwrite('output/grayscale.png', depth_instensity)

        计算灰度图像意味着将深度值缩放到 。现在图像更清晰了:[0, 255]

计算出的灰度图像。黑色像素表示噪点。

请注意,Matplotlib 在可视化深度图像时也会做同样的事情:

# Display depth and grayscale image:
fig, axs = plt.subplots(1, 2)
axs[0].imshow(depth_image, cmap="gray")
axs[0].set_title('Depth image')
axs[1].imshow(depth_grayscale, cmap="gray")
axs[1].set_title('Depth grayscale image')
plt.show()

Matplotlib自动缩放深度图像的像素。

三. 点云

        现在我们已经导入并显示了深度图像,我们如何从中估计点云?第一步是校准深度相机以估计相机矩阵,然后使用它来计算点云。获得的点云也称为2.5D点云,因为它是根据2D投影(深度图像)而不是激光传感器等3D传感器估计的。

3.2 深度相机校准

        校准相机意味着通过查找失真系数和相机矩阵(也称为固有参数)来估计镜头和传感器参数。一般来说,校准相机有三种方法:使用工厂提供的标准参数、使用校准研究中获得的结果或手动校准 Kinect。手动校准相机包括应用一种校准算法,例如棋盘算法[1]。该算法在机器人操作系统(ROS)和OpenCV中实现。校准矩阵 M 是一个 3×3 矩阵:

        其中fx,fy和cx,cy分别是焦距和光学中心。在本教程中,我们将使用获得的纽约大学深度 V2 数据集的结果:

# Depth camera parameters:
FX_DEPTH = 5.8262448167737955e+02
FY_DEPTH = 5.8269103270988637e+02
CX_DEPTH = 3.1304475870804731e+02
CY_DEPTH = 2.3844389626620386e+02

        如果您想自己校准相机,可以参考此OpenCV教程。

3.3 点云计算

        这里的计算点云是指将深度像素从深度图像2D坐标系转换为深度相机3D坐标系(x,y和z)。3D 坐标使用以下公式 [2] 计算,其中 depth(i, j) 是行 i 和列 j 处的深度值:

该公式应用于每个像素:

# compute point cloud:
pcd = []
height, width = depth_image.shape
for i in range(height):for j in range(width):z = depth_image[i][j]x = (j - CX_DEPTH) * z / FX_DEPTHy = (i - CY_DEPTH) * z / FY_DEPTHpcd.append([x, y, z])

让我们使用 Open3D 库显示它:

pcd_o3d = o3d.geometry.PointCloud()  # create point cloud object
pcd_o3d.points = o3d.utility.Vector3dVector(pcd)  # set pcd_np as the point cloud points
# Visualize:
o3d.visualization.draw_geometries([pcd_o3d])

从深度图像计算出的点云。

四. 彩色点云

        如果我们想从RGB-D图像中计算彩色点云怎么办?颜色信息可以增强许多任务(如点云配准)的性能。在这种情况下,如果输入传感器也提供RGB图像,则最好使用它。彩色点云可以定义如下:

        其中 x、y 和 z 是 3D 坐标,r、g 和 b 表示 RGB 系统中的颜色。

        我们首先导入上一个深度图像的相应 RGB 图像:

# Read the rgb image:
rgb_image = iio.imread('../data/rgb.jpg')# Display depth and grayscale image:
fig, axs = plt.subplots(1, 2)
axs[0].imshow(depth_image, cmap="gray")
axs[0].set_title('Depth image')
axs[1].imshow(rgb_image)
axs[1].set_title('RGB image')
plt.show()

        深度图像及其对应的 RGB 图像

        要查找深度传感器 3D 坐标系中定义的给定点 p(x, y,z) 的颜色,请执行以下操作:

1. 我们将其转换为 RGB 相机坐标系 [2]:

        其中 R 和 T 是两个相机之间的外在参数:分别是旋转矩阵和平移矢量。

        同样,我们使用来自纽约大学深度V2数据集的参数:

# Rotation matrix:
R = -np.array([[9.9997798940829263e-01, 5.0518419386157446e-03, 4.3011152014118693e-03],[-5.0359919480810989e-03, 9.9998051861143999e-01, -3.6879781309514218e-03],[- 4.3196624923060242e-03, 3.6662365748484798e-03, 9.9998394948385538e-01]])
# Translation vector:
T = np.array([2.5031875059141302e-02, -2.9342312935846411e-04, 6.6238747008330102e-04])

        RGB 照相机坐标系中的点计算方法如下:

"""Convert the point from depth sensor 3D coordinate systemto rgb camera coordinate system:
"""
[x_RGB, y_RGB, z_RGB] = np.linalg.inv(R).dot([x, y, z]) - np.linalg.inv(R).dot(T)

2. 使用 RGB 相机的固有参数,我们将其映射到彩色图像坐标系 [2]:

这些是获取颜色像素的索引。

请注意,在前面的公式中,焦距和光学中心是RGB相机参数。同样,我们使用来自纽约大学深度V2数据集的参数:

# RGB camera intrinsic Parameters:
FX_RGB = 5.1885790117450188e+02
FY_RGB = 5.1946961112127485e+02
CX_RGB = 3.2558244941119034e+0
CY_RGB = 2.5373616633400465e+02

        对应像素的指数计算如下:

"""Convert from rgb camera coordinate systemto rgb image coordinate system:
"""
j_rgb = int((x_RGB * FX_RGB) / z_RGB + CX_RGB + width / 2)
i_rgb = int((y_RGB * FY_RGB) / z_RGB + CY_RGB)

        让我们把所有东西放在一起,显示点云:

colors = []
pcd = []
for i in range(height):for j in range(width):"""Convert the pixel from depth coordinate systemto depth sensor 3D coordinate system"""z = depth_image[i][j]x = (j - CX_DEPTH) * z / FX_DEPTHy = (i - CY_DEPTH) * z / FY_DEPTH"""Convert the point from depth sensor 3D coordinate systemto rgb camera coordinate system:"""[x_RGB, y_RGB, z_RGB] = np.linalg.inv(R).dot([x, y, z]) - np.linalg.inv(R).dot(T)"""Convert from rgb camera coordinates systemto rgb image coordinates system:"""j_rgb = int((x_RGB * FX_RGB) / z_RGB + CX_RGB + width / 2)i_rgb = int((y_RGB * FY_RGB) / z_RGB + CY_RGB)# Add point to point cloud:pcd.append([x, y, z])# Add the color of the pixel if it exists:if 0 <= j_rgb < width and 0 <= i_rgb < height:colors.append(rgb_image[i_rgb][j_rgb] / 255)else:colors.append([0., 0., 0.])# Convert to Open3D.PointCLoud:
pcd_o3d = o3d.geometry.PointCloud()  # create a point cloud object
pcd_o3d.points = o3d.utility.Vector3dVector(pcd)
pcd_o3d.colors = o3d.utility.Vector3dVector(colors)
# Visualize:
o3d.visualization.draw_geometries([pcd_o3d])

从RGB-D图像计算出的彩色点云

五、代码优化

在本节中,我们将介绍如何优化代码,使其更高效并适合实时应用程序。

5.1 点云

使用嵌套循环计算点云非常耗时。对于分辨率为 480×640 的深度图像,在具有 8GB RAM 和 i7-4500 CPU 的机器上,计算点云大约需要 2.154 秒

为了减少计算时间,嵌套循环可以用矢量化操作代替,计算时间可以减少到0.024秒左右:

# get depth resolution:
height, width = depth_im.shape
length = height * width
# compute indices:
jj = np.tile(range(width), height)
ii = np.repeat(range(height), width)
# rechape depth image
z = depth_im.reshape(length)
# compute pcd:
pcd = np.dstack([(ii - CX_DEPTH) * z / FX_DEPTH,(jj - CY_DEPTH) * z / FY_DEPTH,z]).reshape((length, 3))

        我们还可以通过在开始时计算一次常数来将计算时间减少到大约 0.015 秒

# compute indices:
jj = np.tile(range(width), height)
ii = np.repeat(range(height), width)
# Compute constants:
xx = (jj - CX_DEPTH) / FX_DEPTH
yy = (ii - CY_DEPTH) / FY_DEPTH
# transform depth image to vector of z:
length = height * width
z = depth_image.reshape(height * width)
# compute point cloud
pcd = np.dstack((xx * z, yy * z, z)).reshape((length, 3))

4.2 彩色点云

        至于彩色点云,在同一台机器上,执行前面的示例大约需要 36.263 秒。通过应用矢量化,运行时间减少到 0.722 秒

# compute indices:
jj = np.tile(range(width), height)
ii = np.repeat(range(height), width)# Compute constants:
xx = (jj - CX_DEPTH) / FX_DEPTH
yy = (ii - CY_DEPTH) / FY_DEPTH# transform depth image to vector of z:
length = height * width
z = depth_image.reshape(length)# compute point cloud
pcd = np.dstack((xx * z, yy * z, z)).reshape((length, 3))
cam_RGB = np.apply_along_axis(np.linalg.inv(R).dot, 1, pcd) - np.linalg.inv(R).dot(T)
xx_rgb = ((cam_RGB[:, 0] * FX_RGB) / cam_RGB[:, 2] + CX_RGB + width / 2).astype(int).clip(0, width - 1)
yy_rgb = ((cam_RGB[:, 1] * FY_RGB) / cam_RGB[:, 2] + CY_RGB).astype(int).clip(0, height - 1)
colors = rgb_image[yy_rgb, xx_rgb]/255

六. 结论

        在本教程中,我们学习了如何从 RGB-D 数据计算点云。在下一个教程中,我们将以一个简单的地面检测为例,仔细分析点云。

谢谢,我希望你喜欢阅读这篇文章。您可以在我的 GitHub 存储库中找到示例。

引用

[1] Zhang, S., & Huang, P. S. (2006).结构光系统校准的新方法。光学工程, 45(8), 083601.

[2] 周旭, (2012).Microsoft Kinect 校准的研究。费尔法克斯乔治梅森大学计算机科学系


http://www.mrgr.cn/p/22802274

相关文章

【VUE】解决图片视频加载缓慢/首屏加载白屏的问题

1 问题描述 在 Vue3 项目中&#xff0c;有时候会出现图片视频加载缓慢、首屏加载白屏的问题 2 原因分析 通常是由以下原因导致的&#xff1a; 图片或视频格式不当&#xff1a;如果图片或视频格式选择不当&#xff0c;比如选择了无损压缩格式&#xff0c;可能会导致文件大小过大…

微信小程序交易体验分常见问题指引

小程序交易体验分是为保障小程序用户的交易体验&#xff0c;促进开发者向用户提供更好的服务&#xff0c;帮助开发者更好的评估自身服务水平的机制。平台将对开发者在其小程序的违规行为进行判定&#xff0c;根据违规行为的严重程度对该小程序扣减不同分值的交易体验分&#xf…

风靡朋友圈的妙鸭相机,到底用了哪些底层技术?

不知道大家近期的朋友圈有没有被和海马体、天真蓝如出一辙的AI写真刷屏&#xff01; 这些面若桃花、精致到头发丝、光影充满氛围感的写真都是一款叫“妙鸭相机”的小程序生成的&#xff01;只要9.9&#xff0c;就能体验999写真&#xff01; 虽然只要9.9&#xff0c;但生成的照片…

opencv-24 图像几何变换03-仿射-cv2.warpAffine()

什么是仿射&#xff1f; 仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够 保持图像的平直性和平行性。平直性是指图像经过仿射变换后&#xff0c;直线仍然是直线&#xff1b;平行性是指 图像在完成仿射变换后&#xff0c;平行线仍然是平行线。…

海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储

文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/131679108 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结…

服务器 Docker Alist挂载到本地磁盘(Mac版)夸克网盘

1.服务器下载alist 默认有docker环境 docker pull xhofe/alist2.生成容器 -v /home/alist:/opt/alist/data 这段意思是alist中的数据映射到docker 主机的文件夹&#xff0c;/home/alist就是我主机的文件夹&#xff0c;这个文件夹必须先创建 docker run -d --restartalways…

【Python】数据分析+数据挖掘——探索Pandas中的数据筛选

1. 前言 当涉及数据处理和分析时&#xff0c;Pandas是Python编程语言中最强大、灵活且广泛使用的工具之一。Pandas提供了丰富的功能和方法&#xff0c;使得数据的选择、筛选和处理变得简单而高效。在本博客中&#xff0c;我们将重点介绍Pandas中数据筛选的关键知识点&#xff…

ChatGPT结合知识图谱构建医疗问答应用 (一) - 构建知识图谱

一、ChatGPT结合知识图谱 在本专栏的前面文章中构建 ChatGPT 本地知识库问答应用&#xff0c;都是基于词向量检索 Embedding 嵌入的方式实现的&#xff0c;在传统的问答领域中&#xff0c;一般知识源采用知识图谱来进行构建&#xff0c;但基于知识图谱的问答对于自然语言的处理…

Nginx配置WebSocket反向代理

1、WebSocket协议 ​ WebSocket协议相比较于HTTP协议成功握手后可以多次进行通讯&#xff0c;直到连接被关闭。但是WebSocket中的握手和HTTP中的握手兼容&#xff0c;它使用HTTP中的Upgrade协议头将连接从HTTP升级到WebSocket。这使得WebSocket程序可以更容易的使用现已存在的…

深度学习——LSTM解决分类问题

RNN基本介绍 概述 循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一种深度学习模型&#xff0c;主要用于处理序列数据&#xff0c;如文本、语音、时间序列等具有时序关系的数据。 核心思想 RNN的关键思想是引入了循环结构&#xff0c;允许…

记一次安装nvm切换node.js版本实例详解

最后效果如下&#xff1a; 背景&#xff1a;由于我以前安装过node.js&#xff0c;后续想安装nvm将node.js管理起来。 问题&#xff1a;nvm-use命令行运行成功&#xff0c;但是nvm-list显示并没有成功。 原因&#xff1a;因为安装过node.js&#xff0c;所以原先的node.js不收n…

CloudStudio搭建Next框架博客_抛开电脑性能在云端编程(沉浸式体验)

文章目录 ⭐前言⭐进入cloud studio工作区指引&#x1f496; 注册coding账号&#x1f496; 选择cloud studio&#x1f496; cloud studio选择next.js&#x1f496; 安装react的ui框架&#xff08;tDesign&#xff09;&#x1f496; 安装axios&#x1f496; 代理请求跨域&#x…

2.获取DOM元素

获取DOM元素就是利用JS选择页面中的标签元素 2.1 根据CSS选择器来获取DOM元素(重点) 2.1.1选择匹配的第一个元素 语法: document.querySelector( css选择器 )参数: 包含一个或多个有效的CSS选择器 字符串 返回值: CSS选择器匹配的第一个元素&#xff0c;一个HTMLElement对象…

Data Structure, Algorithm,and Applications in C++

在学习这本书进阶内容之前&#xff0c;我们可以跟着它的第一章部分再巩固和复习。本书由Sartaj Sahni撰写&#xff0c;由王立柱和刘志红翻译。全书通俗易懂&#xff0c;内容丰富&#xff0c;是巩固C内容的不二选择。希望本文对各位有所帮助。 目录 1.函数与参数 1.1.传值参数…

【Linux】用户相关内容

如果命令ll 出现以上信息&#xff0c;UID为具体的数字&#xff0c;代表之前UID为502的用户被删除了。 更改目录或文件所属用户和所属组 在Linux中&#xff0c;创建一个文件时&#xff0c;该文件的拥有者都是创建该文件的用户。 更改所属用户 chown 用户名 文件名/目录名 更…

:is()、:where() 和 :has() 伪元素

:is()、:where() 和 :has() 伪元素是 CSS 中用于样式化元素的非常强大的工具。它们是在 CSS 选择器 Level 4 规范中引入的。它们允许我们将样式应用于符合特定条件的任何元素&#xff0c;例如元素的类型、元素的位置和元素的后代。 :is() :is() - CSS&#xff1a;层叠样式表 …

Python 进阶(三):正则表达式(re 模块)

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 1. 导入re模块2. re模块中的常用函数2.1 re.search()2.2 re.findall()2.3 re.sub()2.4…

开源快速开发平台:做好数据管理,实现流程化办公!

做好数据管理&#xff0c;可以提升企业的办公协作效率&#xff0c;实现数字化转型。开源快速开发平台是深受企业喜爱的低代码开发平台&#xff0c;拥有多项典型功能&#xff0c;是可以打造自主可控快速开发平台&#xff0c;实现一对一框架定制的软件平台。在快节奏的社会中&…

游戏APP开发:创新设计的秘诀

在游戏 APP开发中&#xff0c;创新设计是游戏开发公司的一大追求&#xff0c;为了可以为用户带来更好的游戏体验&#xff0c;这就需要对游戏 APP开发进行创新设计。那么&#xff0c;游戏 APP开发中的创新设计是什么呢&#xff1f;接下来&#xff0c;我们就一起来看看吧。 想要…

基于深度学习的高精度80类动物目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度80类动物目标检测识别系统可用于日常生活中或野外来检测与定位80类动物目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的80类动物目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YO…