簡述
使用 Qt 做一個實時顯示視訊的Demo,這裡結合Opencv來做的。該視訊可以是從攝像頭實時擷取的資料,也可以是本地儲存的視訊資料。
簡述下思路:
注:這裡注重說明一點,這裡必須用定時器,而不可用 waitKey(),因QLabel是元件,使用waitKey()方法會使得QLabel資料堵塞,不能顯示。
- 使用 QT 做一個檔案選擇器,選擇本地視訊;
- 使用opencv讀取該視訊資料,将視訊分解成一幀一幀的圖像;
- 利用定時器,定時的調用顯示圖像接口。(這裡使用QLabel顯示每一幀圖像;定時器的時間與視訊的幀率有關;);
代碼
界面展示:
1、初始化
QTimer m_timer = new QTimer(this);
VideoCapture m_cap = new VideoCapture();
//定時器信号關聯
connect(m_timer, SIGNAL(timeout()), this, SLOT(slots_catchCap()));
2、選擇本地視訊路徑
//"選擇視訊"按鈕點選事件
void LeakDetectionSimulator::slots_chooseVideo_Btn_Clicked()
{
//定義檔案對話框類
QFileDialog *fileDialog = new QFileDialog(this);
//定義檔案對話框标題
fileDialog->setWindowTitle(QStringLiteral("選擇視訊"));
//設定預設檔案路徑
fileDialog->setDirectory(".");
//設定檔案過濾器
fileDialog->setNameFilter(tr("Video(*.mp4 *.avi *.mkv *.flv *.rmvb)"));
//設定視圖模式
fileDialog->setViewMode(QFileDialog::Detail);
//列印所選擇的檔案的路徑
QString fileName;
if (fileDialog->exec())
{
fileName = fileDialog->selectedFiles()[0];
//顯示選擇的視訊路徑
ui.videoPath_LineEdit->setText(fileName);
}
}
3、開始檢測
//“開始檢測”按鈕點選事件
void slots_start_Btn_Clicked()
{
if (ui.start_Btn->text().compare(QStringLiteral("停止檢測")) == 0)
{
//關閉定時器
m_timer->stop();
ui.imgShow_label->clear();
PrintInfo("已停止檢測!");
ui.start_Btn->setText(QStringLiteral("開始檢測"));
return;
}
//擷取全局參數
if (ui.videoPath_LineEdit->text().compare("") == 0)
{
QMessageBox::about(NULL, QStringLiteral("提示"), QStringLiteral("未選擇視訊,請選擇視訊後重新操作!"));
return;
}
if (ui.interval_LineEdit->text().compare("") == 0)
{
QMessageBox::about(NULL, QStringLiteral("提示"), QStringLiteral("未輸入間隔時間,請檢查!"));
return;
}
//打開視訊
m_cap->open(QStr2Str(ui.videoPath_LineEdit->text()));
if (m_cap->isOpened() == false)
{
PrintInfo("視訊未能打開!");
return;
}
//擷取目前視訊幀率
double rate = m_cap->get(CV_CAP_PROP_FPS);
//每一幀之間的延時與視訊的幀率相對應
m_delay = 1000 / rate;
//開啟定時器
m_timer->start(m_delay);
PrintInfo("開始檢測!");
ui.start_Btn->setText(QStringLiteral("停止檢測"));
}
//QString 轉 string (無中文論碼)
string QStr2Str(const QString qStr)
{
QByteArray cdata = qStr.toLocal8Bit();
return string(cdata);
}
//擷取圖像(定時器關聯的槽方法)
void slots_catchCap()
{
m_cap->read(m_frame);
if (m_frame.empty())
{
PrintInfo("視訊讀取完!");
//關閉定時器
m_timer->stop();
return;
}
//顯示圖像
ui.imgShow_label->setPixmap(QPixmap::fromImage(MatToQImage(m_frame)));
}
// Mat 轉換成 QImage
QImage MatToQImage(const cv::Mat &inMat)
{
switch (inMat.type())
{
case CV_8UC4: // 8-bit, 4 channel
{
QImage image(inMat.data,
inMat.cols, inMat.rows,
static_cast<int>(inMat.step),
QImage::Format_ARGB32);
return image;
}
case CV_8UC3: // 8-bit, 3 channel
{
QImage image(inMat.data,
inMat.cols, inMat.rows,
static_cast<int>(inMat.step),
QImage::Format_RGB888);
return image.rgbSwapped();
}
case CV_8UC1:// 8-bit, 1 channel
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
QImage image(inMat.data,
inMat.cols, inMat.rows,
static_cast<int>(inMat.step),
QImage::Format_Grayscale8); //Format_Alpha8 and Format_Grayscale8 were added in Qt 5.5
#else
static QVector<QRgb> sColorTable;
// only create our color table the first time
if (sColorTable.isEmpty())
{
sColorTable.resize(256);
for (int i = 0; i < 256; ++i)
{
sColorTable[i] = qRgb(i, i, i);
}
}
QImage image(inMat.data,
inMat.cols, inMat.rows,
static_cast<int>(inMat.step),
QImage::Format_Indexed8);
image.setColorTable(sColorTable);
#endif
return image;
}
default:
// qWarning() << "CVS::cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
break;
}
return QImage();
}