使用 OpenCV 和 PyQT5 视频播放速度过快

Posted

技术标签:

【中文标题】使用 OpenCV 和 PyQT5 视频播放速度过快【英文标题】:Videos are playing too fast using OpenCV and PyQT5 【发布时间】:2021-04-09 13:21:37 【问题描述】:

当在QListWidget 中选择时,我的 GUI 能够自动播放视频。但是,视频播放速度不是正常速度,而是非常快。我以 720p Mp4 视频为例,放在某个文件夹中。我尝试使用cv2.CAP_PROP_FPScv2.CAP_PROP_BUFFERSIZE,但它们都不起作用。我在QThreadconvert_cv_qt 函数中使用了pyqtSignal,我在其他指南中看到了这些函数。 如何以正常速度/帧速率播放视频?

import os
import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtMultimedia import QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread


class VideoThread(QThread):
    change_pixmap_signal = pyqtSignal(np.ndarray)

    def __init__(self, parent=None):
        super(VideoThread, self).__init__(parent)
        self.get_file_dir = self.parent().file_dir #Get the file_dir from UI_MainWindow

    def run(self):
        cap = cv2.VideoCapture(self.get_file_dir)
        
        while True:
            ret, cv_img = cap.read()
            if ret:
                self.change_pixmap_signal.emit(cv_img)
        cap.release()


class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Ui_MainWindow, self).__init__(parent)
        self.setupUI()
    
    def setupUI(self):
        MainWindow.resize(1720, 863)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
        self.tabWidget.setTabShape(QtWidgets.QTabWidget.Triangular)
        self.tabWidget.setElideMode(QtCore.Qt.ElideNone)
        self.tabWidget.setTabsClosable(False)
        self.tabWidget.setMovable(True)
        self.tab = QtWidgets.QWidget()
        self.videoimage = QtWidgets.QLabel(self.tab)
        self.videoimage.setGeometry(QtCore.QRect(0, 10, 1280, 720))
        self.listWidget = QtWidgets.QListWidget(self.tab)
        self.listWidget.setGeometry(QtCore.QRect(1290, 60, 391, 241))
        self.listWidget.setObjectName("listWidget")
        self.listWidget.itemClicked.connect(self.itemclick)
        self.tabWidget.addTab(self.tab, "")
        self.tabWidget.setTabText(0, "Testing")
        self.verticalLayout_2.addWidget(self.tabWidget)
        MainWindow.setCentralWidget(self.centralwidget)

        self.listvideos()
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    @pyqtSlot(np.ndarray)
    def listvideos(self):
        file_extension = ['avi','mp4','mov']
        arr = os.listdir('D:\\Files\\VSCODE\\APPS\\HDTS\\videos')
        for i in arr:
            for fe in file_extension:
                    if i[-3:] == fe:
                        item = QtWidgets.QListWidgetItem(str(i))
                        self.listWidget.addItem(item)

    def itemclick(self):
        try:
          self.thread.terminate()
        except:
          print("Nothing to terminate!")

        item = self.listWidget.currentItem()
        self.file_dir = 'D:\\Files\\VSCODE\\APPS\\HDTS\\videos\\' + item.text()
        print(self.file_dir)        
        self.thread = VideoThread(self)
        self.thread.change_pixmap_signal.connect(self.update_image)
        self.thread.start()
    
    
    def update_image(self, cv_img):
        qt_img = self.convert_cv_qt(cv_img)
        self.videoimage.setPixmap(qt_img)
    
    def convert_cv_qt(self, cv_img):
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        p = convert_to_Qt_format.scaled(1280, 720, Qt.KeepAspectRatio)
        return QtGui.QPixmap.fromImage(p)
    
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    MainWindow.show()
    sys.exit(app.exec_())

【问题讨论】:

请考虑您对setupUi 的实现不是很好,最重要的是因为它在全局 MainWindow 上运行(使用全局变量通常被认为是不好的做法,导致由于实施不佳),而且还因为它可能会导致类和实例出现问题。真的,不要那样做。 Ui_MainWindow 已经是 QMainWindow 的子类,因此您应该使用它,而不创建另一个 QMainWindow 实例;然后将setupUi中对MainWindow的所有引用更改为self 在我学习的过程中,我会尽力听从您的建议。谢谢。 【参考方案1】:

当您将 VideoCapture 与文件一起使用时,如果您调用“cap.read()”,您将获得视频的下一帧,而不管其实际帧速率如何。因此,每次捕获帧时都应该使用“msleep”:

def run(self):
    cap = cv2.VideoCapture(self.get_file_dir)
    
    while True:
        ret, cv_img = cap.read()
        if ret:
            self.change_pixmap_signal.emit(cv_img)
        self.msleep(1000//30) # You can change 30 with 60 if you need 60 fps.
    cap.release()

【讨论】:

我尝试了相同的缩进,但它产生了错误:TypeError: arguments did not match any overloaded call: wait(self, msecs: int = ULONG_MAX): argument 1 has unexpected type 'float' wait(self, QDeadlineTimer): argument 1 has unexpected type 'float' @gentales17 我编辑了我的答案,试试这个 它仍然播放得太快。我尝试了30-1000,根本没有任何变化。 self.wait(1000//30)self.wait(1000//60)self.wait(1000//100)self.wait(1000//1000) 我不确定那是什么。我只是将值从 30- 更改为 60(如您所建议的),甚至高达 1000,但根本没有任何变化。我也尝试改变缩进,但它也没有工作。我的代码有问题吗? @gentales17 我又编辑了一次,试试这个版本。我认为在这种情况下使用“等待”不是正确的方法。

以上是关于使用 OpenCV 和 PyQT5 视频播放速度过快的主要内容,如果未能解决你的问题,请参考以下文章

Python-pyqt5+opencv视频播放器,上传本地视频

在树莓派上用PyQt5写GUI出现视频卡死

Pyqt5:使用Qlabel标签进行视频播放

PyQt5 - 在新窗口中打开 QMediaplayer 并播放视频

pyqt5 qwebenginview 不会自动播放 youtube 视频

PyQt5 使用指定大小窗口播放视频