利用Qt5与OpenCV实现realsense相机录制bag文件播放的简单例程
1.项目背景
Realsense是Inter公司的一个立体相机系列,提供了易用的深度相机硬件并配备了开发SDK。但是在实践中,我们往往需要按照自己的功能需求对图像进行处理。一般的做法是仅使用Realsense的通讯库,获得左右相机图像、彩色相机图像、深度图像后使用OpenCV对获得的图像进行处理。如果仅仅是对图像进行处理分析的话,OpenCV自带的也有简单的GUI。但是,如果考虑将其开发为一个完整的桌面程序,配合Qt或许是个好选择。
2.项目简介
本项目作为一个例程,所要完成的目标是从录制好的.bag文件读取视频,并将其在Qt绘制的界面中播放出来。项目本身有几个关键点:
(1)Qt+OpenCV+Realsense开发环境的配置
(2)图像帧的读取与格式转换
(3)程序中各函数的功能设计
由于开发环境配置具有一定的通用性,我准备之后再专门写一个说明,在本例中假设在Windows下的开发环境已经搭建良好,可以直接开始工作。
3.程序介绍
本程序遵照Qt程序的一般设计规则,程序主要包括.pro文件;main.cpp;mainwindow.h;mainwindow.cpp;mainwindow.ui。另外为了环境配置的简单,单独的写了一个opencv+realsense的.pri配置文件。实际情况如下图
下面将对每个文件进行介绍。
3.1项目文件.pro与配置文件.pri
Qt的pro文件是Qt的项目管理文件,当你新建项目时会自动创建,在本例中,pro文件仅有两句与自动生成的不同,具体为:
DISTFILES += \
../opencv_realsense.pri//在项目中添加已经配置好的pri文件
win32{
include("../opencv_realsense.pri")
}//在window下包含pri文件内声明的库
而在pri文件中主要完成的是对项目使用的opencv以及realsense库进行包含,具体代码为:
INCLUDEPATH += D:/opencv348/opencv/build/include\
D:/opencv348/opencv/build/include/opencv\
D:/opencv348/opencv/build/include/opencv2\
"C:/Program Files (x86)/Intel RealSense SDK 2.0/include/"
Debug: {
LIBS += D:/opencv348/opencv/build/x64/vc14/lib/opencv_world348d.lib\
"C:/Program Files (x86)/Intel RealSense SDK 2.0/lib/x64/realsense2.lib"
}
Release: {
LIBS += D:/opencv348/opencv/build/x64/vc14/lib/opencv_world348.lib\
"C:/Program Files (x86)/Intel RealSense SDK 2.0/lib/x64/realsense2.lib"
}//库的位置与opencv以及realsense的安装位置有关
3.2 UI介绍
本例仅考虑对已录制的bag文件的播放,界面设计比较简单,功能也是一看便知,直接上图不再赘言
各个控件的命名如下
需要提醒的是,本例为了简单直接使用了QLabel作为显示空间,如果你需要在显示上实现更复杂的功能,可能QGraphicsItem更适合。
3.3 main.cpp
本例中没有对main.cpp修改,直接实例化mainwindow调用show方法,进入循环,代码如下:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
3.4 mainwindow.h
在mainwindow.h中,主要完成的任务是对使用的头文件进行包含;mainwindow中需要的函数声明;以及一些类和变量的声明。
按照上面的任务分类分三部分展示代码:
#include <QMainWindow>
#include <QTimer>
#include <QString>
#include <QMessageBox>
#include <QFileDialog>
#include <QDebug>
#include <QFile>
#include <QPixmap>
#include <QDir>
#include <QTransform>
#include "opencv2/opencv.hpp"
#include "librealsense2/rs.hpp"
private slots:
void on_btn_play_clicked();
void on_btn_stop_clicked();
void updateWindow();
void on_btn_openFile_pressed();
void on_lineEdit_openFile_textChanged(const QString &arg1);
private:
Ui::MainWindow *ui;
QTimer * timer;//刷新定时器
QImage depth_QImage,color_QImage;//QImage格式帧
QString fileName;//文件名变量
3.5 mainwindow.cpp
mainwindow.cpp是我们这个小程序的核心功能实现部分,我会将其分为几个部分分别介绍。
(1) 变量声明以及定时器实例化
有一些后面需要使用的变量提前声明,以及使用的命名空间
using namespace cv;
using namespace rs2;
Mat depth_Mat,color_Mat;//Mat格式帧
colorizer color_map;//深度图着色过滤器
pipeline pipe;//创建管道
另外需要在构造函数中实例化一个定时器,只需要添加一行代码
timer = new QTimer(this);
(2) 获取bag文件的实际位置
本来这个小例程是可以直接把bag文件拖到可执行文件的目录下,然后硬编码fileName的具体值的,但是bag文件是很大的,可能一分钟的视频就几个G了,来回拖有些占地方也不方便。因此,在这里使用了一个lineEdit和pushButton配合获得文件的地址,这两个函数都不难,直接写下
void MainWindow::on_btn_openFile_pressed()//选择文件
{
QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"), QDir::currentPath(), tr("Files (*.bag)"),0, QFileDialog::DontUseNativeDialog);
if(QFile::exists(fileName))
{
qDebug()<<"exist";
ui->lineEdit_openFile->setText(fileName);
}
}
void MainWindow::on_lineEdit_openFile_textChanged(const QString &arg1)//更新行编辑器的文本内容
{
this->fileName = arg1;
}
唯一有一点比较困扰我的是,在我使用QFileDialog::getOpenFileName
函数时,直接使用四参数的常用类型就导致程序卡死,配置第六个参数后才能正常运行,但是和默认的四参数的效果有一点点差异,如果有知道怎么回事的大佬还请教我。使用六参数版的解决方案出自链接。
(3)打开与关闭视频
打开与关闭按钮对应的槽函数分别为
void MainWindow::on_btn_play_clicked()//播放视频
{
QString fileName = ui->lineEdit_openFile->text();//从行编辑器获取实际的文件名
if(QFile::exists(fileName))//文件存在判别
{
qDebug()<<"exist";
}
config cfg;//从文件读取视频流时需要的配置
cfg.enable_device_from_file(fileName.toStdString());
connect(timer,SIGNAL(timeout()),this,SLOT(updateWindow()));//使用定时器定时刷新窗口
timer->start(20);
pipe.start(cfg);//打开视频流
ui->lb_message->setText("playing");//更改状态指示
}
void MainWindow::on_btn_stop_clicked()//关闭视频
{
pipe.stop();
timer->stop();
ui->lb_color->clear();
ui->lb_depth->clear();
ui->lb_message->setText("closed");
}
而在on_btn_play_clicked
函数中,通过connect将定时器与刷新函数链接起来,实现每隔20ms刷新显示,而刷新函数的代码如下:
void MainWindow::updateWindow()//刷新窗口
{
frameset data = pipe.wait_for_frames();//获取图像帧
frame depth = data.get_depth_frame().apply_filter(color_map);//获得着色后的深度图像
const int w = depth.as<video_frame>().get_width();//获得帧的大小
const int h = depth.as<video_frame>().get_height();
depth_Mat = Mat(Size(w,h),CV_8UC3,(void*)depth.get_data(),Mat::AUTO_STEP);//构造mat类型数据
cvtColor(depth_Mat,depth_Mat,CV_BGR2RGB);//对颜色通道进行调整
depth_QImage = QImage((const unsigned char*)(depth_Mat.data),depth_Mat.cols,depth_Mat.rows,QImage::Format_RGB888);//实现Mat到QImage的转换
ui->lb_depth->setPixmap(QPixmap::fromImage(depth_QImage));//显示设置
ui->lb_depth->setFixedSize(480,260);
ui->lb_depth->setScaledContents(true);
//彩色窗口与深度窗口大致相同
//frame color = data.get_color_frame();
}
4 实现效果图
程序启动
选择文件
文件选择后
播放界面
关闭播放
5 最后
还要提醒的是,如果你的程序编译通过但是打不开,有可能是因为realsense2.dll没被复制到程序运行目录。
6最后的最后
完整的代码我厚颜放公号了
发送Qt001即可获得。
按理来说,这个程序介绍的已经很详细了,但是如果你在实际操作中遇到了问题可以与完整的对比一下,这个方向目前我还会继续做下去,感兴趣的话可以互相交流一下。
完结,撒花。