【问题标题】:How can I render a mutable video to a PyQt widget?如何将可变视频渲染到 PyQt 小部件?
【发布时间】:2021-01-20 23:10:25
【问题描述】:

我正在使用 PyQt 在 python 中开发视频编辑应用程序。我希望能够从视频文件中制作“剪辑”,并能够连接这些剪辑以呈现新视频。我已经使用带有 VideoSurface 的 QMediaPlayer 类来播放使用 .mp4 文件实例化 QVideoWidget 的视频,但我想使用 numpy 数组(首选)或一些可变对象来播放视频。我浏览了一些开源视频编辑器(OpenShot、Vidcutter、Pitivi),但似乎找不到我需要的东西。许多人使用我不熟悉的 C++ 框架。

我使用多线程和 for 循环尝试通过使用 QImage 对象来破解我的解决方案,这意味着我逐帧循环播放视频,提取每个帧的 numpy 数组表示并将其转换为 QImage 对象并调用重绘()。不幸的是,即使在新线程上调用此函数,它也不会以所需的速度呈现。这是受moviepy 使用pygame 渲染剪辑的方法的启发。我也查看了 PyQt 文档,但这些类似乎不能满足我的需求,或者我不理解它们。图形用户界面编程对我来说是新的。

我知道 fps 不是问题,如果您使用更新的 VideoFileCLip 参数运行下面的代码,视频将以原始帧速率的大约 75% 显示(无声音)。而且,如果没有多线程,窗口将“不响应”。我尝试了 30 和 60 的 fps,但我的 for-loop 方法仍然不理想,因为其他任务将在其他地方执行,并且计算复杂度只会增加。

这是一个简化版本,可以揭示问题:

import numpy as np                                                     
import sys
import time
from PyQt5.QtGui import QImage, QPainter
from PyQt5.QtWidgets import QApplication, QWidget
import threading
from moviepy.editor import VideoFileClip

class Demo(QWidget):
    def __init__(self):
        super().__init__()
        self.video = VideoFileClip(r'C:\Users\jklew\Videos\Music\Fractalia.MP4') # I am using a real video
        im_np = self.video.get_frame(0)
        self.image = QImage(im_np, im_np.shape[1], im_np.shape[0],                                                                                                                                                 
                        QImage.Format_RGB888)
        self.stopEvent = threading.Event()
        self.thread = threading.Thread(target=self.display_clip, args=())
        self.thread.start()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawImage(self.rect(), self.image)

    def display_clip(self, fps=60):
        clip = self.video
        img = clip.get_frame(0) # returns numpy array of frame at time 0

        t0 = time.time()

        for t in np.arange(1.0 / fps, clip.duration-.001, 1.0 / fps):
            
            img = clip.get_frame(t) # returns numpy array of frame at time t
            # print(img.shape)
            t1 = time.time()
            time.sleep(max(0, t - (t1-t0))) # loop at framerate specified
            self.imdisplay(img) #, screen)
    
    def imdisplay(self, img_array):
        # fill the widget with the image array
        # TODO: Qt widget
        self.image = QImage(img_array, img_array.shape[1], img_array.shape[0], QImage.Format_RGB888)
        self.repaint()

def main():
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

这个问题也延伸到可变音频数据。

【问题讨论】:

  • 通常不鼓励使用 python 进行实时音频/视频处理,因为 python 的速度肯定不为人所知。除非您找到一种通过外部库进行整个处理的方法(或者您能够用 C++ 编写核心实现),否则我不建议您走得更远,尤其是因为您说过您'是 GUI 编程新手。

标签: python user-interface pyqt rendering video-editing


【解决方案1】:

尝试使用 QPixmaps 来实现。在显示框架之前,请将它们保存到列表中:

self.frames = []
for file in files:
  pixmap = QPixmap(file)
  self.frames.append(pixmap)

然后,当您播放图像序列时:

for frame in self.frames:
            self.<LABELNAME>.setPixmap(frame)
            time.sleep(1/FPS)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多