• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • videodisplay.h
    #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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • videodisplay.cpp
    #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();
            }
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158

    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、完整源代码💻

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

  • 相关阅读:
    基于网络爬虫的购物平台价格监测系统的设计与实现
    DDoS和CC攻击的原理
    MySQL实战实战系列 07 行锁功过:怎么减少行锁对性能的影响?
    【收纳】电脑资料-高效整理电脑上的文件
    SLAM经验分享:少年不惧岁月长
    Python手册
    【Linux】进程控制 —— 进程替换
    计算机组成原理习题课第四章-2(唐朔飞)
    家里蹲了七个月的大学生活是怎么度过的?
    动静态库--Linux
  • 原文地址:https://blog.csdn.net/qq_43627907/article/details/125668164