[英]How can I render a mutable video to a PyQt widget?
I am working on a video editing application in python using PyQt.我正在使用 PyQt 在 python 中开发视频编辑应用程序。 I want to be able to make "clips" out of video files and have the ability to concatenate these clips to render a new video.
我希望能够从视频文件中制作“剪辑”,并能够连接这些剪辑以呈现新视频。 I have used the QMediaPlayer class with a VideoSurface to play back a video that has instantiated a QVideoWidget with a .mp4 file, but I want to play back a video using a numpy array (preferred) or some mutable object.
我已经使用带有 VideoSurface 的 QMediaPlayer 类来播放使用 .mp4 文件实例化 QVideoWidget 的视频,但我想使用 numpy 数组(首选)或某些可变对象播放视频。 I have looked through some open source video editors (OpenShot, Vidcutter, Pitivi) but I cannot seem to find what I need.
我浏览了一些开源视频编辑器(OpenShot、Vidcutter、Pitivi),但似乎找不到我需要的东西。 Many use C++ frameworks, with which I am unfamiliar.
许多人使用我不熟悉的 C++ 框架。
I have used multithreading and for-loops to try to hack my way through a solution by using a QImage object, meaning I loop through a video frame by frame, extracting the numpy array representation of each frame and converting it a QImage object and calling repaint().我已经使用多线程和 for 循环来尝试通过使用 QImage 对象来破解我的解决方案,这意味着我逐帧循环播放视频,提取每帧的 numpy 数组表示并将其转换为 QImage 对象并调用 repaint ()。 Unfortunately, even with calling this function on a new thread, this does not render at the desired speed.
不幸的是,即使在新线程上调用此函数,也不会以所需的速度呈现。 This was inspired by moviepy's method of rendering clips with pygame.
这受到了moviepy使用pygame渲染剪辑的方法的启发。 I have also looked through PyQt documentation, but these classes do not seem to meet my needs or I do not understand them.
我还查看了 PyQt 文档,但这些类似乎不能满足我的需求或者我不理解它们。 Graphical User Interface programming is new to me.
图形用户界面编程对我来说是新的。
I know that fps is not the issue, and if you run the code below with an updated VideoFileCLip parameter, the video will display (without sound) at approximately 75% of the original frame rate.我知道 fps 不是问题,如果您使用更新的 VideoFileCLip 参数运行下面的代码,视频将以原始帧速率的大约 75% 显示(没有声音)。 And, the window will "not respond" if there is no multithreading.
并且,如果没有多线程,窗口将“不响应”。 I have tried fps of 30 and 60, but still my for-loop method is not desired because other tasks will be performed elsewhere, and the computational complexity will only increase.
我已经尝试过30和60的fps,但仍然不希望我的for循环方法,因为其他任务将在其他地方执行,并且计算复杂度只会增加。
Here is a simplified version that will reveal the issue:这是一个可以揭示问题的简化版本:
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()
This problem also extends to mutable audio data as well.这个问题也扩展到可变音频数据。
Try to do it with QPixmaps.尝试使用 QPixmaps 来实现。 Before you show the frames save them into a list:
在显示框架之前,将它们保存到列表中:
self.frames = []
for file in files:
pixmap = QPixmap(file)
self.frames.append(pixmap)
and than, when you play the image sequenze back:然后,当您播放图像序列时:
for frame in self.frames:
self.<LABELNAME>.setPixmap(frame)
time.sleep(1/FPS)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.