Qt+OpenCV读取摄像头视频、本地视频、网络视频流Demo 原创

Qt+OpenCV读取摄像头视频、本地视频、网络视频流Demo📹

更多精彩内容
👉个人内容分类汇总 👈

1、准备工作🎥

  • 看这里,下载官方编译的版本就可以了;

2、说明📺

  • 我写这个Demo的环境,但这个Demo不只局限于这个环境

    • 系统:Windows10
    • Qt版本:V5.12.5
    • OpenCV:opencv-4.6.0-vc14_vc15.exe(注意如果自己编译则要包含ffmpeg,否则部分功能不能使用)
    • 编译器:MSVC2017-64
  • Demo功能描述

    1. 启动时获取所有可用摄像头名称;
    2. 支持使用opencv打开本地摄像头,读取视频并显示;
    3. 由于opencv包含ffmpeg,所以支持打开多种格式的本地视频文件并显示;
    4. 支持打开多种协议的网络视频流并显示;
    5. 支持传入显示QImage、QPixmap图片进行显示;
    6. 支持图片的自适应窗口缩放,并保持纵横比。

3、关键代码🔍

  • VideoPlay.pro
# 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
  • videodisplay.h
#ifndef VIDEODISPLAY_H
#define VIDEODISPLAY_H

#include <QWidget>
#include <opencv2/opencv.hpp>

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

  • videodisplay.cpp
#include "videodisplay.h"
#include "ui_videodisplay.h"
#include <QtConcurrent>
#include <QCameraInfo>
#include <QFileDialog>

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();
        }
    }
}


4、注意事项🔎

  • 如果opencv没有配置环境变量,则这个程序编译后无法运行;
  • 需要将opencv存放路径E:\lib\OpenCV_MSVC2\build\x64\vc15\bin路径下的opencv_videoio_ffmpeg460_64.dllopencv_videoio_msmf460_64.dllopencv_world460.dll三个文件拷贝到编译生成的可执行文件路径下;
  • 注意: 低版本的opencv没有opencv_videoio_msmf460_64.dll这个文件。

5、实现效果

在这里插入图片描述

6、完整源代码💻

💡💡💡💡💡💡💡💡💡💡💡💡💡💡

posted @ 2022-07-07 22:10  mahuifa  阅读(0)  评论(0编辑  收藏  举报  来源