利用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即可獲得。
按理來說,這個程序介紹的已經很詳細了,但是如果你在實際操作中遇到了問題可以與完整的對比一下,這個方向目前我還會繼續做下去,感興趣的話可以互相交流一下。
完結,撒花。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM