Realsense相机疑难问题

2023-05-16

目录

  • 不同传感器模块之间的外参问题
    • 问题描述
    • 解决办法
      • 打开windows命令行,将目录导航到C:\Program Files (x86)\Intel RealSense SDK 2.0\tools目录下
      • 确保此时电脑连入了Realsense设备
      • 输入rs-enumerate-devices -c命令
      • 得到相机的相关参数,可以查询自己需要的信息
    • 解决问题
  • 深度图与RGB图在配准之后的内参问题
    • 问题描述
    • 结论
  • 使用API获取一个RGB像素点的三维坐标(在RGB三维坐标系下)
    • 明确的几个要点:
    • 介绍两种方法
      • 完成RGB和深度图配准之后获取三维坐标
      • 跳过配准直接根据API获取三维坐标
    • 两种方法的进一步验证:
      • 配准之后求取深度值
      • 未配准根据API求取深度值
    • 使用过程中又遇到一个问题
      • D455
      • L515
    • 进一步分析:
      • D455
        • depth_min = 0.1 depth_max = 2.0
        • depth_min = 0.1 depth_max = 1.0
        • depth_min = 0.1 depth_max = 1.5
        • depth_min = 1.0 depth_max = 1.5
      • 分析
      • L515
        • depth_min = 0.11 depth_max = 2.0
        • depth_min = 0.1 depth_max = 1.0
        • depth_min = 0.1 depth_max = 1.5
        • depth_min = 0.5 depth_max = 1.5
        • depth_min = 0.7 depth_max = 1.5
    • 结论
  • 将.bag转换为一帧帧的图片过程中,帧丢失的问题
    • 方法一:使用Realsense自带的工具re-convert 进行帧转换就不会有帧丢失问题
    • 解决步骤:
      • 打开windows命令行,将目录导航到C:\Program Files (x86)\Intel RealSense SDK 2.0\tools目录下
      • 输入如下:
      • -i 后面跟输入的.bag文件路径,-p后面跟输出的.png图片路径
      • 回车
    • 方法二:使用python脚本转换

不同传感器模块之间的外参问题

问题描述

在使用Realsense API 的过程中,遇到一个相机不同传感器的之间的转换问题。一开始通过get_extrinsics_to 方法得到的了相机深度模块转换到彩色模块的问题,代码如下:

import pyrealsense2 as rs
import numpy as np
import cv2

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)
stream_profile_color = profile.get_stream(rs.stream.color)
stream_profile_depth = profile.get_stream(rs.stream.depth)
color_intrs = stream_profile_color.as_video_stream_profile().get_intrinsics()
depth_intrs = stream_profile_depth.as_video_stream_profile().get_intrinsics()
extrinsics = stream_profile_depth.get_extrinsics_to(stream_profile_color)
print("extrins:\n", extrinsics)

之后在API文档中看到rs.rs2_transform_point_to_point() 的函数,便想验证这个函数的内在转换是不是通过坐标乘以外参变换矩阵 所得,所以根据上面得到的外参矩阵 来进行验证:

import pyrealsense2 as rs
import numpy as np
import cv2

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)
stream_profile_color = profile.get_stream(rs.stream.color)
stream_profile_depth = profile.get_stream(rs.stream.depth)
color_intrs = stream_profile_color.as_video_stream_profile().get_intrinsics()
depth_intrs = stream_profile_depth.as_video_stream_profile().get_intrinsics()
extrinsics = stream_profile_depth.get_extrinsics_to(stream_profile_color)
print("extrins:\n", extrinsics)
align_to = rs.stream.color
align = rs.align(align_to)
x = 766
y = 486
frames = pipeline.wait_for_frames()
aligned_frames = align.process(frames)
aligned_depth_frames = aligned_frames.get_depth_frame()
dis = aligned_depth_frames.get_distance(x, y)
camera_coordinate = rs.rs2_deproject_pixel_to_point(depth_intrs, [x, y], dis)
camera_coordinate1 = rs.rs2_deproject_pixel_to_point(color_intrs, [x, y], dis)
# generate the transform matrix on my own
rotation_matrix = np.array(extrinsics.rotation).reshape(3, 3)
translation_matrix = np.array(extrinsics.translation).reshape(3, 1)
tf_matrix = np.vstack((np.hstack((rotation_matrix, translation_matrix)), [0, 0, 0, 1]))
print("tf_matrix:", tf_matrix)
# Convert to homogeneous coordinates
camera_coordinate_my_own = np.dot(tf_matrix, np.append(camera_coordinate, [1]).reshape(4, 1))
camera_coordinate_api = rs.rs2_transform_point_to_point(extrinsics, camera_coordinate)
# the coordinate unit is meter
print("my own:\n", camera_coordinate_my_own)
print("api:\n", camera_coordinate_api)
print("distance:", dis)
print("depth intrins:", camera_coordinate)
print("color intrins:", camera_coordinate1)
pipeline.stop()

结果如下:

my own:
 [[0.40040609]
 [0.27704751]
 [0.50412326]
 [1.        ]]
api:
 [0.3927474021911621, 0.3071334660053253, 0.49359646439552307]

可以看出两者得出的结果是不一样的,但是原理上我感觉没有错误,所以就针对这个问题进行了研究。

解决办法

打开windows命令行,将目录导航到C:\Program Files (x86)\Intel RealSense SDK 2.0\tools目录下

确保此时电脑连入了Realsense设备

输入rs-enumerate-devices -c命令

得到相机的相关参数,可以查询自己需要的信息

在这里插入图片描述

tf_matrix: [[ 9.99965429e-01  7.62356725e-03  3.31247575e-03  3.04106681e-04]
 [-7.54260505e-03  9.99688208e-01 -2.38026995e-02  1.42350988e-02]
 [-3.49290436e-03  2.37768926e-02  9.99711215e-01 -6.95471419e-03]
 [ 0.00000000e+00  0.00000000e+00  0.00000000e+00  1.00000000e+00]]

可以看到两者是转置的关系,所以问题就出现在这里,自己在生成转换矩阵的时候忘记转置,所以得到了不同的结果。

解决问题

需要修改的代码如下:

rotation_matrix = np.array(extrinsics.rotation).reshape(3, 3).T
translation_matrix = np.array(extrinsics.translation).reshape(3, 1)
tf_matrix = np.vstack((np.hstack((rotation_matrix, translation_matrix)), [0, 0, 0, 1]))
print("tf_matrix:", tf_matrix)

运行得到的结果如下:

my own:
 [[0.39313513]
 [0.30742287]
 [0.49409104]
 [1.        ]]
api:
 [0.393135130405426, 0.3074228763580322, 0.49409106373786926]

结果相同,问题解决,同时也说明rs.rs2_transform_point_to_point() 函数的内在机理就是通过外参矩阵 去完成转换的!

深度图与RGB图在配准之后的内参问题

问题描述

在使用rs.rs2_deproject_pixel_to_point() 的函数去将像素点转化为三维坐标的过程中,对于这个函数的内部参数的使用问题产生疑惑。rs2_deproject_pixel_to_point(intrin: pyrealsense2.pyrealsense2.intrinsics, pixel: List[float[2]], depth: float) 中的intrinsics 参数是使用RGB图像的内参还是深度图像的内参?故进行以下实验:

import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)
stream_profile_color = profile.get_stream(rs.stream.color)
stream_profile_depth = profile.get_stream(rs.stream.depth)
color_intrs = stream_profile_color.as_video_stream_profile().get_intrinsics()
depth_intrs = stream_profile_depth.as_video_stream_profile().get_intrinsics()
print("color_intrs:", color_intrs)
print("depth_intrs:", depth_intrs)
align_to = rs.stream.color
align = rs.align(align_to)
x = 668
y = 508
frames = pipeline.wait_for_frames()
aligned_frames = align.process(frames)
aligned_depth_frames = aligned_frames.get_depth_frame()
aligned_color_frames = aligned_frames.get_color_frame()
aligned_color_intrs = aligned_color_frames.profile.as_video_stream_profile().intrinsics
aligned_depth_intrs = aligned_depth_frames.profile.as_video_stream_profile().intrinsics
print("aligned_color_intrs:", aligned_color_intrs)
print("aligned_depth_intrs:", aligned_depth_intrs)
dis = aligned_depth_frames.get_distance(x, y)
camera_coordinate = rs.rs2_deproject_pixel_to_point(aligned_depth_intrs, [x, y], dis)
camera_coordinate1 = rs.rs2_deproject_pixel_to_point(color_intrs, [x, y], dis)
camera_coordinate2 = rs.rs2_deproject_pixel_to_point(aligned_color_intrs, [x, y], dis)
print("aligned_depth_coordinate:", camera_coordinate)
print("color_coordinate::", camera_coordinate1)
print("aligned_color_coordinate::", camera_coordinate2)
pipeline.stop()

运行结果如下:

color_intrs: [ 1280x720  p[633.544 366.202]  f[631.882 631.281]  Inverse Brown Conrady [-0.0545333 0.0667639 -0.0007223 0.00052989 -0.0208721] ]
depth_intrs: [ 640x480  p[316.003 243.892]  f[388.318 388.318]  Brown Conrady [0 0 0 0 0] ]
aligned_color_intrs: [ 1280x720  p[633.544 366.202]  f[631.882 631.281]  Inverse Brown Conrady [-0.0545333 0.0667639 -0.0007223 0.00052989 -0.0208721] ]
aligned_depth_intrs: [ 1280x720  p[633.544 366.202]  f[631.882 631.281]  Inverse Brown Conrady [-0.0545333 0.0667639 -0.0007223 0.00052989 -0.0208721] ]
aligned_depth_coordinate: [0.0614432729780674, 0.2532774806022644, 1.124000072479248]
color_coordinate:: [0.0614432729780674, 0.2532774806022644, 1.124000072479248]
aligned_color_coordinate:: [0.0614432729780674, 0.2532774806022644, 1.124000072479248]

结论

从以上实验可以看出,在RGB和深度图像进行配准之后,深度图的内参会调整为与RGB图的内参相同的参数,所以无论在rs.rs2_deproject_pixel_to_point() 函数中使用深度图内参还是RGB内参,结果都是一样的。

使用API获取一个RGB像素点的三维坐标(在RGB三维坐标系下)

参考官方文档

明确的几个要点:

  • realsense不同的传感器模块之间对应的坐标系是不同的。也就是说,RGB传感器和深度传感器的坐标系不同。
  • 传感器坐标系的单位是以 为单位的
  • 传感器不同坐标系的转化需要用到外参
  • 深度图和RGB图的像素点在没有进行配准的情况下是没有对应关系的(深度图和Infrared图是重合的),也就是说,从RGB图中获取的一个像素点,深度图中相同坐标的像素点,两者对应真实物理环境中的点是完全不一样的。

介绍两种方法

完成RGB和深度图配准之后获取三维坐标

import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)
stream_profile_color = profile.get_stream(rs.stream.color)
stream_profile_depth = profile.get_stream(rs.stream.depth)
color_intrs = stream_profile_color.as_video_stream_profile().get_intrinsics()
depth_intrs = stream_profile_depth.as_video_stream_profile().get_intrinsics()
print("color_intrs:", color_intrs)
print("depth_intrs:", depth_intrs)
align_to = rs.stream.color
align = rs.align(align_to)
x = 668
y = 508
frames = pipeline.wait_for_frames()
aligned_frames = align.process(frames)
aligned_depth_frames = aligned_frames.get_depth_frame()
aligned_color_frames = aligned_frames.get_color_frame()
aligned_color_intrs = aligned_color_frames.profile.as_video_stream_profile().intrinsics
aligned_depth_intrs = aligned_depth_frames.profile.as_video_stream_profile().intrinsics
print("aligned_color_intrs:", aligned_color_intrs)
print("aligned_depth_intrs:", aligned_depth_intrs)
dis = aligned_depth_frames.get_distance(x, y)
camera_coordinate = rs.rs2_deproject_pixel_to_point(aligned_depth_intrs, [x, y], dis)
camera_coordinate1 = rs.rs2_deproject_pixel_to_point(color_intrs, [x, y], dis)
camera_coordinate2 = rs.rs2_deproject_pixel_to_point(aligned_color_intrs, [x, y], dis)
print("aligned_depth_coordinate:", camera_coordinate)
print("color_coordinate::", camera_coordinate1)
print("aligned_color_coordinate::", camera_coordinate2)
pipeline.stop()

运行结果如下:

color_intrs: [ 1280x720  p[633.544 366.202]  f[631.71 631.109]  Inverse Brown Conrady [-0.0545333 0.0667639 -0.0007223 0.00052989 -0.0208721] ]
depth_intrs: [ 640x480  p[316.003 243.892]  f[388.318 388.318]  Brown Conrady [0 0 0 0 0] ]
aligned_color_intrs: [ 1280x720  p[633.544 366.202]  f[631.71 631.109]  Inverse Brown Conrady [-0.0545333 0.0667639 -0.0007223 0.00052989 -0.0208721] ]
aligned_depth_intrs: [ 1280x720  p[633.544 366.202]  f[631.71 631.109]  Inverse Brown Conrady [-0.0545333 0.0667639 -0.0007223 0.00052989 -0.0208721] ]
distance: 1.124000072479248
aligned_depth_coordinate: [0.06146007776260376, 0.2533468008041382, 1.124000072479248]
color_coordinate:: [0.06146007776260376, 0.2533468008041382, 1.124000072479248]
aligned_color_coordinate:: [0.06146007776260376, 0.2533468008041382, 1.124000072479248]

跳过配准直接根据API获取三维坐标

import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)
depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()
depth_min = 0.11  # meter
depth_max = 2.0  # meter

depth_intrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_intrinsics()
color_intrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_intrinsics()

depth_to_color_extrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.color))
color_to_depth_extrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.depth))
frames = pipeline.wait_for_frames()
depth_frame = frames.get_depth_frame()
x = 668
y = 508
color_point = [x, y]
depth_point = rs.rs2_project_color_pixel_to_depth_pixel(
    depth_frame.get_data(), depth_scale,
    depth_min, depth_max,
    depth_intrin, color_intrin, depth_to_color_extrin, color_to_depth_extrin, color_point)
print("depth_point:", depth_point)
distance = depth_frame.get_distance(int(depth_point[0]), int(depth_point[1]))
print("distance:", distance)
camera_coordinate_depth = rs.rs2_deproject_pixel_to_point(depth_intrin, depth_point, distance)
print("coordinate in depth:", camera_coordinate_depth)
camera_coordinate_color = rs.rs2_transform_point_to_point(depth_to_color_extrin, camera_coordinate_depth)
print("coordinate in color:", camera_coordinate_color)
pipeline.stop()

运行结果如下:

depth_point: [357.95562744140625, 329.447998046875]
distance: 1.124000072479248
coordinate in depth: [0.12143445014953613, 0.24764372408390045, 1.124000072479248]
coordinate in color: [0.061697326600551605, 0.25312334299087524, 1.1230082511901855]

比较两者的运行结果:

distance: 1.124000072479248
aligned_depth_coordinate: [0.06146007776260376, 0.2533468008041382, 1.124000072479248]
color_coordinate:: [0.06146007776260376, 0.2533468008041382, 1.124000072479248]
aligned_color_coordinate:: [0.06146007776260376, 0.2533468008041382, 1.124000072479248]
depth_point: [357.95562744140625, 329.447998046875]
distance: 1.124000072479248
coordinate in depth: [0.12143445014953613, 0.24764372408390045, 1.124000072479248]
coordinate in color: [0.061697326600551605, 0.25312334299087524, 1.1230082511901855]

最终得到的坐标是在RGB坐标系下的三维坐标(单位),两者十分接近,说明两种方法都是正确的。但是第二种方法所需计算消耗要比第一种小,更高效。

两种方法的进一步验证:

import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)
depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()
depth_min = 0.11  # meter
depth_max = 2.0  # meter

depth_intrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_intrinsics()
color_intrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_intrinsics()

depth_to_color_extrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.color))
color_to_depth_extrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.depth))
# align the color and depth streams
align_to = rs.stream.color
align = rs.align(align_to)

frames = pipeline.wait_for_frames()
# get the depth frame after alignment
aligned_frames = align.process(frames)
aligned_depth_frames = aligned_frames.get_depth_frame()
# get the original depth frame
depth_frame = frames.get_depth_frame()
x = 668
y = 508
dis = aligned_depth_frames.get_distance(x, y)
print("distance after alignment:", dis)
color_point = [x, y]
depth_point = rs.rs2_project_color_pixel_to_depth_pixel(
    depth_frame.get_data(), depth_scale,
    depth_min, depth_max,
    depth_intrin, color_intrin, depth_to_color_extrin, color_to_depth_extrin, color_point)
print("depth_point:", depth_point)
distance = depth_frame.get_distance(int(depth_point[0]), int(depth_point[1]))
print("distance without alignment:", distance)
camera_coordinate_depth = rs.rs2_deproject_pixel_to_point(depth_intrin, depth_point, distance)
print("coordinate in depth:", camera_coordinate_depth)
camera_coordinate_color = rs.rs2_transform_point_to_point(depth_to_color_extrin, camera_coordinate_depth)
print("coordinate in color:", camera_coordinate_color)
pipeline.stop()

结果如下:

distance after alignment: 1.127000093460083
depth_point: [357.96136474609375, 329.471923828125]
distance without alignment: 1.127000093460083
coordinate in depth: [0.12177521735429764, 0.24837413430213928, 1.127000093460083]
coordinate in color: [0.0620361790060997, 0.25386834144592285, 1.1260048151016235]

上述代码用两种方法求取了对应同一个RGB像素点 的距离值,可以看到两种方法求取的距离值是完全相同的。由此我们也可以看到两种方法的正确性。

配准之后求取深度值

配准之后获取的深度帧可以直接使用RGB像素坐标来获取对应RGB像素点的深度。因为深度帧RGB帧完成了配准,像素点之间是一一对应的。(相当于每个像素点完成了未配准求取深度值的操作)

未配准根据API求取深度值

未配准的深度帧RGB帧的像素坐标不是一一对应的。想要求取一个RGB像素点的深度值,就得先把这个RGB像素点转换为深度帧下的像素坐标值,在求取距离。

使用过程中又遇到一个问题

我当时以为在验证完两种方法获取XYZ三维坐标之后,就可以根据情况随意选用了。结果本来在D455上计算正确的坐标在L515上有很大的偏差。之后我通过rs.rs2_project_point_to_pixel() (这个函数是将3维坐标转化为像素坐标的API)来验证两种方法的正确性。验证如下:

D455

import pyrealsense2 as rs
import numpy as np
import transforms3d as tfs

# 初始化配置
pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)

# 获得相机内参和depth_scale
depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()
print("depth_scale:",depth_scale)
depth_min = 0.11  # meter
depth_max = 2.0  # meter
depth_intrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_intrinsics()
color_intrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_intrinsics()
# 获得相机深度模块和彩色模块的外参转换
depth_to_color_extrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.color))
color_to_depth_extrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.depth))

coordinate_collect = []
frames = pipeline.wait_for_frames()
# 使用API获得三维坐标
# get the original depth frame
depth_frame = frames.get_depth_frame()
# 指定像素坐标
x = 850
y = 450
color_point = [x, y]
depth_point = rs.rs2_project_color_pixel_to_depth_pixel(
    depth_frame.get_data(), depth_scale,
    depth_min, depth_max,
    depth_intrin, color_intrin, depth_to_color_extrin, color_to_depth_extrin, color_point)
print("depth pixel:", depth_point)
distance = depth_frame.get_distance(int(depth_point[0]), int(depth_point[1]))
# 获得点在深度模块三维坐标系下的坐标
camera_coordinate_depth = rs.rs2_deproject_pixel_to_point(depth_intrin, depth_point, distance)
depth_point_after_calculation = rs.rs2_project_point_to_pixel(depth_intrin, camera_coordinate_depth)
print("经过计算后的深度坐标为:", depth_point_after_calculation)
# print("coordinate relative to depth sensor", camera_coordinate_depth)
camera_coordinate_color = rs.rs2_transform_point_to_point(depth_to_color_extrin, camera_coordinate_depth)
print("coordinate relative to color sensor using rs.rs2_project_color_pixel_to_depth_pixel():\n",
      camera_coordinate_color)
color_pixel = rs.rs2_project_point_to_pixel(color_intrin, camera_coordinate_color)
print("原始彩色像素坐标为:", [x, y])
print("经过计算后的彩色坐标为:", color_pixel)

align_to = rs.stream.color
align = rs.align(align_to)
aligned_frames = align.process(frames)
aligned_depth_frames = aligned_frames.get_depth_frame()
dis = aligned_depth_frames.get_distance(x, y)
camera_coordinate_align = rs.rs2_deproject_pixel_to_point(color_intrin, [x, y], dis)
print("coordinate relative to the color sensor after alignment:\n", camera_coordinate_align)
color_pixel1 = rs.rs2_project_point_to_pixel(color_intrin, camera_coordinate_align)
print("经过配准后的彩色像素坐标:", color_pixel1)
pipeline.stop()

运行结果如下:

depth_scale: 0.0010000000474974513
depth pixel: [467.0, 293.9060974121094]
经过计算后的深度坐标为: [467.0, 293.9060974121094]
coordinate relative to color sensor using rs.rs2_project_color_pixel_to_depth_pixel():
 [0.4696837365627289, 0.18194164335727692, 1.3616513013839722]
原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [850.2799072265625, 449.99932861328125]
coordinate relative to the color sensor after alignment:
 [0.4623022973537445, 0.17931503057479858, 1.3420000076293945]
经过配准后的彩色像素坐标: [850.0, 450.0]

注意观察以上结果:
我指定的彩色图片中的像素坐标为:

原始彩色像素坐标为: [850, 450]

通过未配准的方法求取3维坐标进行反算像素坐标的值如下:

经过计算后的彩色坐标为: [850.2799072265625, 449.99932861328125]

通过配准的方法求取3维坐标进行反算像素坐标的值如下:

经过配准后的彩色像素坐标: [850.0, 450.0]

可以看出两种方法求得的像素坐标与指定坐标是近似相等的,也就是说两种方法求取的三维坐标也是近似相等的。但是在L515上,使用未配准的方法去求取三维坐标出现了很大的偏差。

L515

import pyrealsense2 as rs
import numpy as np
import transforms3d as tfs

# 初始化配置
pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
profile = pipeline.start(config)

# 获得相机内参和depth_scale
depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()
print("depth_scale:",depth_scale)
depth_min = 0.11  # meter
depth_max = 2.0  # meter
depth_intrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_intrinsics()
color_intrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_intrinsics()
# 获得相机深度模块和彩色模块的外参转换
depth_to_color_extrin = profile.get_stream(rs.stream.depth).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.color))
color_to_depth_extrin = profile.get_stream(rs.stream.color).as_video_stream_profile().get_extrinsics_to(
    profile.get_stream(rs.stream.depth))

coordinate_collect = []
frames = pipeline.wait_for_frames()
# 使用API获得三维坐标
# get the original depth frame
depth_frame = frames.get_depth_frame()
# 指定像素坐标
x = 850
y = 450
color_point = [x, y]
depth_point = rs.rs2_project_color_pixel_to_depth_pixel(
    depth_frame.get_data(), depth_scale,
    depth_min, depth_max,
    depth_intrin, color_intrin, depth_to_color_extrin, color_to_depth_extrin, color_point)
print("depth pixel:", depth_point)
distance = depth_frame.get_distance(int(depth_point[0]), int(depth_point[1]))
# 获得点在深度模块三维坐标系下的坐标
camera_coordinate_depth = rs.rs2_deproject_pixel_to_point(depth_intrin, depth_point, distance)
depth_point_after_calculation = rs.rs2_project_point_to_pixel(depth_intrin, camera_coordinate_depth)
print("经过计算后的深度坐标为:", depth_point_after_calculation)
# print("coordinate relative to depth sensor", camera_coordinate_depth)
camera_coordinate_color = rs.rs2_transform_point_to_point(depth_to_color_extrin, camera_coordinate_depth)
print("coordinate relative to color sensor using rs.rs2_project_color_pixel_to_depth_pixel():\n",
      camera_coordinate_color)
color_pixel = rs.rs2_project_point_to_pixel(color_intrin, camera_coordinate_color)
print("原始彩色像素坐标为:", [x, y])
print("经过计算后的彩色坐标为:", color_pixel)

align_to = rs.stream.color
align = rs.align(align_to)
aligned_frames = align.process(frames)
aligned_depth_frames = aligned_frames.get_depth_frame()
dis = aligned_depth_frames.get_distance(x, y)
camera_coordinate_align = rs.rs2_deproject_pixel_to_point(color_intrin, [x, y], dis)
print("coordinate relative to the color sensor after alignment:\n", camera_coordinate_align)
color_pixel1 = rs.rs2_project_point_to_pixel(color_intrin, camera_coordinate_align)
print("经过配准后的彩色像素坐标:", color_pixel1)
pipeline.stop()

运行结果如下:

depth_scale: 0.0002500000118743628
depth pixel: [398.83453369140625, 233.0365753173828]
经过计算后的深度坐标为: [398.83453369140625, 233.0365753173828]
coordinate relative to color sensor using rs.rs2_project_color_pixel_to_depth_pixel():
 [0.10922971367835999, 0.002470635809004307, 0.5473655462265015]
原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [838.7570190429688, 357.4697265625]
coordinate relative to the color sensor after alignment:
 [0.16353169083595276, 0.0825377032160759, 0.7725000381469727]
经过配准后的彩色像素坐标: [850.0, 450.0]

注意观察以上结果:
我指定的彩色图片中的像素坐标为:

原始彩色像素坐标为: [850, 450]

通过未配准的方法求取3维坐标进行反算像素坐标的值如下:

经过计算后的彩色坐标为: [838.7570190429688, 357.4697265625]

通过配准的方法求取3维坐标进行反算像素坐标的值如下:

经过配准后的彩色像素坐标: [850.0, 450.0]

可以看到通过未配准的方法求取的像素坐标与指定坐标相差较大,导致这种方法所求得的三维坐标也与正确点偏差较大。但是配准之后的方法依旧是准确的。

进一步分析:

两种方法中所使用的原理是一样的,但是得出的结果却千差万别。现在使用D455做一些实验。修改rs.rs2_project_color_pixel_to_depth_pixel() 中的一些参数,发现在这些参数中,可以修改的只有depth_mindepth_max两个参数,修改两个参数进行实验。

D455

depth_min = 0.1 depth_max = 2.0

depth_min = 0.1  # meter
depth_max = 2.0  # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [850.614990234375, 449.99853515625]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 0.1 depth_max = 1.0

depth_min = 0.1  # meter
depth_max = 1.0  # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [861.7542114257812, 449.9549255371094]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 0.1 depth_max = 1.5

depth_min = 0.1  # meter
depth_max = 1.5  # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [850.7327880859375, 449.99749755859375]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 1.0 depth_max = 1.5

depth_min = 1.0  # meter
depth_max = 1.5  # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [849.806396484375, 450.000732421875]
经过配准后的彩色像素坐标: [850.0, 450.0]

分析

经过上面参数的实验,可以得出:使用rs.rs2_project_color_pixel_to_depth_pixel() 的参数要符合测量的深度的范围,如果所选范围不当,那么所求出的depth_pixel就是不准确的。

L515

depth_min = 0.11 depth_max = 2.0

depth_min = 0.11  # meter
depth_max = 2.0 # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [838.7406005859375, 357.3323669433594]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 0.1 depth_max = 1.0

depth_min = 0.1  # meter
depth_max = 1.0 # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [837.4801635742188, 346.76116943359375]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 0.1 depth_max = 1.5

depth_min = 0.1  # meter
depth_max = 1.5 # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [837.4309692382812, 346.347900390625]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 0.5 depth_max = 1.5

depth_min = 0.5  # meter
depth_max = 1.5 # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [848.8622436523438, 440.7563781738281]
经过配准后的彩色像素坐标: [850.0, 450.0]

depth_min = 0.7 depth_max = 1.5

depth_min = 0.7  # meter
depth_max = 1.5 # meter

其他代码如上,仅仅修改这两个参数的值,得以下结果:

原始彩色像素坐标为: [850, 450]
经过计算后的彩色坐标为: [849.7996215820312, 448.3738098144531]
经过配准后的彩色像素坐标: [850.0, 450.0]

综上,可以看出depth_maxdepth_min对函数rs.rs2_project_color_pixel_to_depth_pixel() 的影响很大,选取准确的区间能够让L515也获得准确的值。

结论

通过对两种方法原理的验证,可以看到未配准的方法所进行的计算量是远小于配准的方法的,在只需要知道一个RGB像素点的深度的使用场景中,使用未配准的方法效率更高。但是在使用函数rs.rs2_project_color_pixel_to_depth_pixel()的过程中,depth_min和depth_max的参数选取至关重要。

将.bag转换为一帧帧的图片过程中,帧丢失的问题

方法一:使用Realsense自带的工具re-convert 进行帧转换就不会有帧丢失问题

解决步骤:

打开windows命令行,将目录导航到C:\Program Files (x86)\Intel RealSense SDK 2.0\tools目录下

输入如下:

C:\Program Files (x86)\Intel RealSense SDK 2.0\tools>rs-convert -i D:\Realsense\video_capture\20211214_103936.bag -p D:\Realsense\out1\

-i 后面跟输入的.bag文件路径,-p后面跟输出的.png图片路径

回车

100%
PNG converter
        388 Color frame(s) processed

完成图片转换

方法二:使用python脚本转换

import pyrealsense2 as rs
import numpy as np
import cv2
import os.path


def GetAllFrames(bag_file):
    pipeline = rs.pipeline()
    config = rs.config()
    rs.config.enable_device_from_file(config, bag_file, repeat_playback=False)
    # config.enable_stream(rs.stream.depth, 1280, 720, rs.format.z16, 30)
    config.enable_stream(rs.stream.color, 1920, 1080, rs.format.rgb8, 30)

    profile = pipeline.start(config)
    playback = profile.get_device().as_playback()  # get playback device
    playback.set_real_time(False)  # disable real-time playback

    cur_frame_number = -1
    filepath = "D:\\Realsense\\out2"
    i = 0
    while True:
        is_frame, frames = pipeline.try_wait_for_frames()
        if not is_frame:
            break
        color_frame = frames.get_color_frame()
        # depth_frame = frames.get_depth_frame()
        color_image = np.asanyarray(color_frame.get_data())
        color_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2RGB)

        print("color number: %d" % color_frame.get_frame_number())
        # print("depth number: %d" % depth_frame.get_frame_number())
        if cur_frame_number < color_frame.get_frame_number():
        	# print("image saved: %d .jpg" % i) 
        	# frame_number和i不吻合的原因是frame_number在运行过程中跳跃
        	# 不是连续的
            cur_frame_number = color_frame.get_frame_number()
            i += 1
            name = str(i) + ".jpg"
            cv2.imwrite(os.path.join(filepath, name), color_image)
        else:
            break
    print("current_number: %d" % cur_frame_number)
    pipeline.stop()


if __name__ == "__main__":
    file_names = ['D:\\Realsense\\20211214_124552.bag']
    GetAllFrames(file_names[0])
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Realsense相机疑难问题 的相关文章

  • 2020年三非上岸北邮计算机院考研经验贴(励志)

    64 TOC 两次北邮考研经验 前言 xff1a 这是我两次考北邮的日常生活的点点滴滴 xff0c 我不是什么名校出身 xff0c 也不是什么学霸 xff0c 但是有梦想终归会成功的 xff0c 希望我的经历能够带给你们一些鼓励 xff0c
  • 23条养生小常识,赶紧看看吧,千万不要错过!

    一 情绪不畅容易生病 天气多变时 xff0c 人们情绪很容易受气候的影响而抑郁 xff0c 情绪变化也影响了食欲 xff0c 从而引发胃病 在胃病的预防上要重视精神与饮食的调摄 xff0c 一个人经常情绪不畅 xff0c 很容易被疾病侵袭
  • Windows上安装GPU版本TensorFlow的详细安装步骤

    1 检查并安装VS环境 安装GPU版本的TensorFlow xff0c 首先需要检查VS环境 xff0c 如果没有需要安装 xff0c 但是VS全部安装会占内存 xff0c 因此可以去Download Visual C 43 43 Red
  • ISE14.7逻辑综合与实现工作过程

    1 1 ISE14 7逻辑综合与实现工作过程 1 1 1 本节目录 1 本节目录 2 本节引言 3 FPGA简介 4 ISE14 7逻辑综合与实现工作过程 5 结束语 1 1 2 本节引言 不积跬步 无以至千里 不积小流 无以成江海 就是说
  • (4)FPGA开发工具介绍(第1天)

    4 FPGA开发工具介绍 第1天 1 文章目录 1 文章目录 2 FPGA初级课程介绍 3 FPGA初级课程架构 4 FPGA开发工具介绍 第1天 5 技术交流 6 参考资料 2 FPGA初级课程介绍 1 FPGA初级就业课程共100篇文章
  • (02)Shell脚本【可执行程序】

    02 Shell脚本 可执行程序 1 目录 1 1 Shell脚本简介 1 2 nbsp Shell脚本文件 1 3 nbsp Shell脚本作用 1 4 nbsp Shell运行环境 1 5 nbsp Shell脚本 可执行程序 1 6
  • AD20知识补充及四层板了解

    之前自己动手画过一个stm32c8t6最小系统的板子 xff0c 但由于当时学的比较仓促对AD的很多东西不了解 xff0c 如今想了解四层板子 xff0c 就从b站上把AD20从新建项目工程到最后画完板子整个完整的流程又重新学习了一遍 xf
  • 常用服务器和存储设备管理口默认IP用户名密码汇总(持续更新)

    一 服务器设备默认管理 1 宝德4卡服务器 默认用户名 xff1a ADMIN 密码 xff1a 11111111 2 超微服务器 默认用户名 xff1a ADMIN 密码 xff1a admin000 默认用户名 xff1a ADMIN
  • RS232,RS485原理与应用

    Uart存在的问题 1 没有一个统一连接器的标准 xff0c 且只规定了数据传输的顺序 xff08 只规定了两根线 xff09 2 只规定了高电平为1 xff0c 低电平为0 xff08 例 xff1a 51是5v xff0c stm32为
  • STM32的面试题

    一 STM32F1和F4的区别 1 内核不同 xff1a F1内核为cortex m3 xff0c F4为cortex m4 2 主频不同 xff1a F1主频72MHz xff0c F4168MHz xff08 主频就是CPU内核时钟频率
  • GCC【3】-Win10 + CMake + MinGW+搭建STM32 GCC开源开发环境

    文章目录 前言一 安装git bash二 安装MinGW MinGW w642 1 MinGW是什么 xff1f 2 2 MinGW gcc安装简图2 2 MinGW make安装简图 三 安装Make for Windows3 1 下载安
  • Zookeeper集群无法启动的原因分析

    Zookeeper集群无法启动的原因分析 xff1a centos7上搭建三台zookeeper xff0c 相关文件配置没有问题 问题描述 xff1a span class token punctuation span atguigu 6
  • 美女体验小米无人机4K版:直接解锁新手模式

    其实说实话 xff0c 这小米无人机4K版我们拿到手上已经挺长时间的了 但是奈何全北京禁飞 xff0c 所以一直没有机会飞飞看 xff0c 于是我们就在解禁之后的第一时间 xff0c 跑到了六环外为大家带来试飞 就让我们从开箱开始 xff0
  • intel realsense摄像头标定教程(win10环境)

    intel realsense摄像头标定教程 准备工具 Intel RealSense D400 Series Dynamic Calibration Toolsprint target fixed width pdf或者对应手机app I
  • TCP网络编程例子(C语言实现)

    说明 xff1a 之前在CSDN上找TCP编程时 xff0c 发现有各种版本 不同版本之间写法不一 xff0c 所以自己写了个C语言版本的 xff0c 记录下来 服务端代码 xff1a span class token comment TC
  • D435i相机的标定及VINS-Fusion config文件修改

    引言 当我们想使用D435i相机去跑VINS Fusion时 xff0c 如果不把标定过的相机信息写入config文件中就运行 xff0c 这样运动轨迹会抖动十分严重 xff0c 里程计很容易漂 接下来将介绍如何标定D435i相机 xff0
  • 【C语言】--- while(1)语句内的if(i--)的无限循环

    例子 span class token macro property span class token directive keyword include span span class token string lt stdio h gt
  • uC/OS-III移植后发现程序停在空闲任务出不来

    今天移植了一下UCOS III的源码到STM32F103RCT6的板子上 xff0c 然后发现在所有任务都初始化完成后 xff0c 使用OSTimeDlyHMSM函数进行延时后就再也跳不出空闲任务了 xff0c 当时还以为是在移植程序的时候
  • 对uC/OS-III时钟节拍运转机制的一点理解

    目录 如何产生时基信号系统时钟中断管理时基任务时基列表更新写在最后 我在初学uC OS III的时候 xff0c 时基产生后到底是如何去驱动操作系统运转的 xff0c 对于这个问题一直有很多疑问 xff0c 最后读了手册并且仔细推敲源码后终
  • Altium Designer 常见的问题和解决办法,常更!

    本人的Altium Designer 版本是18 1 8 问题1 xff1a AD左上角有一个坐标显示框 xff0c 今天按键盘不小心弄没了 xff0c 然后弄出来后发现又会随着鼠标移动跟着移动 xff0c 不再固定到左上角了 相关解释 x

随机推荐

  • 推荐一款串口调试助手(win10,无广告,功能齐全,操作简单)

    目录 1 安装2 打开软件3 测试功能4 使用脚本功能5 波形显示功能6 升级专业版 作为一个搞嵌入式软件的 xff0c 串口是我们经常需要使用的一个外设资源 xff0c 对应的我们在调试的时候就需要用到一些工具 xff0c 本人也用过目前
  • STM32L1单片机的ADC必须开启HSI才能工作

    之前玩过F1和F4的板子 xff0c 这段时间 xff0c 接个项目需要用低功耗 xff0c 所以就整了STM32L151单片机 xff0c 然后今天在写ADC的时候 xff0c 发现了一个问题 xff0c 就是STM32L151MCU必须
  • 深入理解Linux文件系统与日志文件,快来看!

    目录 一 inode与block1 1 inode和block概述1 2 inode的内容 1 11 3 inode的内容 1 21 4 inode的内容 1 31 5 inode的号码1 6 文件存储小结1 7 inode的大小1 8 i
  • AT 指令集详解

    AT 指令集详解 1 AT的历史与应用1 1 AT的历史1 2 AT的优点1 3 AT命令与ppp协议的关系 2 AT的命令格式3 DCE的状态切换与AT的命令拨号流程3 1 初始化DCE的Modem设备3 2 拨号连接3 3 数据传输及处
  • 幽暗的廊桥,热烈的遗梦:火了25年的中年“杰克苏”童话

    廊桥是一种有遮盖的桥梁 路人经过一段幽暗隐蔽的空间后 xff0c 已身在彼岸 廊桥遗梦 的作者罗伯特 詹姆斯 沃勒于2017年3月10日凌晨 xff0c 在美国德克萨斯州弗雷德里克斯堡的家中去世 xff0c 享年77岁 作者当年如有神助 x
  • STM32L152 实现电源电压采集 基于HAL库实现

    CudeMX中对相关ADC的配置 这里用ADC1的通道17 keil中 自己编写 ADC Getvalue的值的函数 在主函数中调用 在debug模式下 可以在watch窗口中看见相关值 单位为mv
  • 基本共射放大电路的动态分析以及放大电路Q点的稳定

    求解静态 xff1a 直流通路 xff1a VCC 61 Ubeq 43 Ibq Rb gt Ibq gt Ieq 2 动态分析 xff1a 交流通路 xff1a H参数等效 xff1a 在放大过程中 xff1a 动态的Ube秉承动态的Ib
  • FreeRTOS 创建任务

    用已经移植好的工程模板 创建一个简单的任务 xff08 电量LED灯 xff09 1 硬件初始化 需要用到开发板上的LED灯小江LED灯相关的函数初始化 xff0c 在main c中创建一个Init xff08 xff09 函数 xff0c
  • 差分放大电路及动态分析

    由温度引起的零点漂移成为温漂 xff0c 引入负反馈 加入Re电阻 从抑制温度漂移的角度来说 希望Re越大越好 但是会导致放大倍数变小 加一个镜像的电路可以让两边一起浮动 但是两个Re可以共用一个 于是改成下面的 形式 Re电阻现在的作用变
  • FreeRTOS的启动流程

    目前的RTOS中有两种比较流行的启动方式 万事俱备 只欠东风 xff1a 在main函数中将硬件初始化 xff0c rtos系统初始化 xff0c 所有任务的创建都完成 再启动RTOS的调度器 xff08 在刚刚创建的的任务中选择一个优先级
  • FreeRTOS--消息队列

    队列 xff1a 队列又称为消息队列 xff0c 是一种用于任务间通信的数据结构 xff0c 是一种异步通信方式 xff0c 实现接收来自其他任务或者中断的不固定长度的消息 xff0c 任务能够从队列中读取消息 xff0c 当队列中消息是空
  • FreeRTOS--信号量

    信号量的基本概念 xff1a 信号量是一种实现任务间通信的机制 xff0c 可以实现任务之间同步或者临界资源的互斥访问 xff0c 常用于协助一组相互竞争的任务来访问临界资源 信号量是一个非负整数 xff0c 所以获取它的任务都会将整数减一
  • FreeRTOS-互斥信号量

    互斥量又称为互斥信号量 xff0c 本质是一种特殊的二值信号量 xff0c 支持互斥量所有权 xff0c 递归访问 xff0c 以及防止优先翻转的特性 xff0c 用于实现对临界资源的独占式处理 任意时刻互斥量的状态只有两种 xff0c 开
  • FreeRTOS--软件定时器

    定时器 xff1a 从指定的时刻开始 经过一个指定时间 触发一个超时事件 用户自定义定时器的周期和频率 硬件定时器 xff1a 芯片本身提供的定时功能 一般是外部晶振提供给芯片输入时钟 芯片向软件模块一直配置寄存器 接收控制输入 到达设定时
  • FreeRTOS--任务通知

    任务通知 xff1a 每个任务有一个32位的通知值在大多数情况下 xff0c 任务通知可以替代二值信号量 计数信号量 事件组 xff0c 也可以替代长度为 1 的队列 xff08 可以保存一个 32位整数或指针值 xff09 使用任务通知比
  • 模拟实现strchr,strrchr,strstr,strrstr函数

    模拟实现strchr strnchr strstr strrstr函数 strchr 查找字符串s中首次出现字符c的位置 xff0c 返回首次出现c的位置的指针 xff0c 如果s中不存在c则返回NULL include lt stdio
  • FreeRTOS--内存管理

    FreeRTOS操作系统将内核和内存管理分开实现 xff0c 操作系统内核仅规定了必要的内存管理函数原型 不关心内存管理函数是如何实现的 所以在 FreeRTOS 中提供了多种内存分配算法 xff08 分配策略 xff09 xff0c 但是
  • 谈谈C++多态的基本使用和总结

    前言 文章目录 前言1 多态的理解2 构成多态的条件3 虚函数4 虚函数的重写 覆盖 5 多态调用虚函数的规则6 析构函数的重写 基类与派生类析构函数的名字不同 7 设计一个不能被继承的基类8 final关键字和override 关键字9
  • 位运算-2进制表示浮点数

    2进制表示浮点数 题目实现思路代码实现 题目 输入一个浮点数 xff0c 输出浮点数的二进制码 实现思路 1 二进制表示浮点数 xff1a 以0为原点向左移是2的整数次幂 xff0c 向右移是逐级除2 类似 8 4 2 1 0 0 5 0
  • Realsense相机疑难问题

    目录 不同传感器模块之间的外参问题问题描述解决办法打开windows命令行 xff0c 将目录导航到C Program Files x86 Intel RealSense SDK 2 0 tools目录下确保此时电脑连入了Realsense