简述
使用 Qt 做一个实时显示视频的Demo,这里结合Opencv来做的。该视频可以是从摄像头实时获取的数据,也可以是本地保存的视频数据。
简述下思路:
- 使用 QT 做一个文件选择器,选择本地视频;
- 使用opencv读取该视频数据,将视频分解成一帧一帧的图像;
- 利用定时器,定时的调用显示图像接口。(这里使用QLabel显示每一帧图像;定时器的时间与视频的帧率有关;);
注:这里注重说明一点,这里必须用定时器,而不可用 waitKey(),因QLabel是组件,使用waitKey()方法会使得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();
}