こちらの記事について、内容・翻訳・視点・長さなど、皆様のご意見をお送りください。今後の記事製作の参考にしたいと思います。
Thank you! Your feedback has been received.
There was a problem submitting your feedback, please try again later.
こちらの記事の感想をお聞かせください。
之前的教学文当中主要教学如何安装与使用RealSense,本次针对RealSense的Python 范例进行详解。现在市面上相当多项目都使用上RealSense了,越来越多人有撰写客制化程序的需求,这边就带大家认识Intel提供的Sample Code以及我们可以稍微修改的内容吧!
作者 |
张嘉钧 |
难度 |
普通 |
材料表 |
Intel RealSense D435 X1 NVIDIA Jetson Nano X1 |
目录
pyrealsense的基礎 ( python-tutorial-1-depth.py ) 6
透過opencv顯示pipeline的畫面 ( opencv_viewer_example.py ) 10
安装RealSense Viewer
要使用RealSense系列的摄影机建议先安装 RealSense Viewer,有一些图形化的选项参数可以做微调与显示,没有驱动的时候也会自动搜寻并安装,每次我要安装新系统都会先安装Viewer,确认抓得到深度摄影机之后才会再安装pyrealsense2。
安装RealSense Viewer的方法很简单,在 librealsense 这个Github当中有介绍如何使用Jetson Nano来安装Viewer,我撷取几个重点步骤,想要了解更详细的信息可以到下列网址:
https://github.com/IntelRealSense/librealsense/blob/master/doc/installation_jetson.md
1.准备好你的 Jetson Nano
2.确保你的环境是 NVIDIA®L4T Ubuntu 16.04 / 18.043 ( NVIDIA 原厂提供的映象档 )
3.选择 RealSense SDK 的后端 ( Backend ):这步可省略,通常需要客制化的使用者才需要来改变后端的API,我们使用预设的就可以了。
4.注册服务器公钥
$ sudo apt-key adv --keyserver keys.gnupg.net --recv-key F6E65AC044F831AC80A06380C8B3A55A6F3EFCDE || sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key F6E65AC044F831AC80A06380C8B3A55A6F3EFCDE
5.将服务器加入repositories,待会透过apt-get去安装才抓得到来源
Ubuntu 16请使用下列指令
$ sudo add-apt-repository "deb https://librealsense.intel.com/Debian/apt-repo xenial main" -u
Ubuntu 18 请使用下列指令
$ sudo add-apt-repository "deb https://librealsense.intel.com/Debian/apt-repo bionic main" -u
6.安装SDK
$ sudo apt-get install librealsense2-utils
$ sudo apt-get install librealsense2-dev
7.开启RealSense Viewer,可于终端机中输入指令开启
$ realsense-viewer
安装Pyrealsense2
Pyrealsense是Intel RealSense的Python函式库,透过这个函式库可以开启RealSense所有镜头以及取得到传感器的数值,对于Windows用户,Intel已经提供 PyPI的发行版可以透过 pip install pyrealsense2 来安装,但是 Jetson Nano ( Ubuntu ) 只能从源头安装这个函式库。
1.从源头来安装就需要将整个realsense的github下载下来:
$ git clone https://github.com/IntelRealSense/librealsense.git
$ cd librealsense
2.确保apt-get的版本是最新的
$ sudo apt-get update && sudo apt-get upgrade
3.确保有安装Python环境 ( 以Python3为例 )
$ sudo apt-get install python3 python3-dev
4.透过CMake建置,并且强制使用Python3 来编译
$ mkdir build
$ cd build
$ cmake ../ -DBUILD_PYTHON_BINDINGS:bool=true -DPYTHON_EXECUTABLE=/usr/bin/python3.6
$ make -j5
$ sudo make install
5.将realsense的函式库加入环境变量中
$ nano ~/.bashrc
export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python3.6/pyrealsense2
6.接着可以导入函式库,基本上没报错就没问题了
$ python3
>> import pyrealsense2 as rs
Python 范例程序详解
首先,先来丢个pyrealsense的文件,想要了解更多函式的用法建议一定要去看文件:https://intelrealsense.github.io/librealsense/python_docs/_generated/pyrealsense2.html
接着,这边提供几个范例程序的详细解说,注意,此部分将不会放上完整程序代码,完整程序代码请至librealsense/wrappers/python/examples/ 中去查看:
Pyrealsense2基础 ( python-tutorial-1-depth.py )
这个例子主要教学如何透过pyrealsense2开启影像并撷取特定位置的深度信息,此范例会将该实时影像划之像素分成多个 10 x 20 的区域,并且将一公尺以内的深度影像转换成文本模式,显示结果如下。
首先我们需要导入函式库:
# 导入函示库
import pyrealsense2 as rs
使用pipline的方法存取RealSense摄影机并且透过config宣告基本的参数:
try:
# 建立一个context对象存放所有 RealSense 的处理函示
pipeline = rs.pipeline()
# 配置串流对象
config = rs.config()
# 宣告特定设备进行影像串流 ( 定义的相机, 宽, 高, realsense的型态, 帧率 )
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
# 开启影像串流
pipeline.start(config)
接着因为是实时影像,所以需要使用While来运行,不断的截取与处理新的影像,我们可以透过wait_for_frames取得最新的影像,因为我们一开始设定的pipeline是深度的摄影机 ( rs.stream.depth ),并且需要透过 get_depth_frame来取得深度影像,最后再做二次确认,如果没有取得影像信息后面的程序会报错:
# 使用 While 循环不断撷取、处理新的影像
while True:
# 等待有新的影像信息才继续
frames = pipeline.wait_for_frames()
# 取得深度影像
depth = frames.get_depth_frame()
# 确保有获得深度信息 否则后续程序会出错
if not depth: continue
将输入图片 ( 480 x 640 ) 划分成 ( 20 x 10 ) 的像素区块进行深度的解析并将一公尺以内的画面转换成文字显示,这部份我们将分成两个部分介绍,首先先宣告文字覆盖的数组 ( coverage ),接着我们使用两个For循环将所有的像素都读取过一次,再读取的同时先取得深度讯息 ( get_distance ) 再判断是否于一公尺内,如果是的话就针对 coverage 的内容「加1」,这边算法比较特别的地方是由于 coverage的长度是64所以我们必须以10个单位内的像素做一个总和 ( 640/10=64 ),举例来说如果位置0~9的像素都是一公尺以内的话最后coverage[0] 的数值就是10:
# 覆盖范围
coverage = [0]*64
# 逐个像素 进行 距离检测 ( 高480 宽640)
for y in range(480):
for x in range(640):
# 透过 get_distance 取得该坐标的深度
dist = depth.get_distance(x, y)
# 如果对象在一公尺以内 就把Coverage填上 1
if 0 < dist and dist < 1:
# 640(图片) // 10(字符串长度) = 64 ( Coverage大小)
coverage[x//10] += 1
我们在上述程序中处理了10 x 20像素区域10的部分,接着要处理20的部分并且最后要将文字显示出来,因为像素区域高是20并且位置是从0开始所以我们取20的余数等于19来判断是否20行了,这里我们需要将深度信息转换成文字内容,所以先宣告了一个line来存放文字信息,接着去解析长度为64的coverage内容,针对coverage的内容整除25来转换成文字,这边除以25的部分我个人认为是为了让输出的文本模式图像比例较为正常,最后再刷新coverage:
# 计算了20行的深度后 显示一次 coverage 并刷新
if y%20 is 19:
line = ""
for c in coverage:
# 整除25稍微整理一下,让其比例看起来较像正常的图像
line += " .:nhBXWW"[c//25]
# 刷新 coverage
coverage = [0]*64
print(line)
透过opencv显示pipeline的画面
( opencv_viewer_example.py )
这个范例的重点在于「如何同时开启深度与RGB摄影机」、「如何转换成OpenCV并且将其显示出来」,那就让我们开始解析程序吧!
首先导入函式库以及宣告pipeline跟config:
# 导入函式库
import pyrealsense2 as rs
import numpy as np
import cv2
# 建立一个context对象存放所有 RealSense 的处理函示
pipeline = rs.pipeline()
# 配置串流对象
config = rs.config()
接着这边提供了取得设备信息的方式,最后的 device_product_line 包含了首个可用设备的名称:
# 可以透过下列程序取得设备信息
# 透过 resolve 确认第一个可用的设备
pipeline_wrapper = rs.pipeline_wrapper(pipeline)
pipeline_profile = config.resolve(pipeline_wrapper)
# 取得该设备信息
device = pipeline_profile.get_device()
device_product_line = str(device.get_info(rs.camera_info.product_line))
接着要宣告影像串流,这次范例提供了深度与RGB影像,但因为L500系列之RGB摄影机分辨率与其他系列不同,所以才需要取得摄影机名称,这里我们需要加上一个判断,如果L500系列则RGB影像串流需要设定成960 x 540:
# 建立「深度」影像串流
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
# 建立「彩色」影像串流
# 由于 L500 系列的彩色摄影机分辨率维 960 x 540 所以才需要取得设备信息
if device_product_line == 'L500':
config.enable_stream(rs.stream.color, 960, 540, rs.format.bgr8, 30)
else:
config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
接着就可以开始串流并且取得最新的影像信息:
# 开启影像串流
pipeline.start(config)
try:
while True:
# 等待最新的影像,wait_for_frames返回的是一个合成的影像
frames = pipeline.wait_for_frames()
# 可以透过下列函式各自取得深度或彩色影像
depth_frame = frames.get_depth_frame()
color_frame = frames.get_color_frame()
# 确保是否有取得影像
if not depth_frame or not color_frame:
continue
接着就是如何让OpenCV可以显示的部分了,首先要先来解决数据格式的问题,因为OpenCV吃的是Numpy格式所以我们需要先将其转换成numpy array:
# 由于 Opencv 显示格式为 numpy 所以需要转换成 nparray
depth_image = np.asanyarray(depth_frame.get_data())
color_image = np.asanyarray(color_frame.get_data())
接着处理深度影像信息的色彩格式,这部分稍微复杂了一些,先介绍一下功能,这边会使用 cv2. convertScaleAbs将深度数值缩放到0~255之间并转换成uint 8 的数据型态,再透过 applyColorMap将颜色对应上数值:
# 如果要显示深度影像需要将其转换成 8-bit 的影像格式
depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET)
最后因为深度摄影机与RGB摄影机分辨率不同的关系,需要将输出图片进行缩放才能够合并在一起,需要先取得彼此维度 ( shape ),经过判断之后再使用一般的 cv2.resize就可以缩放特定图片了:
# 取得影像的维度
depth_colormap_dim = depth_colormap.shape
color_colormap_dim = color_image.shape
# 如果维度不同直接将 彩色图片 进行 缩放,最后再水平合并 ( hstack )
if depth_colormap_dim != color_colormap_dim:
resized_color_image = cv2.resize(color_image, dsize=(depth_colormap_dim[1], depth_colormap_dim[0]), interpolation=cv2.INTER_AREA)
images = np.hstack((resized_color_image, depth_colormap))
else:
images = np.hstack((color_image, depth_colormap))
接着就可以显示合并后的图片了:
# 显示图片
cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
cv2.imshow('RealSense', images)
cv2.waitKey(1)
针对convertScaleAbs 可以有一些更深入的理解,这个函式的目的是将数值缩限到0~255之间并且计算绝对值后转换成 uint-8 的数据形式,公式如下:
我们可以透过控制 alpha值来计算最后显示的颜色最大距离是多少,写一个简单的程序来计算一下,我先宣告了一个简单的数组 (a),并且控制了b_alpha跟c_alpha的数值,各别为0.05、0.03,进行covert之后再显示出来。
import cv2
def count_dis(alpha):
return (255/alpha)
a = np.asarray( [[6000, 1000, 0, -100, 8000],
[5000, 100 , 7500, 60, 5],
[50, -1000, 225, 43, 192]] )
print('\nOriginal')
print(a)
b_alpha = 0.05
print(f'\nAlpha : {b_alpha},\tMax distance : {count_dis(b_alpha)}')
b = cv2.convertScaleAbs(a, alpha=b_alpha)
print(b)
c_alpha = 0.03
print(f'\nAlpha : {c_alpha},\tMax distance : {count_dis(c_alpha)}')
c = cv2.convertScaleAbs(a, alpha=c_alpha)
print(c)
显示结果如下,可以注意到当alpha值为0.05最大距离数值为5100,所以大于5100都会被标为255,此外由于有绝对值所以负值都会被转换成正值,1000跟 (-1000) 的数值都转换后的都是50:
我们用更直观的方式来了解,这边使用的是D435,由于转换后的数值会影响ColorMap的结果,所以我们一样使用0.03跟0.05来测试一下,先给予cv2.COLORMAP_JET的色度图如下
可以看到由于数值0.03的时候最大距离为8公尺,我距离门只有2公尺所以颜色分布都还在前半段,可以想象将色度图切成八等分去抓颜色分布;而数值0.05最大距离为5公尺,所以2公尺处颜色分布会落在偏中间也就是绿色的位置:
Alpha : 0.03 |
Alpha : 0.05 |
我们将会在下一篇介绍其他功能:
- 对齐RGB跟Depth画面并去除背景 ( align-depth2color.py )
- 解决掉帧问题 ( frame_queue_example.py )
- 读取预录好的深度影像方式 ( read_bag_example.py )
结语
在这篇我们学会怎么在Jetson Nano上安装RealSense Viewer以及Python函式库 pyrealsense2,除此之外还深入了解了两个范例程序的内容,下一篇将把剩余的范例程序一一介绍完,后续也会撰写一些简单的程序给大家参考。