Qt实用技巧:使用OpenCV库的视频播放器(支持播放器操作,如暂停、恢复、停止、时间、进度条拽托等...

news/2024/7/7 19:21:31

点击上方“小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达

需求

使用OpenCV库的视频播放器(支持播放器操作,如暂停、恢复、停止、时间、进度条拽托等)。

原理

 使用OpenCV打开视频文件,获取总帧数,根据当前帧数,刷新当前时间戳与预期的时间间隔,调用槽函数动态刷新播放内容。

注意

当前只测试了avi文件

运行效果

1118c9010ff28748b1c97f09b31a1bbf.gif

核心类代码

OpenCVPlayerManager.h

#ifndef OPENCVPLAYERMANAGER_H
#define OPENCVPLAYERMANAGER_H/************************************************************\* 控件名称:OpenCVPlayerManager,OpenCV管理类* 控件描述:*          1.OpenCV打开视频文件*          2.播放器操作:播放、暂停、停止*          3.播放时显示:当前时间、总时间*          4.设置当前播放的时间点** 作者:红模仿    联系方式:QQ21497936* 博客地址:https://blog.csdn.net/qq21497936*       日期                版本               描述*   2019年11月25日      v1.0.0         opencv打开文件
\************************************************************/#include <QObject>
#include <QImage>
#include <QTimer>
#include <QElapsedTimer>
// opencv
#include "opencv/highgui.h"
#include "opencv/cxcore.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"class OpenCVPlayerManager : public QObject
{Q_OBJECT
public:enum PLAY_STATE {PLAY_STATE_PLAYING,PLAY_STATE_PAUSE,PLAY_STATE_STOP};public:explicit OpenCVPlayerManager(QObject *parent = 0);~OpenCVPlayerManager();public:QString getWindowTitle() const;public:void setWindowTitle(const QString &windowTitle);signals:void signal_captureOneFrame(cv::Mat mat);           // 接收图像后抛出信号void signal_playStateChanged(OpenCVPlayerManager::PLAY_STATE playState);// 播放器状态void signal_durationChanged(qint64 duration);       // 视频总长度void signal_positionChanged(qint64 position);       // 当前位置public:bool startPlay(QString filePath, int width = 480, int height = 320);void pause();                       // 暂停void resume();                      // 恢复播放void stopPlay();                    // 停止播放void setPosition(qint64 position);  // 切换到播放位置public slots:bool slot_start();                  // 开启线程bool stop();                        // 关闭线程protected slots:void slot_captrueFrame();           // 消息循环获取图像void slot_stopPlay();               // 停止播放void slot_setPosition(qint64 position);public:static QImage cvMat2QImage(const cv::Mat &mat);private:cv::VideoCapture *_pVideoCapture;   // 播放文件实例QString _filePath;                  // 播放文件路径bool _running;                      // 线程是否运行int _totalFrames;                   // 总帧数int _fps;                           // 每秒帧数int _currentFrame;                  // 当前帧数int _width;                         // 高度int _height;                        // 宽度QElapsedTimer _elapsedTimer;        // 计时器qint64 _pauseMs;                    // 暂停的毫秒qint64 _duration;                   // 视频总长度(毫秒)PLAY_STATE _playState;              // 播放器状态int _position;                      // 改变到的播放位置bool _setPostion;                   // 播放位置修改标志private:QString _windowTitle;
};
#endif // OPENCVPLAYERMANAGER_H

OpenCVPlayerManager.cpp

#include "OpenCVPlayerManager.h"#include <QDebug>OpenCVPlayerManager::OpenCVPlayerManager(QObject *parent): QObject(parent),_pVideoCapture(0),_totalFrames(0),_currentFrame(0),_playState(PLAY_STATE_STOP),_pauseMs(0),_duration(0)
{QString version = "v1.0.0";_windowTitle = QString("OpenCVPlayer %1(红模仿-红胖子 QQ21497936 博客地址: blog.csdn.net/qq21497936").arg(version);
}OpenCVPlayerManager::~OpenCVPlayerManager()
{}bool OpenCVPlayerManager::startPlay(QString filePath, int width, int height)
{// 状态判断switch (_playState){case PLAY_STATE_PAUSE:resume();case PLAY_STATE_PLAYING:return true;break;case PLAY_STATE_STOP:break;default:break;}if(!_pVideoCapture->open(filePath.toStdString())){qDebug() << __FILE__ << __LINE__ << "Failed to start video :" << filePath;return false;}_width = width;_height = height;_pVideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, _width);_pVideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, _height);_width = _pVideoCapture->get(CV_CAP_PROP_FRAME_WIDTH);_height = _pVideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT);_fps = _pVideoCapture->get(CV_CAP_PROP_FPS);_totalFrames = _pVideoCapture->get(CV_CAP_PROP_FRAME_COUNT);qDebug() << __FILE__ << __LINE__ << "width =" << width;qDebug() << __FILE__ << __LINE__ << "height =" << height;qDebug() << __FILE__ << __LINE__ << "_width =" << _width;qDebug() << __FILE__ << __LINE__ << "_height =" << _height;qDebug() << __FILE__ << __LINE__ << "_fps =" << _fps;qDebug() << __FILE__ << __LINE__ << "_totalFrames =" << _totalFrames;_currentFrame = 0;_pauseMs = 0;QTimer::singleShot(0, this, SLOT(slot_captrueFrame()));_elapsedTimer.start();_playState = PLAY_STATE_PLAYING;emit signal_playStateChanged(_playState);_duration = (_totalFrames - 1) * (1000.0f / _fps);emit signal_durationChanged(_duration);return true;
}void OpenCVPlayerManager::pause()
{// 状态判断switch (_playState){case PLAY_STATE_PAUSE:case PLAY_STATE_STOP:return;break;case PLAY_STATE_PLAYING:break;default:break;}_playState = PLAY_STATE_PAUSE;emit signal_playStateChanged(_playState);
}void OpenCVPlayerManager::resume()
{// 状态判断switch (_playState){case PLAY_STATE_PLAYING:case PLAY_STATE_STOP:return;break;case PLAY_STATE_PAUSE:break;default:break;}QTimer::singleShot(0, this, SLOT(slot_captrueFrame()));_elapsedTimer.start();_playState = PLAY_STATE_PLAYING;emit signal_playStateChanged(_playState);
}void OpenCVPlayerManager::stopPlay()
{// 状态判断switch (_playState){case PLAY_STATE_STOP:return;break;case PLAY_STATE_PAUSE:case PLAY_STATE_PLAYING:break;default:break;}QTimer::singleShot(0, this, SLOT(slot_stopPlay()));
}void OpenCVPlayerManager::setPosition(qint64 position)
{QMetaObject::invokeMethod(this, "slot_setPosition", Q_ARG(qint64, position));
}void OpenCVPlayerManager::slot_setPosition(qint64 position)
{// 当前帧数_currentFrame = position / (1000.0f / _fps);_pauseMs = _currentFrame * (1000.0f / _fps);_pVideoCapture->set(CV_CAP_PROP_POS_FRAMES, _currentFrame);_elapsedTimer.start();qDebug() << __FILE__ << __LINE__ << _currentFrame << ":" << _totalFrames << ":" << _pauseMs;
}bool OpenCVPlayerManager::slot_start()
{_pVideoCapture = new cv::VideoCapture;_running = true;
}bool OpenCVPlayerManager::stop()
{if(_running){_running = false;delete _pVideoCapture;_pVideoCapture = 0;}
}void OpenCVPlayerManager::slot_captrueFrame()
{if(!_running){return;}if(_pVideoCapture->isOpened()){// 没有下一帧,表示播放结束if(_currentFrame >= _totalFrames){_playState = PLAY_STATE_STOP;emit signal_playStateChanged(_playState);cv::Mat mat(_height, _width, CV_8UC3, cv::Scalar(0, 0, 0));emit signal_captureOneFrame(mat);return;}cv::Mat mat;*_pVideoCapture >> mat;emit signal_captureOneFrame(mat);if(_currentFrame % _fps == 0){emit signal_positionChanged(_currentFrame * (1000.0f / _fps));}_currentFrame++;int nowTime;switch (_playState){case PLAY_STATE_STOP:case PLAY_STATE_PAUSE:_pauseMs = _currentFrame * (1000.0 / _fps);break;case PLAY_STATE_PLAYING:nowTime = _elapsedTimer.elapsed();if((_currentFrame * (1000.0 / _fps) - (nowTime + _pauseMs) < 0)){QTimer::singleShot(0, this, SLOT(slot_captrueFrame()));}else{QTimer::singleShot(_currentFrame * (1000.0 / _fps) - (nowTime + _pauseMs), this, SLOT(slot_captrueFrame()));}break;default:break;}}
}void OpenCVPlayerManager::slot_stopPlay()
{if(_pVideoCapture->isOpened()){_playState = PLAY_STATE_STOP;_pVideoCapture->release();cv::Mat mat(_width, _height, CV_8UC3, cv::Scalar(0, 0, 0));emit signal_captureOneFrame(mat);_currentFrame = 0;emit signal_positionChanged(0);_duration = 0;emit signal_durationChanged(_duration);}
}QImage OpenCVPlayerManager::cvMat2QImage(const cv::Mat &mat)
{// 8-bits unsigned, NO. OF CHANNELS = 1if(mat.type() == CV_8UC1){QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);// Set the color table (used to translate colour indexes to qRgb values)image.setColorCount(256);for(int i = 0; i < 256; i++){image.setColor(i, qRgb(i, i, i));}// Copy input Matuchar *pSrc = mat.data;for(int row = 0; row < mat.rows; row ++){uchar *pDest = image.scanLine(row);memcpy(pDest, pSrc, mat.cols);pSrc += mat.step;}return image;}// 8-bits unsigned, NO. OF CHANNELS = 3else if(mat.type() == CV_8UC3){// Copy input Matconst uchar *pSrc = (const uchar*)mat.data;// Create QImage with same dimensions as input MatQImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);QImage image2 = image.rgbSwapped();return image2;}else if(mat.type() == CV_8UC4){qDebug() << "CV_8UC4";// Copy input Matconst uchar *pSrc = (const uchar*)mat.data;// Create QImage with same dimensions as input MatQImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
//        return image.copy();return image;}else{qDebug() << "ERROR: Mat could not be converted to QImage.";QImage image;return image;}
}QString OpenCVPlayerManager::getWindowTitle() const
{return _windowTitle;
}void OpenCVPlayerManager::setWindowTitle(const QString &windowTitle)
{_windowTitle = windowTitle;
}

槽点

调整播放位置时,宕机

原因:

直接主线程操作设置当前帧的位置,同时子线程也却在读取,OpenCV内部没有错异步处理。

解决方法:

先调用设置位置,然后槽调用,将设置位置放置到子线程当中去。

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲

在「小白学视觉」公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲

在「小白学视觉」公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~

df541160817dbacf41ec21687d672d3b.png

687d95d62514fc2e987b684aa7f6915f.png


http://lihuaxi.xjx100.cn/news/269945.html

相关文章

My97DatePicker日历控件日报、每周和每月的选择

My97DatePicker日历控件日报、每周和每月的选择 1、设计源代码 <% page language"java" import"java.util.*" pageEncoding"UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html><h…

涨见识了,在终端执行 Python 代码的 6 种方式

作者 | BRETT CANNON译者 | 豌豆花下猫Python猫为了我们推出的 VS Code 的 Python 插件[1]&#xff0c;我写了一个简单的脚本来生成变更日志[2]&#xff08;类似于Towncrier[3]&#xff0c;但简单些&#xff0c;支持 Markdown&#xff0c;符合我们的需求&#xff09;。在发布过…

用python解析word文件(段落篇(paragraph) 表格篇(table) 样式篇(style))

首先需要安装相应的支持库&#xff1a; 直接在命令行执行pip install python-docx 示例代码如下&#xff1a; import docxfrom docx import Document #导入库 path "E:\\python_data\\1234.docx" #文件路径document Document(path) #读入文件tables document.tabl…

java数组出现次数最多的数_找出数组中出现次数最多的那个数——主元素问题...

方法一&#xff1a;以空间换时间&#xff0c;可以定义一个计数数组int count[101]&#xff0c;用来对数组中数字出现的次数进行计数(只能针对数组中数字的范围1~100)&#xff0c;count数组中最大的元素对应的下标&#xff0c;即为出现次数最多的那个数。Java代码如下&#xff1…

make报错:/usr/bin/ld: cannot find -lXXX

在编译php时报错如下&#xff1a; # make 。。。 /usr/bin/ld: cannot find -lltdl collect2: ld returned 1 exit status make: *** [libphp5.la] Error 1 问题原因&#xff1a; 该问题一般是由于ld在进行库的连接时找不到库文件所致&#xff1a; 解决方案&#xff1a; 出现该…

送40本书,人人有份!

亲们&#xff0c;清华大学出版社的送书活动来啦&#xff01;&#xff01;&#xff01;本号联合多名小伙伴一起给大家助力送书&#xff0c;助您早日实现梦想话不多说&#xff0c;咱们进入正题&#xff1a; 怎么送和送什么书&#xff1f;如何送书呢&#xff1f;赠送规则本次【多个…

人有多大胆,GAN有多高产 | AI创作利器演变全过程

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达GAN自从诞生以来&#xff0c;一路升级&#xff0c;功能越来越强。这种强大的方法走过了怎样的进化之路&#xff1f;GAN的诞生和构架GAN诞生在2014年&#xff0c;Ian Goodf…

一个 SpringBoot 项目该包含哪些?

点击上方蓝色“方志朋”&#xff0c;选择“设为星标”回复“666”获取独家整理的学习资料&#xff01;作者&#xff1a;不一样的科技宅 juejin.im/post/6844904083942277127前言建立一个全新的项目&#xff0c;或者把旧的庞大的项目&#xff0c;进行拆分成多个项目。在建立新的…