更多精彩内容 |
---|
个人内容分类汇总 |
我写这个Demo的环境,但这个Demo不只局限于这个环境
Demo功能描述
- 启动时获取所有可用摄像头名称;
- 支持使用opencv打开本地摄像头,读取视频并显示;
- 由于opencv包含ffmpeg,所以支持打开多种格式的本地视频文件并显示;
- 支持打开多种协议的网络视频流并显示;
- 支持传入显示QImage、QPixmap图片进行显示;
- 支持图片的自适应窗口缩放,并保持纵横比。
# opencv需要带有ffmpeg才可以打开本地视频或者网络视频流,否则只能打开图片和摄像头
win32:CONFIG(release, debug|release): LIBS += -LE:/lib/opencv_MSVC2/build/x64/vc15/lib/ -lopencv_world460
else:win32:CONFIG(debug, debug|release): LIBS += -LE:/lib/opencv_MSVC2/build/x64/vc15/lib/ -lopencv_world460d
INCLUDEPATH += E:/lib/opencv_MSVC2/build/include
DEPENDPATH += E:/lib/opencv_MSVC2/build/include
#ifndef VIDEODISPLAY_H
#define VIDEODISPLAY_H
#include
#include
using namespace cv;
namespace Ui {
class VideoDisplay;
}
class VideoDisplay : public QWidget
{
Q_OBJECT
public:
explicit VideoDisplay(QWidget *parent = nullptr);
~VideoDisplay();
signals:
void updateImage(QImage img);
private slots:
void on_but_open_clicked();
void readImage();
void quitThread();
QImage MatToQImage(const Mat& mat);
void on_but_play_clicked();
void on_but_file_clicked();
private:
Ui::VideoDisplay *ui;
VideoCapture* m_cap = nullptr;
Mat mat;
bool m_play = false;
};
#endif // VIDEODISPLAY_H
#include "videodisplay.h"
#include "ui_videodisplay.h"
#include
#include
#include
VideoDisplay::VideoDisplay(QWidget *parent) :
QWidget(parent),
ui(new Ui::VideoDisplay)
{
ui->setupUi(this);
QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); // 获取可用摄像头列表
for(auto camera : cameras)
{
ui->com_cameras->addItem(camera.description());
}
m_cap = new VideoCapture();
connect(this, &VideoDisplay::updateImage, ui->playImage, &PlayImage::updateImage);
}
VideoDisplay::~VideoDisplay()
{
// 关闭并释放摄像头
if(m_cap)
{
if(m_cap->isOpened())
{
quitThread();
}
delete m_cap;
}
delete ui;
}
/**
* @brief 打开关闭摄像头
*/
void VideoDisplay::on_but_open_clicked()
{
if(!m_cap->isOpened())
{
if(ui->com_cameras->count() <= 0) return;
bool ret = m_cap->open(ui->com_cameras->currentIndex());
if(ret)
{
m_play = true;
ui->but_open->setText("关闭摄像头");
QtConcurrent::run(this, &VideoDisplay::readImage); // 在线程中读取
}
}
else
{
quitThread();
m_cap->release();
ui->but_open->setText("打开摄像头");
}
}
/**
* @brief 打开本地视频或者网络视流,如rtsp、rtmp
*/
void VideoDisplay::on_but_play_clicked()
{
if(!m_cap->isOpened())
{
QString url = ui->line_url->text();
if(url.isEmpty()) return;
bool ret = m_cap->open(url.toStdString());
if(ret)
{
m_play = true;
ui->but_play->setText("停止播放");
QtConcurrent::run(this, &VideoDisplay::readImage); // 在线程中读取
}
}
else
{
quitThread();
m_cap->release();
ui->but_play->setText("开始播放");
}
}
void VideoDisplay::on_but_file_clicked()
{
QString str = QFileDialog::getOpenFileName(this, "视频文件", "./", "视频文件(*)");
if(str.isNull()) return;
ui->line_url->setText(str);
}
/**
* @brief 退出opencv读取线程
* 在一个线程打开关闭m_cap,在另一个线程异步读取需要先退出线程再关闭m_cap,否则可能导致m_cap关闭后又读取导致报错
*/
void VideoDisplay::quitThread()
{
m_play = false;
while (!m_play) {
QThread::msleep(1); // 等待线程退出
}
m_play = false;
}
/**
* @brief 在线程中使用opencv读取图像
*/
void VideoDisplay::readImage()
{
while (m_play && m_cap->isOpened())
{
bool ret = m_cap->read(mat);
if(ret)
{
emit this->updateImage(MatToQImage(mat)); // 将mat转换为Qimage并发送给显示界面
}
QThread::msleep(1); // 防止频繁读取或者读取不到
}
m_play = true;
}
/**
* @brief Mat转QImage
* @param mat
* @return
*/
QImage VideoDisplay::MatToQImage(const Mat &mat)
{
switch (mat.type())
{
case CV_8UC1:
{
QImage img(mat.data, mat.cols, mat.rows, mat.cols, QImage::Format_Grayscale8);
return img;
}
case CV_8UC3:
{
QImage img(mat.data, mat.cols, mat.rows, mat.cols * 3, QImage::Format_RGB888);
return img.rgbSwapped();
}
case CV_8UC4:
{
QImage img(mat.data, mat.cols, mat.rows, mat.cols * 4, QImage::Format_ARGB32);
return img;
}
default:
{
return QImage();
}
}
}
E:\lib\OpenCV_MSVC2\build\x64\vc15\bin
路径下的opencv_videoio_ffmpeg460_64.dll
、opencv_videoio_msmf460_64.dll
、opencv_world460.dll
三个文件拷贝到编译生成的可执行文件路径下;opencv_videoio_msmf460_64.dll
这个文件。