librealsense的基本使用以及获取D435i的Bayer Raw数据 您所在的位置:网站首页 raw10和raw16 librealsense的基本使用以及获取D435i的Bayer Raw数据

librealsense的基本使用以及获取D435i的Bayer Raw数据

2023-05-12 02:32| 来源: 网络整理| 查看: 265

1.我们要准备什么 2.我们要获取什么 3.我们要怎么获取 4.拿到数据后我们要做什么 5.参考资料

在之前这篇和这篇博客中,我们详细地讨论了RealSense D435i的使用。在这篇博客中利用Kalibr对D435i进行了标定,这篇博客中利用D435i运行了ORB-SLAM3。那么接着上一篇博客的问题,我们想探究一下,能否获得D435i的Raw数据呢?这是一个很有意思的问题,也是本篇博客的主要内容。这里先给出结论:可以,但不是利用V4L2,而是用RealSense提供的API接口。

1.我们要准备什么

从硬件和软件两个层面来说,硬件当然是RealSense D435i了,把它插到电脑上即可。如果对硬件细节感兴趣,可以参考Realsense的官方Datasheet,里面介绍的很详细。而软件层面,主要是RealSense的驱动、SDK以及对应的开发接口(在这篇博客中我们以Python API为例,如果对C++接口感兴趣可以参考这个网页)等,关于环境配置可以参考这篇博客,这里就不再赘述。比如,配置好软硬件环境后,可以用在终端中输入realsense-viewer,启动Viewer可视化数据,如下。

2.我们要获取什么

第二个需要回答的问题是我们要获取什么。那么在这里来说,我们需要获取的就是相机传感器的Bayer Raw原始数据。更进一步来说,Realsense D435i有一个RGB相机、两个红外相机。红外相机是黑白相机,不存在Bayer数据,所以我们要获取的就是RGB相机的Bayer Raw数据。当然红外相机的数据方式是一样的,也很容易可以获得。

另外,我们需要明确的是,相机都支持哪些输出,这个可以通过打开Realsense Viewer选择不同的分辨率、帧率和数据类型进行查看。比如RGB相机,在1280×720分辨率下,支持YUYV、Y16、RAW16等多种格式输出。 其中,RAW16就是我们想要的Bayer Raw数据。又比如对于红外相机,其只支持Y8和Y16两种输出模式,如下。 这里简单列举几个常用的模式:

Infrared

848×480 30fps Y8/Y16 1280×720 30fps Y8/Y16

Infrared Depth

848×480 30fps Z16 1280×720 30fps Z16

RGB

1280×720 30fps RGB8/BGR8/YUYV/Y16/Raw16 848×480 30fps RGB8/BGR8/YUYV/Y16/Raw16 3.我们要怎么获取

在明确了我们要获取的数据为RGB相机的Bayer Raw数据之后,下一步就是思考如何获取它们。从单纯可视化角度而言,直接用Realsense Viewer就可以获取。从开发角度而言,如果有ROS,那么可以利用Realsense的ROS接口获取。最后如果想直接获取,那么就是利用librealsense提供的各种接口来获取。这里我们以Python接口为例。最核心包含四步:

Step1: 检查设备可用性并初始化 Step2: 设置数据流格式 Step3: 启动相机流水线并设置相关参数 Step4: 循环读取帧内容

下面的代码展示了一个非常基础和简单的利用librealsense获取数据的脚本。

import pyrealsense2 as rs import numpy as np import cv2 if __name__ == '__main__': # step1 检查并初始化设备可用性 ctx = rs.context() if len(ctx.devices) == 0: print("No realsense D435i was detected.") exit() device = ctx.devices[0] serial_number = device.get_info(rs.camera_info.serial_number) config = rs.config() config.enable_device(serial_number) # step2 根据配置文件设置数据流 config.enable_stream(rs.stream.color, 1920, 1080, rs.format.bgr8, 30) # step3 启动相机流水线并设置是否自动曝光 pipeline = rs.pipeline() profile = pipeline.start(config) color_sensor = pipeline.get_active_profile().get_device().query_sensors()[1] # 0-depth(两个infra)相机, 1-rgb相机,2-IMU # 自动曝光设置 color_sensor.set_option(rs.option.enable_auto_exposure, True) # step4 循环读取帧内容,如果需要并输出 print("Shooting ...") while 1: frame = pipeline.wait_for_frames() frame_data = np.asanyarray(frame.get_color_frame().get_data()) timestamp_ms = frame.timestamp cv2.imshow("received frames", frame_data) cv2.waitKey(int(1))

直接运行上述代码(对应Github项目中的realsense_simple_demo.py),正常情况下就能获得相机普通RGB数据,如下所示。 上面展示的就是获取数据的基本流程。而至于我们关心的Bayer Raw数据,其实获取方式是一样的,唯一不同的就是stream的数据类型要改成rs.format.raw16,其余不变即可,如下。 可以看到,图片上有类似网格的情况,这就是Bayer Array了。如果放大会看得更清楚。 最后,我们只需要把获取到的数据保存即可。当然需要注意的是,由于是16bit的数据,不能保存成jpg格式,应该保存成无损的png格式(支持非8bit)。

当然,为了更方便使用,也编写了不同的脚本来更加方便地获取各类型的数据,完整见Github项目。下面是彩色相机的代码:

import pyrealsense2 as rs import numpy as np import cv2 import os def getFormat(data_format): format = rs.format.rgb8 if data_format.__contains__("rgb8"): format = rs.format.rgb8 elif data_format.__contains__("bgr8"): format = rs.format.bgr8 elif data_format.__contains__("raw8"): format = rs.format.raw8 elif data_format.__contains__("raw10"): format = rs.format.raw10 elif data_format.__contains__("raw16"): format = rs.format.raw16 elif data_format.__contains__("y8"): format = rs.format.y8 elif data_format.__contains__("y16"): format = rs.format.y16 elif data_format.__contains__("z16"): format = rs.format.z16 elif data_format.__contains__("yuyv"): format = rs.format.yuyv elif data_format.__contains__("xyz32f"): format = rs.format.motion_xyz32f return format def isDirExist(path='output'): """ 判断指定目录是否存在,如果存在返回True,否则返回False并新建目录 :param path: 指定目录 :return: 判断结果 """ if not os.path.exists(path): os.makedirs(path) return False else: return True if __name__ == '__main__': config_file = "config.yml" fs = cv2.FileStorage(config_file, cv2.FILE_STORAGE_READ) # step0 一些运行前零碎操作 view_mode = int(fs.getNode("view_mode").real()) enable_AE_color = int(fs.getNode("enable_AE_color").real()) if view_mode == 2 or view_mode == 3: output_dir_image = fs.getNode("output_dir_image").string().lower() output_type_image = fs.getNode("output_type_image").string().lower() isDirExist(output_dir_image) if not output_type_image.startswith("."): output_type_image = "." + output_type_image # step1 检查并初始化设备可用性 ctx = rs.context() if len(ctx.devices) == 0: print("No realsense D435i was detected.") exit() device = ctx.devices[0] serial_number = device.get_info(rs.camera_info.serial_number) config = rs.config() config.enable_device(serial_number) # step2 根据配置文件设置数据流 data_format_color = fs.getNode("data_format_color").string().lower() frame_width_color = int(fs.getNode("frame_width_color").real()) frame_height_color = int(fs.getNode("frame_height_color").real()) frame_rate_color = int(fs.getNode("frame_rate_color").real()) color_format = getFormat(data_format_color) config.enable_stream(rs.stream.color, frame_width_color, frame_height_color, color_format, frame_rate_color) # step3 启动相机流水线并设置是否自动曝光 pipeline = rs.pipeline() profile = pipeline.start(config) color_sensor = pipeline.get_active_profile().get_device().query_sensors()[1] # 0-depth(两个infra)相机, 1-rgb相机,2-IMU # 自动曝光设置 if enable_AE_color == 1: color_sensor.set_option(rs.option.enable_auto_exposure, True) else: manual_exposure_color = fs.getNode("manual_exposure_color").real() color_sensor.set_option(rs.option.exposure, manual_exposure_color) # step4 循环读取帧内容,如果需要并输出 print("Shooting ...") while 1: frame = pipeline.wait_for_frames() frame_data = np.asanyarray(frame.get_color_frame().get_data()) timestamp_ms = frame.timestamp if data_format_color.__contains__("rgb"): frame_data = cv2.cvtColor(frame_data, cv2.COLOR_RGB2BGR) if view_mode == 1 or view_mode == 3: cv2.imshow("received frames", frame_data) cv2.waitKey(int(1)) if view_mode == 2 or view_mode == 3: cv2.imwrite(output_dir_image + "/" + str(timestamp_ms) + output_type_image, frame_data)

相比于上面的代码,加入了更多判断以应对不同情况。同时将一些参数以配置文件的形式进行储存,便于修改,如下所示。

4.拿到数据后我们要做什么

拿到原始的Bayer Raw数据之后,我们自然就需要自己对其进行解析,才能得到“看得过去”的彩色RGB影像。关于Raw数据的后处理,在这篇博客中也有提到。这里以实际数据进行说明,主要进行了Bayer Raw的Demosaic、白平衡、色彩缩放步骤,代码如下。

import cv2 import numpy as np if __name__ == '__main__': file_path = "./test_raw.png" # step1 读取数据 # ---------------------------------------------------------------------------------- raw_data = cv2.imread(file_path, cv2.IMREAD_UNCHANGED) # ---------------------------------------------------------------------------------- # step2 Bayer插值 # ---------------------------------------------------------------------------------- # Bayer array: # G B # R G raw_data_bgr = cv2.cvtColor(raw_data, cv2.COLOR_BAYER_GB2BGR) print('image size:', raw_data_bgr.shape) # ---------------------------------------------------------------------------------- # step3 白平衡 # ---------------------------------------------------------------------------------- band_b = raw_data_bgr[:, :, 0] band_g = raw_data_bgr[:, :, 1] band_r = raw_data_bgr[:, :, 2] band_r = band_r * 2.04 band_g = band_g band_b = band_b * 1.32 # ---------------------------------------------------------------------------------- # step4 缩放颜色尺度 # ---------------------------------------------------------------------------------- max_r = np.max(band_r) max_g = np.max(band_g) max_b = np.max(band_b) print('max red:', max_r, 'max green:', max_g, 'max blue:', max_b) min_value = min(max_r, min(max_g, max_b)) print('min value:', min_value) band_r_clip = np.where(band_r > min_value, min_value, band_r) band_g_clip = np.where(band_g > min_value, min_value, band_g) band_b_clip = np.where(band_b > min_value, min_value, band_b) # ---------------------------------------------------------------------------------- # step5 合并波段并输出 # ---------------------------------------------------------------------------------- band_r_clip_int = np.zeros([band_r_clip.shape[0], band_r_clip.shape[1]], np.uint8) band_g_clip_int = np.zeros([band_g_clip.shape[0], band_g_clip.shape[1]], np.uint8) band_b_clip_int = np.zeros([band_b_clip.shape[0], band_b_clip.shape[1]], np.uint8) for i in range(band_r_clip.shape[0]): for j in range(band_r_clip_int.shape[1]): band_r_clip_int[i, j] = int(band_r_clip[i, j] / 256) band_g_clip_int[i, j] = int(band_g_clip[i, j] / 256) band_b_clip_int[i, j] = int(band_b_clip[i, j] / 256) bands = cv2.merge((band_b_clip_int, band_g_clip_int, band_r_clip_int)) cv2.imwrite("merged.png", bands) # ----------------------------------------------------------------------------------

这里需要注意的是,虽然我们获取的数据是16bit的,但并不是说量化级数真的就是16bit。量化级数还是10bit,只是每个灰度级都乘以64,变成了16bit,占满存储级数。这点其实在这篇博客里有提到。如下图所示,可以看到,直接读取的16bit数据灰度级数存在间隔(像素个数为0),而且这个间隔是64。也就是说原本10bit量化灰度数据被缩放离散化到了16bit。 我们对其除以64,就可以得到正常的灰度直方图,如下所示。 感兴趣可以参考Github项目中的histogramRaw.py脚本。

运行上述代码(对应Github项目的parseRaw.py),就可以得到解析好的影像,如下所示。 和它对应的直接输出彩色图像如下。 可以看到差异还是比较大的,这是因为我们没有进行复杂的处理,包括lens shading的补偿等,只进行了基本的色彩解析。在一些较亮的地方,我们解析出来的图像细节方面更好一些,没有那么重的涂抹感(主要是因为锐化造成的),如下对比所示。 可以看出路灯的光晕和反光的区域更小。

至此,我们便完成了前面提到的任务:获取RealSense D435i的Bayer Raw数据,并对其进行了简单的处理。

5.参考资料 [1] https://www.intel.com/content/dam/support/us/en/documents/emerging-technologies/intel-realsense-technology/Intel-RealSense-D400-Series-Datasheet.pdf [2] https://support.intelrealsense.com/hc/en-us/community/posts/1500001169042-How-to-capture-realsense-RGB-IRs-raw-images- [3] https://support.intelrealsense.com/hc/en-us/community/posts/360049233974-Can-you-access-raw-synchronized-D435-output- [4] https://blog.csdn.net/u013832707/article/details/108584866 [5] https://github.com/IntelRealSense/librealsense/tree/master/wrappers/python/examples [6] https://github.com/IntelRealSense/librealsense/issues/450 [7] https://blog.csdn.net/qq_42393859/article/details/108468235 [8] https://www.freesion.com/article/8664151074/ [9] https://github.com/IntelRealSense/realsense-ros/issues/1409 [10] https://github.com/IntelRealSense/librealsense/issues/3878#issuecomment-569550710 [11] https://github.com/IntelRealSense/librealsense/issues/7275 [12] https://github.com/IntelRealSense/librealsense/issues/3376


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有