Python 3.x opencv pyqt视频正常帧速率

Python 3.x opencv pyqt视频正常帧速率,python-3.x,pyqt5,opencv3.0,Python 3.x,Pyqt5,Opencv3.0,我正在用Python3.6创建一个特殊用途的视频播放器,使用OpenCV3和ffmepg处理图像,并在Windows环境中使用PyQt5。我之所以选择这种软件包组合,是因为ffmpeg处理的编解码器比QT多媒体更广泛 我遇到了一个障碍。我的球员不是以正常速度打球,而是以正常速度的¾左右打球。我使用QTimer.timer以1/帧速率的速度循环显示引擎(nextFrameSlot) 关于如何让视频以正常速度播放有什么建议吗?下面是一组简短的代码,演示了我的问题 import sys from Py

我正在用Python3.6创建一个特殊用途的视频播放器,使用OpenCV3和ffmepg处理图像,并在Windows环境中使用PyQt5。我之所以选择这种软件包组合,是因为ffmpeg处理的编解码器比QT多媒体更广泛

我遇到了一个障碍。我的球员不是以正常速度打球,而是以正常速度的¾左右打球。我使用QTimer.timer以1/帧速率的速度循环显示引擎(nextFrameSlot)

关于如何让视频以正常速度播放有什么建议吗?下面是一组简短的代码,演示了我的问题

import sys
from PyQt5.QtWidgets import QWidget, QLabel, QFormLayout, QPushButton, QMainWindow
from PyQt5.QtWidgets import QAction, QMessageBox, QApplication, QFileDialog
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QTimer
import cv2


class VideoCapture(QWidget):
    def __init__(self, filename, parent):
        super(QWidget, self).__init__()
        self.cap = cv2.VideoCapture(str(filename[0]))
        self.length = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
        self.frame_rate = self.cap.get(cv2.CAP_PROP_FPS)
        #self.codec = self.cap.get(cv2.CAP_PROP_FOURCC)
        self.video_frame = QLabel()
        parent.layout.addWidget(self.video_frame)

    def nextFrameSlot(self):
        ret, frame = self.cap.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
        pix = QPixmap.fromImage(img)
        self.video_frame.setPixmap(pix)

    def start(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.nextFrameSlot)
        self.timer.start(1000.0/self.frame_rate)

    def pause(self):
        self.timer.stop()

    def deleteLater(self):
        self.cap.release()
        super(QWidget, self).deleteLater()

class VideoDisplayWidget(QWidget):
    def __init__(self,parent):
        super(VideoDisplayWidget, self).__init__(parent)
        self.layout = QFormLayout(self)
        self.startButton = QPushButton('Start', parent)
        self.startButton.clicked.connect(parent.startCapture)
        self.startButton.setFixedWidth(50)
        self.pauseButton = QPushButton('Pause', parent)
        self.pauseButton.setFixedWidth(50)
        self.layout.addRow(self.startButton, self.pauseButton)
        self.setLayout(self.layout)

class ControlWindow(QMainWindow):
    def __init__(self):
        super(ControlWindow, self).__init__()
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PyTrack")

        self.capture = None

        self.isVideoFileLoaded = False

        self.quitAction = QAction("&Exit", self)
        self.quitAction.setShortcut("Ctrl+Q")
        self.quitAction.triggered.connect(self.closeApplication)

        self.openVideoFile = QAction("&Open Video File", self)
        self.openVideoFile.setShortcut("Ctrl+Shift+V")
        self.openVideoFile.triggered.connect(self.loadVideoFile)

        self.mainMenu = self.menuBar()
        self.fileMenu = self.mainMenu.addMenu('&File')
        self.fileMenu.addAction(self.openVideoFile)
        self.fileMenu.addAction(self.quitAction)

        self.videoDisplayWidget = VideoDisplayWidget(self)
        self.setCentralWidget(self.videoDisplayWidget)

    def startCapture(self):
        if not self.capture and self.isVideoFileLoaded:
            self.capture = VideoCapture(self.videoFileName, self.videoDisplayWidget)
            self.videoDisplayWidget.pauseButton.clicked.connect(self.capture.pause)
        self.capture.start()

    def endCapture(self):
        self.capture.deleteLater()
        self.capture = None

    def loadVideoFile(self):
        try:
            self.videoFileName = QFileDialog.getOpenFileName(self, 'Select a Video File')
            self.isVideoFileLoaded = True
        except:
            print ("Please Select a Video File")

    def closeApplication(self):
        choice = QMessageBox.question(self, 'Message','Do you really want to exit?',QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            print("Closing....")
            sys.exit()
        else:
            pass

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ControlWindow()
    window.show()
    sys.exit(app.exec_())

已解决-我需要在函数
start(self)
中的语句
self.timer=QTimer()
之后指定
self.timer.settimer类型(Qt.PreciseTimer)
。默认情况下,QTimer()使用粗略计时器。对于Windows,粗略时间间隔为15.6毫秒。

您分析过运行
nextFrameSlot()
需要多长时间吗?MP4文件,1280x720,avc1编解码器,29.97 fps:avg=12.6毫秒,stdev=9毫秒。AVI文件,640x480,XVID编解码器,30 fps:avg=3ms,stdev=2毫秒。更新:上面引用的那些avg和stdev编号仅用于下一帧插槽。当QTimer.timer所需的额外时间将整个循环时间增加到约47毫秒时,这超过了所需的33毫秒。除了QTimer.timer之外,还有没有更快的方法调用调用nextFrameSlot?我很惊讶计时器中有这么多开销。您可以尝试使用
QCoreApplication.postEvent()
Solved将高优先级直接注入事件循环-我需要指定
self.timer.setTimerType(Qt.PreciseTimer)
。默认情况下,QTimer()使用粗计时器。对于Windows,粗略时间间隔为15.6毫秒。