主要Python环境:
python==3.7.4;
pyrealsense2==2.38.1.2225;
pyqt5==5.11.2;
numpy=1.17.0;
opencv-python==4.2.0.32;
上述为笔者的环境配置,若无必要不用修改。
目录
目录
前言
一、Realsense D435i深度相机
二、使用步骤
1.引入库
2.初始化Ui_MainWindow
3.定义setupUi
4.设置控件文字显示
5.定义事件函数
6.启动窗体
7.效果
总结
本教程旨在整理本人多年来使用Intel公司出品的Realsense深度相机相关的开发经验和案例,涉及的主要内容有:Realsense D435i深度相机API的基本操作、Pyqt的基本使用、结合计算机视觉的一些应用案例。希望能够帮助到有需要的同学。
有关Realsense D435i深度相机的详细资料请参考Intel官网链接https://www.intelrealsense.com/depth-camera-d435i/
本基础篇教程主要介绍一些基本功能,包括RGB图和深度图的读取与显示、深度画面预设、IMU数据的读取、深度图的几种后处理方式等,并同时介绍相关的PyQt知识。
代码如下:
import cv2
import numpy as np
import math
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import pyrealsense2 as rs
首先实例化pyqt的窗体类Ui_MainWindow,代码如下:
class Ui_MainWindow(QMainWindow):
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.timer_camera = QtCore.QTimer() # 初始化定时器
self.timer_realsense = QtCore.QTimer()
# 定义realsense视频数据流
self.pipeline = rs.pipeline()
# 定义IMU数据流
self.imu_pipeline = rs.pipeline()
self.imu_config = rs.config()
self.imu_config.enable_stream(rs.stream.accel, rs.format.motion_xyz32f, 63) # acceleration
self.imu_config.enable_stream(rs.stream.gyro, rs.format.motion_xyz32f, 200) # gyroscope
# 定义点云
self.pc = rs.pointcloud()
self.config = rs.config()
# 设置对其方式为:深度图向RGB图对齐
self.align_to = rs.stream.color
self.alignedFs = rs.align(self.align_to)
# 创建着色器(其实这个可以替代opencv的convertScaleAbs()和applyColorMap()函数)
self.colorizer = rs.colorizer()
# 深度图像画面预设
self.preset = 0
# 记录点云的数组
self.vtx = None
self.height = 720
self.width = 1280
# 设置字体
self.font2 = QFont()
self.font2.setFamily("宋体")
self.font2.setPixelSize(22)
self.font2.setBold(True)
# 深度相机返回的图像和三角函数值
self.depth_image = None
self.img_r = None
self.pitch = None
self.roll = None
上述代码中需要注意的是画面预设self.preset,对于不同型号的相机画面预设配置模式是不一样的,可以参考Intel官方提供的Intel RealSense Viewer,对于D435i相机,如下图所示:
其中self.preset = 0 即代表第一行的预设Custom,往下同理直接通过序号调用。
接下来,在setupUi中设置自己的UI界面,包括各类窗体控件(label、button、groupbox等)的位置、大小、属性等内容,代码如下:
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1920, 1080)
MainWindow.setAcceptDrops(True)
MainWindow.setAutoFillBackground(False)
MainWindow.setStyleSheet("background-color: rgb(240, 248, 255);")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
# 所有控件的定义要从此处才能定义
# 显示RGB图
self.label_show_camera = QtWidgets.QLabel(self.centralwidget)
self.label_show_camera.setGeometry(QtCore.QRect(0, 0, 960, 540))
self.label_show_camera.setStyleSheet("background-color: rgb(255,255,255);")
self.label_show_camera.setObjectName("label_show_camera")
self.label_show_camera.setScaledContents(True)
# 显示深度图
self.label_show_camera1 = QtWidgets.QLabel(self.centralwidget)
self.label_show_camera1.setGeometry(QtCore.QRect(960, 0, 960, 540))
self.label_show_camera1.setStyleSheet("background-color: rgb(255,255,255);")
self.label_show_camera1.setObjectName("label_show_camera1")
self.label_show_camera1.setScaledContents(True)
# 显示相机位姿
self.label_show_camera2 = QtWidgets.QLabel(self.centralwidget)
self.label_show_camera2.setGeometry(QtCore.QRect(960, 560, 512, 512))
self.label_show_camera2.setStyleSheet("background-color: rgb(255,255,255);")
self.label_show_camera2.setObjectName("label_show_camera2")
self.label_show_camera2.setScaledContents(True)
# 显示目标坐标
self.label_show_performance = QtWidgets.QLabel(self.centralwidget)
self.label_show_performance.setGeometry(QtCore.QRect(300, 560, 600, 30))
self.label_show_performance.setObjectName("label_show_performance")
self.label_show_performance.setScaledContents(True)
self.label_show_performance.setStyleSheet("color:blue")
self.label_show_performance.setFont(self.font2)
# 显示加速度
self.label_show_accel = QtWidgets.QLabel(self.centralwidget)
self.label_show_accel.setGeometry(QtCore.QRect(1480, 660, 600, 30))
self.label_show_accel.setObjectName("label_show_accel")
self.label_show_accel.setScaledContents(True)
self.label_show_accel.setStyleSheet("color:blue")
self.label_show_accel.setFont(self.font2)
# 显示欧拉角
self.label_show_pose = QtWidgets.QLabel(self.centralwidget)
self.label_show_pose.setGeometry(QtCore.QRect(1480, 760, 600, 30))
self.label_show_pose.setObjectName("label_show_pose")
self.label_show_pose.setScaledContents(True)
self.label_show_pose.setStyleSheet("color:blue")
self.label_show_pose.setFont(self.font2)
# 开启相机的按钮
self.button_open_camera = QtWidgets.QPushButton(self.centralwidget)
self.button_open_camera.setGeometry(QtCore.QRect(20, 560, 200, 50))
self.button_open_camera.setStyleSheet("color: rgb(255, 255, 255);\n"
"border-color: rgb(0, 0, 0);\n"
"background-color: rgb(50,50,237);")
self.button_open_camera.setFlat(False)
self.button_open_camera.setObjectName("button_open_camera")
self.button_open_camera.setFont(QFont("宋体", 12, QFont.Bold))
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
接下来,我们要设置窗体控件的初始显示内容,并建立控件与对应的触发事件函数之间的联系,代码如下:
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Realsense D435i"))
self.button_open_camera.setText(_translate("MainWindow", "Start Detection"))
self.label_show_accel.setText(_translate("MainWindow", "Accel"))
self.label_show_pose.setText(_translate("MainWindow", "Pose"))
def slot_init(self): # 建立通信连接
self.button_open_camera.clicked.connect(self.button_open_camera_click)
self.timer_realsense.timeout.connect(self.get_photo)
在上一步中我们建立了与两个事件函数的通信连接,其中self.button_open_camera_click用于定义按下button的事件,self.get_photo用于定义定时器打开的事件。
按下button会开启深度相机的数据流并打开定时器,定时器开启后会调用self.get_photo函数获取深度相机返回的图像和IUM数据等,代码分别如下:
def button_open_camera_click(self):
if not self.timer_camera.isActive():
# 开启深度相机数据流
profile = self.pipeline.start(self.config)
depth_sensor = profile.get_device().first_depth_sensor()
# 开启IMU数据流
imu_profile = self.imu_pipeline.start(self.imu_config)
# 设置画面预设
sensor = self.pipeline.get_active_profile().get_device().query_sensors()[0]
sensor.set_option(rs.option.visual_preset, self.preset)
self.timer_realsense.start(50)
self.timer_camera.start(50)
def get_photo(self):
# 从数据流中读取一帧并进行对齐
frames = self.pipeline.wait_for_frames()
aligned_frames = self.alignedFs.process(frames)
# 分别获得深度帧和RGB帧
depth_frame = aligned_frames.get_depth_frame()
color_frame = aligned_frames.get_color_frame()
# 获取IMU数据
imu_frames = self.imu_pipeline.wait_for_frames()
accel_frame = imu_frames.first_or_default(rs.stream.accel, rs.format.motion_xyz32f)
#gyro_frame = imu_frames.first_or_default(rs.stream.gyro, rs.format.motion_xyz32f)
accel_info = accel_frame.as_motion_frame().get_motion_data()
gx, gy, gz = accel_info.x, accel_info.y, accel_info.z
self.label_show_accel.setText('Accel gx:' + format(gx, '.3f') + ' gy:' + format(gy, '.3f') + ' gz:' + format(gz, '.3f'))
# 计算欧拉角
# 俯仰角
pitch = int(180/math.pi * math.atan(abs(gz)/math.sqrt(gx * gx + gy * gy)))
# 滚转角
roll = int(180/math.pi * math.atan(abs(gx)/math.sqrt(gy * gy + gz * gz)))
# 计算出三角函数值方便坐标转换
vector = math.sqrt(gx * gx + gy * gy + gz * gz)
sin_pitch = abs(gz) / vector
cos_pitch = math.sqrt(gx * gx + gy * gy) / vector
sin_roll = abs(gx) / vector
cos_roll = abs(gy * gy + gz * gz) / vector
self.label_show_pose.setText('Pose pitch:' + str(pitch) + '° roll:' + str(roll) + '°')
# 获取帧的宽高
self.width = depth_frame.get_width()
self.height = depth_frame.get_height()
# 获取点云
self.pc.map_to(color_frame)
points = self.pc.calculate(depth_frame)
self.vtx = np.asanyarray(points.get_vertices())
self.vtx = np.reshape(self.vtx, (self.height, self.width, -1))
# 将深度帧和RBG帧转换为数组
img_r = np.asanyarray(color_frame.get_data())
depth_image = np.asanyarray(self.colorizer.colorize(depth_frame).get_data())
self.img_r = img_r
self.depth_image = depth_image
self.pitch = (sin_pitch, cos_pitch)
self.roll = (sin_roll, cos_roll)
showImage = QtGui.QImage(img_r.data, img_r.shape[1], img_r.shape[0], QtGui.QImage.Format_RGB888)
self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage)) # 读取图片到label_show_camera控件中
showImage1 = QtGui.QImage(depth_image.data, depth_image.shape[1], depth_image.shape[0],
QtGui.QImage.Format_RGB888)
self.label_show_camera1.setPixmap(QtGui.QPixmap.fromImage(showImage1))
记得最后一定要添加如下代码启动窗体:
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(mainWindow)
ui.slot_init()
mainWindow.show()
sys.exit(app.exec_())
可以看到窗体成功显示了RGB图和与之对应的深度图(由于房间没开灯,所以RGB图是黑的)。右下角空白窗体用于绘制相机位姿图,这个在下一篇文章会介绍。右下角控件显示了当前的重力加速度的三个分力和计算出的相机姿态角,分别为俯仰角5°,滚转角0°。
以上就是今天要讲的内容,本文介绍了Realsense D435i深度相机最基本的一些接口和操作,将RGB图、深度图以及IMU数据通过PyQt显示在了窗体应用程序中,下一篇文章将继续介绍深度图的后处理方法和相机位姿的绘制。
欢迎点赞、收藏⭐、留言、讨论,如有错误敬请指正!