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