python-PyQt项目实战案例:制作一个视频播放器

文章目录

    • 1. 关键问题描述
    • 2. 通过OpenCV读取视频/打开摄像头抓取视频
    • 3. 通过PyQt 中的 QTimer定时器实现视频播放
    • 4. PyQt 视频播放器实现代码
    • 参考文献

1. 关键问题描述

在前面的文章中已经分享了pyqt制作图像处理工具的文章,也知道pyqt通过使用label控件显示图像的方式。在此,对于视频的显示,其本质上一帧一帧的图像,因此也可以使用同样的方式对其显示。但是,有两个关键的问题需要解决,也即:
a. 如何读取视频/或通过摄像头抓取视频
b. 如果连续显示视频图像且不会造成界面假死

2. 通过OpenCV读取视频/打开摄像头抓取视频

主要函数:

cv.VideoCapture.isOpened(),检查视频捕获是否初始化成功
cv.VideoCapture.read(),捕获视频文件、视频流或捕获的视频设备
cv.VideoCapture.release(),关闭视频文件或设备,释放对象
cv.VideoWriter.release(),关闭视频写入,释放对象

抓取视频

import cv2
import timecap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # 使用cv2.CAP_DSHOW后启动快,但帧率慢了
cap.set(6, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G'))
# cap.set(6, cv2.VideoWriter.fourcc('Y', 'U', 'V', '2'))# 使用cv2.CAP_DSHOW后大小为640
cap.set(3, 2560)  # 640 1280 1920  2560# 宽
# cap.set(4, 720)   # 720  1080  # 高
# cap.set(5, 60)  # 帧数
# cap.set(10, 150)  # 亮度
# cap.set(14, 150)  # 增益
# cap.set(11, 90)  # 对比度
# cap.set(15, 150)  # 曝光print(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
print(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(cap.get(cv2.CAP_PROP_FPS))num = 0
start = time.time()
fps = '0'
while(True):ret, frame = cap.read()num = num + 1frame = cv2.flip(frame, 1)# frame = beauty_face(frame)if (time.time() - start) > 1:fps = num / (time.time() - start)fps = str(round(fps, 2))num = 0start = time.time()cv2.putText(frame, "FPS:" + fps, (20, 20), 1, 1.5, (255, 255, 255), 2)cv2.imshow("DST", frame)stop = time.time()elapsed = stop - start# print('time=', elapsed)if 1 < elapsed < 1.5:print('time=', elapsed)print('frame num:', num)if cv2.waitKey(1) & 0xFF == ord('q'):break
cap.release()
cv2.destroyAllWindows()

读取视频

import cv2 as cvif __name__ == '__main__':# 创建视频读取/捕获对象vedioRead = "../images/test.mp4"  # 读取视频文件的路径capRead = cv.VideoCapture(vedioRead)  # 实例化 VideoCapture 类# 读取视频文件frameNum = 0  # 视频帧数初值while capRead.isOpened():  # 检查视频捕获是否成功ret, frame = capRead.read()  # 读取下一帧视频图像if ret is True:cv.imshow(vedioRead, frame)  # 播放视频图像if cv.waitKey(1) & 0xFF == ord('q'):  # 按 'q' 退出breakelse:print("Can't receive frame at frameNum {}".format(frameNum))breakcapRead.release()  # 关闭读取视频文件capWrite.release()  # 关闭视频写入对象cv.destroyAllWindows()  # 关闭显示窗口

3. 通过PyQt 中的 QTimer定时器实现视频播放

使用 OpenCV 对视频文件进行解码获得图像帧以后,可以使用 QTime 定时器来控制 QLabel 控件中的图像更新,实现视频播放。
PyQt5 中的 QTimer类提供了重复的和单次的定时器,为计时器提供了高级编程接口。
要使用定时器,需要先创建一个QTimer实例,将定时器的timeout信号连接到相应的槽函数,并调用start(),定时器就会以设定的间隔发出timeout信号。

QTimer类中的常用方法:

start(milliseconds):启动或重新启动定时器,时间间隔为毫秒。如果定时器已经运行,它将被停止并重新启动。如果singleShot信号为真,定时器将仅被激活一次。
stop():停止定时器

QTimer类中的常用信号:
singleShot:在给定的时间间隔后调用一个槽函数时发射此信号。
timeout:当定时器超时时发射此信号。

注意:可以设置槽函数的执行次数,默认为定时器开启后周期性调用槽函数。如果设置了setSingleShot(True),则槽函数仅执行一次。

关键代码:

# 初始化定时器,并绑定触发信号
self.timerCam = QtCore.QTimer()  # 定时器,毫秒
self.timerCam.timeout.connect(self.refreshFrame)  # 计时器结束时调用槽函数刷新当前帧# 开始定时
if self.cap.isOpened():      # 检查视频捕获是否成功self.timerCam.start(20)  # 设置计时间隔并启动,定时结束将触发刷新当前帧
# 暂停播放
self.timerCam.blockSignals(True)  # 信号阻塞,暂停定时器
# 继续播放
self.timerCam.blockSignals(False)  # 取消信号阻塞,恢复定时器

4. PyQt 视频播放器实现代码

import os
import sys
import cv2 as cv
import numpy as np
from PyQt5 import QtCore
from PyQt5.QtCore import QObject, pyqtSignal, QPoint, QRect, qDebug, Qt
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from ui_VideoProV1 import Ui_MainWindow  # 导入 uiDemo8.py 中的 Ui_MainWindow 界面类class MyMainWindow(QMainWindow, Ui_MainWindow):  # 继承 QMainWindow 类和 Ui_MainWindow 界面类def __init__(self, parent=None):super(MyMainWindow, self).__init__(parent)  # 初始化父类self.setupUi(self)  # 继承 Ui_MainWindow 界面类self.timerCam = QtCore.QTimer()  # 定时器,毫秒self.cap = Noneself.frameNum = 1  # 视频帧数初值self.edge_flag = Falseself.smooth_flag = False# 添加可点击执行的菜单self.menu_file = QMenu("文件", self)temp = self.menuBar()temp.addMenu(self.menu_file)t = self.menu_fileself.action_open = QAction("打开", self)self.action_open.triggered.connect(self.openVideo)t.addAction(self.action_open)self.action_save = QAction("保存", self)t.addAction(self.action_save)t.addAction('其他')self.mTest = QAction("帮助", self)self.mTest.triggered.connect(self.trigger_actHelp)temp = self.menuBar()temp.addAction(self.mTest)self.action_close = QAction("退出", self)self.action_close.triggered.connect(self.close)temp = self.menuBar()temp.addAction(self.action_close)## # 菜单栏self.action_save.triggered.connect(self.saveSlot)  # 连接并执行 openSlot 子程序# 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButtonself.pushButton.clicked.connect(self.openVideo)self.pushButton_2.clicked.connect(self.playVideo)self.pushButton_3.clicked.connect(self.pauseVideo)self.pushButton_4.clicked.connect(self.click_pushButton_5)  # # 按钮触发:边缘检测self.pushButton_5.clicked.connect(self.click_pushButton_6)  # 点击 # 按钮触发:双边self.timerCam.timeout.connect(self.refreshFrame)  # 计时器结束时调用槽函数刷新当前帧# 初始化self.frame = np.ndarray(())self.videoPath = 'test'self.textEdit_log.append("欢迎回来!")returndef openVideo(self):  # 读取视频文件,点击 pushButton_1 触发try:self.videoPath, _ = QFileDialog.getOpenFileName(self, "Open Video", "../images/", "*.mp4 *.avi *.flv")print("Open Video: ", self.videoPath)except:print("Open video failed.")returndef playVideo(self):  # 播放视频文件,点击 pushButton_2 触发if self.timerCam.isActive() == False:if self.videoPath.endswith(('avi', 'mp4')):self.cap = cv.VideoCapture(self.videoPath)self.textEdit_log.append('open video successfully')else:self.cap = cv.VideoCapture(0)self.textEdit_log.append('open camera successfully')if self.cap.isOpened():      # 检查视频捕获是否成功self.timerCam.start(20)  # 设置计时间隔并启动,定时结束将触发刷新当前帧else:  #self.timerCam.stop()  # 停止定时器self.cap.release()    # 关闭读取视频文件self.label_1.clear()  # 清除显示内容returndef pauseVideo(self):self.timerCam.blockSignals(False)  # 取消信号阻塞,恢复定时器if self.timerCam.isActive() and self.frameNum % 2 == 1:self.timerCam.blockSignals(True)  # 信号阻塞,暂停定时器self.pushButton_3.setText("继续")  # 点击"继续",恢复播放print("信号阻塞,暂停播放。", self.frameNum)self.textEdit_log.append("信号阻塞,暂停播放。" + str(self.frameNum))else:self.pushButton_3.setText("暂停")  # 点击"暂停",暂停播放print("取消阻塞,恢复播放。", self.frameNum)self.textEdit_log.append("取消阻塞,恢复播放。" + str(self.frameNum))self.frameNum = self.frameNum + 1def refreshFrame(self):  # 刷新视频图像ret, self.frame = self.cap.read()  # 读取下一帧视频图像self.frame = cv.flip(self.frame, 1)if self.smooth_flag:self.frame = cv.bilateralFilter(self.frame, 15, 30, 40, None)if self.edge_flag:# self.frame = cv.Canny(self.frame, 10, 60)self.frame = self.apply_heat_effect(self.frame)qImg = self.cvToQImage(self.frame)  # OpenCV 转为 PyQt 图像格式self.label_1.setScaledContents(True)self.label_1.setPixmap((QPixmap.fromImage(qImg)))  # 加载 PyQt 图像# self.frameNum = self.frameNum + 1# print("视频帧数:", self.frameNum)returndef cvToQImage(self, image):if image.dtype == np.uint8:channels = 1 if len(image.shape) == 2 else image.shape[2]if channels == 3:qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)return qImg.rgbSwapped()elif channels == 1:qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)return qImgelse:QtCore.qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])return QImage()def click_pushButton_5(self):if self.edge_flag:self.edge_flag = Falseself.textEdit_log.append('close edge mode successfully')else:self.edge_flag = Trueself.textEdit_log.append('open edge mode successfully')returndef click_pushButton_6(self):if self.smooth_flag:self.smooth_flag = Falseself.textEdit_log.append('close smooth mode successfully')else:self.smooth_flag = Trueself.textEdit_log.append('open smooth mode successfully')returndef saveSlot(self):  # 保存图像文件# 选择存储文件 dialogtry:fileName, tmp = QFileDialog.getSaveFileName(self, "Save Image", "../images/", '*.png; *.jpg; *.tif')if self.frame.size == 1:return# OpenCV 写入图像文件ret = cv.imwrite(fileName, self.frame)if ret:print(fileName, self.frame.shape)except:print('save failed.')returndef cvToQImage(self, image):# 8-bits unsigned, NO. OF CHANNELS=1if image.dtype == np.uint8:channels = 1 if len(image.shape) == 2 else image.shape[2]if channels == 3:  # CV_8UC3# Create QImage with same dimensions as input MatqImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)return qImg.rgbSwapped()elif channels == 1:# Create QImage with same dimensions as input MatqImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)return qImgelse:qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])return QImage()def qPixmapToCV(self, qPixmap):  # PyQt图像 转换为 OpenCV图像qImg = qPixmap.toImage()  # QPixmap 转换为 QImageshape = (qImg.height(), qImg.bytesPerLine() * 8 // qImg.depth())shape += (4,)ptr = qImg.bits()ptr.setsize(qImg.byteCount())image = np.array(ptr, dtype=np.uint8).reshape(shape)  # 定义 OpenCV 图像image = image[..., :3]return imagedef trigger_actHelp(self):  # 动作 actHelp 触发QMessageBox.about(self, "About", """打开视频或摄像头 v1.0""")returndef closeEvent(self):self.timerCam.stop()  # 停止定时器self.cap.release()  # 关闭读取视频文件self.label_1.clear()  # 清除显示内容if __name__ == '__main__':app = QApplication(sys.argv)myWin = MyMainWindow()myWin.show()sys.exit(app.exec_())

在这里插入图片描述

参考文献

[1] OpenCV-PyQT项目实战(9)项目案例04:视频播放

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/456579.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 初阶——多版本控制 MVCC

一、版本链&#xff08;undo 日志&#xff09; a. 什么是版本链 版本链就是一条以事务为节点的单链表。其 next 指针指向前一个版本的事务。 b. 版本链的增删 当一个事务被完成时&#xff0c;这个事务就会被加入到版本链里去&#xff1b;当要回滚时&#xff0c;版本链就会删…

微前端架构新选择:micro-app 框架一文全解析

目录 前言技术方案沙箱withiframe 环境变量主应用生命周期子应用生命周期初始化更新卸载缓存 JS 沙箱样式隔离元素隔离路由系统⭐数据通信⭐资源系统预加载umd 模式其他功能调试工具 前言 https://micro-zoe.github.io/micro-app/ micro-app 是由京东前端团队推出的一款微前端…

400V交流智能剩余电流监测系统设计与应用

摘要&#xff1a;针对变电站400V交流系统频繁发生剩余电流保护器跳闸的问题&#xff0c;本研究设计了一套智能化的分布式剩余电流监测系统。该系统利用CT传感器采集400V系统各负载端的剩余电流数据&#xff0c;经过运算处理后&#xff0c;将信息传递给交流绝缘监测装置。随后&a…

重生之“我打数据结构,真的假的?”--3.栈和队列(无习题)

栈和队列 C语言中的栈和队列总结 在C语言中&#xff0c;**栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;**是两种非常重要的数据结构。它们广泛用于各种应用中&#xff0c;比如内存管理、任务调度、表达式求值等。本文将对这两种数据结构进行详细的介…

基于Multisim的四人智力竞赛抢答器设计与仿真

1&#xff09;设计任务 设计一台可供 4 名选手参加比赛的智力竞赛抢答器。 用数字显示抢答倒计时间&#xff0c;由“9”倒计到“0”时&#xff0c;无人抢答&#xff0c;蜂鸣器连续响 1 秒。选手抢答时&#xff0c;数码显示选手组号&#xff0c;同时蜂鸣器响 1 秒&#xff0c;倒…

基于Python+SQL Server2008实现(GUI)快递管理系统

快递业务管理系统的设计与实现 摘要: 着网络新零售的到来&#xff0c;传统物流在网购的洗礼下迅速蜕变&#xff0c;在这场以互联网为基础的时代变革中&#xff0c;哪家企业能率先转变其工作模式就能最先分得一杯羹&#xff0c;物流管理也不例外。传统的物流管理模式效率低下&a…

金融工程--pine-script 入门

背景 脚本基本组成 策略实现 实现马丁格尔策略 初始化变量&#xff1a;定义初始资本、初始头寸大小、止损百分比、止盈百分比以及当前资本和当前头寸大小等变量。 更新头寸&#xff1a;创建一个函数来更新头寸大小、止损价格和止盈价格。在马丁格尔策略中&#xff0c;每次亏…

micro-app【微前端实战】主应用 vue3 + vite 子应用 vue3+vite

micro-app 官方文档为 https://micro-zoe.github.io/micro-app/docs.html#/zh-cn/framework/vite 子应用 无需任何修改&#xff0c;直接启动子应用即可。 主应用 1. 安装微前端框架 microApp npm i micro-zoe/micro-app --save2. 导入并启用微前端框架 microApp src/main.ts …

【Ubuntu】Virtualbox下lamp集群分布式搭建Wordpress

WordPress是一种使用PHP语言开发的开源内容管理系统&#xff08;CMS&#xff09;&#xff0c;也常被用作博客平台。 开发语言&#xff1a;PHP 数据库&#xff1a;MySQL、mariadb&#xff08;或其他兼容的数据库系统&#xff09; 授权方式&#xff1a;GNU通用公共许可证下发布&a…

【JavaEE】【多线程】单例模式

目录 一、设计模式1.1 单例模式1.1.1 饿汉模式1.1.2 懒汉模式 1.2 线程安全问题1.3 懒汉模式线程安全问题的解决方法1.3.1 原子性问题解决1.3.2 解决效率问题1.3.3 解决内存可见性问题和指令重排序问题 一、设计模式 在讲解案例前&#xff0c;先介绍一个概念设计模式&#xff…

C++ 模版和继承

目录 一.模版 1.模版的基本概念 a.函数模版 b.类模板 c.模版的实例化 d.class 和 typename的区别 e.非类型模版参数 2.模版的特化 a.全特化 —— 参数类型全部特殊化处理 b.半特化 —— 部分参数特殊化处理 c.偏特化——对某些类型的进一步限制&#xff08;实例化时传…

GD32学习知识点累计

时钟系统 GD32f427主频最高位240MHZ&#xff08;但是只能到200M&#xff09;&#xff0c;GD32给的函数外接25MHZ晶振配置主频为200MHZ,APB1最高频率为60HZ配置为主频的4分频为50MHZ&#xff0c;APB2最大为120MHZ配置为主频的2分频为100MHZ 定时器 无论什么定时器最大频率为200M…

安全见闻---清风

注&#xff1a;本文章源于泷羽SEC&#xff0c;如有侵权请联系我&#xff0c;违规必删 学习请认准泷羽SEC学习视频:https://space.bilibili.com/350329294 安全见闻1 泷哥语录&#xff1a;安全领域什么都有&#xff0c;不要被表象所迷惑&#xff0c;无论技术也好还是其他方面…

AI带货主播框架的搭建!

AI带货主播&#xff0c;作为新兴的技术应用&#xff0c;正在逐渐改变电商行业的面貌&#xff0c;通过利用人工智能技术&#xff0c;AI带货主播能够模拟真实主播的行为&#xff0c;与用户进行互动&#xff0c;推荐商品&#xff0c;提升购物体验。 本文将介绍如何搭建一个AI带货…

处理Hutool的Http工具上传大文件报OOM

程序环境 JDK版本&#xff1a; 1.8Hutool版本&#xff1a; 5.8.25 问题描述 客服端文件上传主要代码&#xff1a; HttpRequest httpRequest HttpUtil.createPost(FILE_UPLOAD_URL); Resource urlResource new UrlResource(url, fileName); httpRequest.form("file&q…

self-supervised learning(BERT和GPT)

1芝麻街与NLP模型 我們接下來要講的主題呢叫做Self-Supervised Learning&#xff0c;在講self-supervised learning之前呢&#xff0c;就不能不介紹一下芝麻街&#xff0c;為什麼呢因為不知道為什麼self-supervised learning的模型都是以芝麻街的人物命名。 因為Bert是一個非常…

实战-任意文件下载

实战-任意文件下载 1、开局 开局一个弱口令&#xff0c;正常来讲我们一般是弱口令或者sql&#xff0c;或者未授权 那么这次运气比较好&#xff0c;直接弱口令进去了 直接访问看看有没有功能点&#xff0c;正常做测试我们一定要先找功能点 发现一个文件上传点&#xff0c;不…

中酱集团:黑松露酱油,天然配方定义健康生活

在如今的大健康时代&#xff0c;人们对于美食的要求越来越高。不仅美味&#xff0c;更要健康。在健康美食的生态链中&#xff0c;有一个名字正逐渐成为品质与美味的代名词——中酱集团。而当中酱集团与黑松露酱油相遇&#xff0c;一场味觉的革命就此拉开帷幕。 中酱集团&#x…

【产品应用】旋转式贴标机一站式解决方案

针对贴标机行业设备&#xff0c;立迈胜公司拥有智能控制器、一体化伺服电机、减速机等系列产品&#xff0c;可以轻松解决传统电机接线不便、占用空间、自重过大、部件繁杂等问题&#xff0c;帮助贴标机制造商实现设备精准控制、运行稳定的同时&#xff0c;保证生产流程高效产出…

开发运维警示录-20241024

开发警示录 1、作为开发&#xff0c;不要私自修改业务人员给的SQL语句&#xff0c;虽然个人感觉SQL很冗余&#xff0c;效率低等。 2、开发前&#xff0c;要明确需求&#xff0c;必要时通过图和文字形成文档与需求方确认、留痕。 3、开发复杂的业务逻辑代码前&#xff0c;先疏通…