1. 程式人生 > 實用技巧 >利用Qt5與OpenCV實現realsense相機錄製bag檔案播放的簡單例程

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