首页 > 解决方案 > OpenCV视频播放:重新打开视频时不显示图像

问题描述

我在使用 Python (3.8.6) Tkinter 和 OpenCV 时遇到了一个奇怪的问题。我正在尝试循环播放视频,直到选择另一个视频。

当我第一次打开视频时,它会正常开始播放。然后当我打开另一个视频(相同的视频或其他视频)时,第一个视频停止,但第二个视频的图像不显示(它似乎在后台播放)。奇怪的是,当 tkinter 文件对话框显示打开另一个视频时,它确实显示了视频。但是当对话框被取消时,显示再次停止。

如果我手动关闭播放('p')并重新打开视频('o'),它可以正常工作。如果视频在打开新视频之前完成(没有循环播放),它也可以正常工作。

import os
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
import cv2

VIDEO_RATE = int(1000.0/30.0)

class VideoPlayback:
    def __init__(self):
        self.cv2Video = None

    def __del__(self):
        if self.cv2Video and self.cv2Video.isOpened():
            self.cv2Video.release()

    def openVideo(self, src, width=405, height=720):
        self.cv2Video = cv2.VideoCapture(src)
        if not self.cv2Video.isOpened():
            raise ValueError("Unable to open video source", src)
        self.resizeDim = (width,height)

    def closeVideo(self):
        if self.cv2Video:
            self.cv2Video.release()

    def readVideoFrame(self, retry=5):
        if self.cv2Video and self.cv2Video.isOpened():
            ret, frame = self.cv2Video.read()
            if ret:
                return (ret, cv2.cvtColor(cv2.resize(frame, self.resizeDim), cv2.COLOR_BGR2RGB))
            else:
                if retry:
                    self.cv2Video.set(cv2.CAP_PROP_POS_FRAMES, 0)
                    return self.readVideoFrame(retry=retry-1)
                else:
                    return (False, None)
        else:
            return (False, None)

class MainWindow(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self,"Minimal working example")

        self.geometry("1280x740+2+2")
        self.attributes("-fullscreen",True)

        frTop = tk.Frame(master=self, width=1280, height=720)
        frTop.pack(side='top', fill='both', expand=1)

        # Playback canvas
        self.cnvVideo = tk.Canvas(master=frTop, width=405, height=720)
        self.cnvVideoImg = self.cnvVideo.create_image(0,0, anchor='nw')
        self.cnvVideo.pack(side='right', fill='both', expand=1)

        # Buttons
        self.bind("o",self.openVideoPlayback)
        self.bind("p",self.closeVideoPlayback)
        
        # reference video playback
        self.video = VideoPlayback()
        self.afterVideo = None

    def openVideoPlayback(self, *args):
        # show file dialog
        videoSrc = filedialog.askopenfilename(title = 'Open video', filetypes=(("Video files (*.mp4)","*.mp4"),("All files (*.*)","*.*")))
        if videoSrc:
            # open selected video for playback
            self.video.openVideo(videoSrc, width=405, height=720)
            # start update loop
            self.afterVideo = self.after(VIDEO_RATE, self.updateVideoPlayback)

    def updateVideoPlayback(self):
        (ret, frame) = self.video.readVideoFrame()
        if ret:
            image = Image.fromarray(frame)
            image = ImageTk.PhotoImage(image, master=self)
            self.cnvVideo.itemconfig(self.cnvVideoImg, image=image)
            self.cnvVideo.image = image
            self.cnvVideo.after(VIDEO_RATE, self.updateVideoPlayback)
        else:
            print("Stop rescheduling")

    def closeVideoPlayback(self, *args):
        # stop video playback
        if self.video:
            self.video.closeVideo()

app = MainWindow()
app.mainloop()

我尝试self.video.closeVideo() 在打开新视频之前添加。我尝试self.after_cancel(self.afterVideo)在打开新视频之前添加。

该代码是更大的 Tkinter 接口的一部分,但被剥离为最小(非)工作示例。

标签: pythonpython-3.xopencvtkinter

解决方案


推荐阅读