0

I'm trying to make a GUI that allows me to play a video and stop it at some point to mark some points and play it again.

I want to implement it with Tkinter and a thread that reads the frames(I put them in a buffer) and another one that updates them on a canvas, my code is:

import tkinter
import threading
import queue
import cv2
import PIL.Image, PIL.ImageTk

class GuiPart:
    def __init__(self, window, queue, w,h):
        self.queue = queue
        self.stopped=False

        self.canvas = tkinter.Canvas(window, width = w, height = h)
        self.canvas.pack()

        self.backgroudThread=threading.Thread(target=self.processIncomingFrames)
        self.backgroudThread.start()

    def processIncomingFrames(self):
        while True:
            try:
                frame = self.queue.get()
                frameImage = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
                self.canvas.create_image(0, 0, image = frameImage, anchor = tkinter.NW)
            except queue.Empty:
                pass

class AppManager:

    def __init__(self, window, video=0):
        self.window = window
        self.video = video

        self.capFile = cv2.VideoCapture(self.video)
        self.fps = self.capFile.get(cv2.CAP_PROP_FPS)
        self.widthVideo = self.capFile.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.heightVideo = self.capFile.get(cv2.CAP_PROP_FRAME_HEIGHT)

        self.queue = queue.Queue(20)
        self.gui = GuiPart(window, self.queue, self.widthVideo, self.heightVideo)

        self.running=True

        self.readFileThread = threading.Thread(target=self.readFileThread)
        self.readFileThread.start()

    def readFileThread(self):
        while self.running:
                ret, frame = self.capFile.read()
                if not ret:
                    self.running = False
                while self.running:
                    try:
                        self.queue.put(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), timeout=1)
                        break
                    except queue.Full:
                        pass

    def endApplication(self):
        self.running = False

window = tkinter.Tk()

manager = AppManager(window,"videoplayback.mp4")
window.mainloop()

The problem is that it reproduces the video well (for now I'm not interested in FPS, I know how to correct it) but every certain amount of frames the initial frame appears, it is as if it were loaded in memory and appeared. If necessary I can record a video of the screen, but in this link I leave a .gif to see what I mean.

Do you know what may be causing this to happen?

UPDATED

To solve the problem I wrote the following code, in processIncomingFrames() function:

def processIncomingFrames(self):
        while True:
            try:
                frame = self.queue.get()
                frameImage = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
                if self.idFrame is None:
                    self.idFrame = self.canvas.create_image(0, 0, image = frameImage, anchor = tkinter.NW)
                else:
                    self.canvas.itemconfig(self.idFrame, image = frameImage)
                    self.canvas.image=frameImage
            except queue.Empty:
                pass
martineau
  • 119,623
  • 25
  • 170
  • 301
  • You do `canvas.create_image(...` but don't `.delete(...` the previos image. Read [remove-canvas-widgets-in-tkinter](https://stackoverflow.com/questions/23074780/remove-canvas-widgets-in-tkinter) – stovfl Nov 02 '18 at 08:37
  • @stovfl Hi, i tried with that but now appears a white image between frames – Guille Gaete Nov 02 '18 at 18:09
  • Can you confirm, that the problem with *"the initial frame appears"* are gone? Try this [how-to-update-an-image-on-a-canvas](https://stackoverflow.com/questions/19838972/how-to-update-an-image-on-a-canvas) – stovfl Nov 02 '18 at 18:14
  • 1
    Yes, now the initial frame does not appear anymore, i'm going to try with you to tell me and tell you the result, thx! – Guille Gaete Nov 02 '18 at 19:01
  • It doesn't work, i think that the issue maybe it related with the "thread" processing. Does the GUI have a special thread to process the update, right? – Guille Gaete Nov 02 '18 at 19:11
  • 1
    Yes, read [tkinter-how-to-use-after-method](https://stackoverflow.com/questions/25753632/tkinter-how-to-use-after-method). Are you familar with [flickering-video-in-opencv-tkinter-integration?](https://stackoverflow.com/questions/48364168/flickering-video-in-opencv-tkinter-integration?) – stovfl Nov 02 '18 at 19:18
  • 1
    @stovfl Great, I found the problem and apparently now it is working correctly, what you have to do after `self.canvas.itemconfig(self.idFrame, image = frameImage)` is to execute `self.canvas.image=frameImage` – Guille Gaete Nov 02 '18 at 20:01
  • Thx for help me! – Guille Gaete Nov 02 '18 at 20:01

0 Answers0