Pybullet获取RGB图像和深度图像构建点云(Open3D)

这篇具有很好参考价值的文章主要介绍了Pybullet获取RGB图像和深度图像构建点云(Open3D)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  最近正在做点云分割相关的课题,数据集采集有点麻烦,想通过Pybullet先制作一批仿真合成数据集出来。虽然思路挺清晰,由RGB-D图像生成点云,但是中间有很多地方会卡住,所以写篇blog记录一下。

图像拍摄

  图像的拍摄挺简单的,直接用Pybullet现成的函数就可以获取RGB图像和深度图像,就是先要对物体还有相机的位置朝向等做个设置。

import pybullet as p
import pybullet_data
import numpy as np
import cv2
import PIL.Image as Image
import open3d as o3d

# 连接引擎
_ = p.connect(p.GUI)
# 不展示GUI的套件
p.configureDebugVisualizer(p.COV_ENABLE_GUI, 1)
# 添加资源路径
p.setAdditionalSearchPath(pybullet_data.getDataPath())


planeUid = p.loadURDF("plane.urdf", useMaximalCoordinates=True)  # 加载一个地面
trayUid = p.loadURDF("tray/traybox.urdf", basePosition=[0, 0, 0])  # 加载一个箱子,设置初始位置为(0,0,0)
p.setGravity(0,0,-10)

width = 1080  # 图像宽度
height = 720   # 图像高度

fov = 50  # 相机视角
aspect = width / height  # 宽高比
near = 0.01  # 最近拍摄距离
far = 20  # 最远拍摄距离

cameraPos = [0,0,1]  # 相机位置
targetPos = [0,0,0]  # 目标位置,与相机位置之间的向量构成相机朝向
cameraupPos = [1,0,0]  # 相机顶端朝向

viewMatrix = p.computeViewMatrix(
    cameraEyePosition=cameraPos,
    cameraTargetPosition=targetPos,
    cameraUpVector=cameraupPos,
    physicsClientId=0
)  # 计算视角矩阵
projection_matrix = p.computeProjectionMatrixFOV(fov, aspect, near, far)  # 计算投影矩阵
w, h, rgb, depth, seg = p.getCameraImage(width, height, viewMatrix, projection_matrix, renderer=p.ER_BULLET_HARDWARE_OPENGL)
# 由此便可以得到RGB图像、深度图像、分割标签图像

图像存储

  接下来就是将图像存储下来了,这里有挺多坑的。

彩色图像

  彩色图像的存储还是比较简单的,我这里直接用OpenCV存,就是需要将RGB通道转换一下,OpenCV存储时的通道顺序有点不一样。

rgbImg = cv2.cvtColor(images[2], cv2.COLOR_BGR2RGB)
cv2.imwrite('image/rgb.jpg',rgbImg)

深度图像

  坑1:深度图像一般的存储单位都是毫米,而Pybullet里的单位是米,所以需要做个换算。
  坑2:需要对深度图像的深度值做一个透视变换,原理参考下文:《【3D数学】——透视变换与相机偏手性》,主要就是如下公式(符号意义看链接中的文章): m 3 + m 4 z = z ′ ⇒ z = m 4 z ′ − m 3 m_3+\frac{m_4}{z}=z'\Rightarrow z= \frac{m_4}{z'-m_3} m3+zm4=zz=zm3m4
  坑3:由于深度数值会超过255,因此深度图像需要存储成为int16格式,jpg文件只能存储int8的数据,因此需要存储成为png文件。
  存储代码如下(做了个按键交互):

i=1
# 开始渲染
while True:
    p.stepSimulation()
    images = p.getCameraImage(width, height, viewMatrix, projection_matrix, renderer=p.ER_BULLET_HARDWARE_OPENGL)
    
    keys = p.getKeyboardEvents()
    
    if ord("z") in keys and keys[ord("z")] & p.KEY_WAS_RELEASED:
        
        rgbImg = cv2.cvtColor(images[2], cv2.COLOR_BGR2RGB)      
        cv2.imwrite('image/rgb'+str(i)+'.jpg',rgbImg)

        depImg = far * near / (far - (far - near) * images[3])
        depImg = np.asanyarray(depImg).astype(np.float32) * 1000.  
        depImg = (depImg.astype(np.uint16))
        print(type(depImg[0,0]))
        depImg = Image.fromarray(depImg)
        depImg.save('image/depth'+str(i)+'.png')
        
        cv2.imwrite('image/seg'+str(i)+'.jpg',images[4])
        i=i+1

分割标签图像

  这个图像的像素值就是0,1,…,代表每个物体的标签,重构点云暂时用不到。


内参计算

  如果要通过RGB-D图像重建点云,那么必然需要相机的内参,但是Pybullet中的相机貌似只能计算得到投影矩阵,无法直接获取内参,那么就需要进行一定的计算获取内参参数。
  这里还是先稍微介绍一下相关概念。

坐标系

pybullet相机,计算机视觉,opencv,python图源:https://zhuanlan.zhihu.com/p/473172644
世界坐标系world:顾名思义。
相机坐标系camera:以相机的光心为原点,前为Z轴,右为X轴,下为Y轴。
图像坐标系xy:成像平面与光轴的交点为原点。真实情况下,图像平面是在光心Oc后面,所产生的图像是一个颠倒的画面。为了讨论方便,这里是将图像平面放到了Oc前面,因此此时图像平面的图像没有颠倒,而xy轴的方向与相机坐标系的方向相反,单位为mm。
像素坐标系:图像平面中图像左上角为原点,单位为pixel。
焦距:光心与成像平面之间的距离。

内外参

我们将世界到相机的变换称为相机的外参矩阵R,t。
我们将相机投影到图像的变换称为相机的内参矩阵K。
外参:将世界坐标系转换成相机坐标系。
内参:将相机坐标系转换成图像坐标系。
pybullet相机,计算机视觉,opencv,pythonpybullet相机,计算机视觉,opencv,python图源:https://zhuanlan.zhihu.com/p/473172644

fx = f / dx
fy = f / dy
f : 焦距长度,物理单位mm
dx、dy : x、y方向上一个像素分别代表多少mm
u0、v0 : 图像的中心像素坐标和图像原点像素坐标之间相差的横向和纵向像素数。理论值应该是图像宽度、高度的一半,但实际是有偏差的,一般摄像头越好越接近理论值。有些地方写作cx,cy。


  大概算是介绍完了一些基础概念,然后开始从Pybullet中的投影矩阵得到内参。投影矩阵就是上文代码中的projection_matrix,输出为16个元素的行向量数组,需要提取一下变成4X4矩阵,Pybullet采用的是OpenGL中的投影矩阵,形式如下:

[ 2 f x r − l 0 r + l r − l 0 0 2 f y t − b t + b t − b 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ]   \begin{bmatrix} \frac {2f_x} {r-l} & 0 & \frac {r+l} {r-l} & 0\\ 0 & \frac {2f_y} {t-b} & \frac {t+b} {t-b} &0 \\ 0 & 0 & \frac {-(f+n)} {f-n} & \frac {-2fn} {f-n} \\ 0 & 0 & -1 & 0 \end{bmatrix} \ rl2fx0000tb2fy00rlr+ltbt+bfn(f+n)100fn2fn0  
  其中:
{ l = − c x r = W − c x b = − c y t = H − c y \begin{cases} l=-c_x \\ r=W-c_x\\ b=-c_y \\ t=H-c_y \\ \end{cases} l=cxr=Wcxb=cyt=Hcy
  H、W分别为图像的高和宽,单位为pixel。
  f为最远拍摄距离,n为最近拍摄距离。
那么投影矩阵又可以表示为
[ 2 f x W 0 W − 2 c x W 0 0 2 f y H H − 2 c y H 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ]   \begin{bmatrix} \frac {2f_x} {W} & 0 & \frac {W-2c_x} {W} & 0\\ 0 & \frac {2f_y} {H} & \frac {H-2c_y} {H} &0 \\ 0 & 0 & \frac {-(f+n)} {f-n} & \frac {-2fn} {f-n} \\ 0 & 0 & -1 & 0 \end{bmatrix} \ W2fx0000H2fy00WW2cxHH2cyfn(f+n)100fn2fn0  
  Pybullet得到的投影矩阵和上面的投影矩阵属于转置关系,其计算公式为:(pybullet已经帮你算好了)
pybullet相机,计算机视觉,opencv,python
  因此做个一一对应就可以计算得到相机内参了,假设投影矩阵为P:
{ c x = W / 2 c y = H / 2 f x = P [ 0 , 0 ] ∗ W / 2 f y = P [ 1 , 1 ] ∗ H / 2 \begin{cases} c_x=W/2 \\ c_y=H/2 \\ f_x = P[0,0]*W/2 \\ f_y = P[1,1]*H/2 \end{cases} cx=W/2cy=H/2fx=P[0,0]W/2fy=P[1,1]H/2
  OK,总算是把内参算好了,畸变这里就不考虑了。
  接下来就简单了,Open3D读取然后重建一下就好了。

import open3d as o3d

color_image = o3d.io.read_image('image/rgb1.jpg')
depth_image = o3d.io.read_image('image/depth1.png')

rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth(color_image, depth_image,convert_rgb_to_intensity=False)
intrinsic = o3d.camera.PinholeCameraIntrinsic(
    o3d.camera.PinholeCameraIntrinsicParameters.Kinect2DepthCameraDefault )
intrinsic.set_intrinsics(width=1080, height=720, fx=2.14450693*1080/2, fy=2.14450693*720/2, cx=1080/2, cy=720/2)

point_cloud = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd_image, intrinsic)
o3d.visualization.draw_geometries([point_cloud])

  大致流程就是这样了,全部代码都在文章里面了,搞懂这一切还是挺花时间的。文章来源地址https://www.toymoban.com/news/detail-795234.html

到了这里,关于Pybullet获取RGB图像和深度图像构建点云(Open3D)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • open3d 深度图和点云数据互转,RGBD和点云互转

    关键代码: (1) 深度图转点云 需要知道相机内外参数。  (2) 点云转深度图  需要知道相机内外参数。  point_cloud_to_depth.py 深度图-》RGBD-》点云 需要知道相机内外参数。 point_cloud_to_rgbd.py

    2024年02月11日
    浏览(41)
  • 点云边缘获取并可视化(附open3d python代码)

    对于简化后续计算步骤,或者提取点云特征都比较有用 结果如下图;   代码如下:

    2024年02月12日
    浏览(57)
  • Azure Kinect DK点云实时可视化及图像点云按键交互存储(Open3D)

      Azure Kinect DK在python上的使用仍然很空白,开篇blog记录一下利用Open3D开发Kinect DK的笔记,内含利用Open3D对Azure Kinect DK相机读取的信息进行点云实时可视化及图像点云按键交互存储。   对官方的代码做些解读: main函数   代码首先是对argparse 模块的设置,argparse 模块可

    2024年02月05日
    浏览(41)
  • python如何实现点云可视化交互——Open3D实例教程(获取所选点的信息)保姆级教学

    Open3D是目前python中可用的用于 3D 数据处理的现代库,可以对点云、网格等三维数据进行读取、采样、配准、可视化等操作。其中对点云等三维模型进行可视化的功能在Python中显得非常方便。 在通过对官方文档的研究之后作者发现在Open3D的多种可视化函数中出现了返回所选点

    2024年02月02日
    浏览(64)
  • Open3D点云处理

    Open3D is an open-source library that supports rapid development of software that deals with 3D data. The Open3D frontend exposes a set of carefully selected data structures and algorithms in both C++ and Python. The backend is highly optimized and is set up for parallelization. Open3D是一个支持3D数据处理软件快速开发的开源库,在前端提供

    2023年04月17日
    浏览(58)
  • open3d点云平移

    功能简介 open3d中点云的平移函数为:pcd.translate((tx, ty, tz), relative=True)。当relative为True时,(tx, ty, tz)表示点云平移的相对尺度,也就是平移了多少距离。当relative为False时,(tx, ty, tz)表示点云中心(质心)平移到的指定位置。质心可以坐标可以通过pcd.get_center()得到。 代码

    2024年01月22日
    浏览(81)
  • Open3d点云对象详解

    PointCloud 是open3d中用于点云处理的类,封装了包括几何变换、数据滤波、聚类分割等一系列实用算法。如无特别说明,本例中所有例程均基于斯坦福兔子的点云模型,下载地址:斯坦福标准模型 读取和清除点云 一般点云数据的读取方法属于 open3d.io 的内容,但点云类也提供了

    2023年04月19日
    浏览(87)
  • 基于Open3D的点云处理17-Open3d的C++版本

    http://www.open3d.org/docs/latest/cpp_api.html http://www.open3d.org/docs/latest/getting_started.html#c http://www.open3d.org/docs/release/cpp_project.html#cplusplus-example-project https://github.com/isl-org/open3d-cmake-find-package https://github.com/isl-org/open3d-cmake-external-project https://github.com/isl-org/Open3D/releases Note: -DBUILD_SHARED_LIBS

    2024年02月09日
    浏览(63)
  • open3d操作.ply文件(点云)

    读取.ply文件

    2024年02月14日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包