利用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配置文件。实际情况如下图
image.png
下面将对每个文件进行介绍。

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文件的播放,界面设计比较简单,功能也是一看便知,直接上图不再赘言
image.png
各个控件的命名如下
image.png
需要提醒的是,本例为了简单直接使用了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 实现效果图

程序启动
image.png
选择文件
image.png
文件选择后
image.png
播放界面
image.png
关闭播放
image.png

5 最后

还要提醒的是,如果你的程序编译通过但是打不开,有可能是因为realsense2.dll没被复制到程序运行目录。

6最后的最后

完整的代码我厚颜放公号了
qrcode_for_gh_c077d3a9edc5_258.jpg
发送Qt001即可获得。
按理来说,这个程序介绍的已经很详细了,但是如果你在实际操作中遇到了问题可以与完整的对比一下,这个方向目前我还会继续做下去,感兴趣的话可以互相交流一下。
完结,撒花。

posted @ 2020-08-25 16:07  wwtd5210  阅读(992)  评论(0编辑  收藏  举报