Jul-23-2024, 09:48 PM
A video has a resolution. A video recorded at 720p resolution is has frames that are 1280 x 720 pixels in size (921,600 pixels). When I play a 720p video using your program, that is the size of the video that I see. Since my screen resolution is 1920 x 1080 pixels (2,073,600 pixels), the image takes up 45% of the screen.
When I set the video to record at 240p resolution it has frames that are 320 x 240 pixels in size (76,800 pixels). This results in a smaller window. In fact, it should make a window that is only 320 x 240 pixels in size (about 4% of my laptop screen).
I modified your code to increase the video size, resizing each image.
In addition to resolution, a video has a frame rate. The only settings I have available for recording video on my laptop is 30 fps. When I play back the 720p video the frame rate looks about right. Your program must be taking about 1/30 of a second to read the frame, convert to an image, and set the image in the label. When I play the 260p video it runs much too fast. It takes less time to read and process the smaller frames, so the program plays more frames per second.
What you need is something that throttles your program so it only reads and displays 30 frames per second (or whatever the frame rate should be for your video). The tkVideoPlayer that menator mentions will do this. If you want to stick with using imageio, you need to either write code to do this, or import a tool that will do it for you (like the pyav package I mentioned in my previous post). Writing code to do this yourself might be fun. Here's a simple and not very good attempt.
When I set the video to record at 240p resolution it has frames that are 320 x 240 pixels in size (76,800 pixels). This results in a smaller window. In fact, it should make a window that is only 320 x 240 pixels in size (about 4% of my laptop screen).
I modified your code to increase the video size, resizing each image.
def update(self): for frame in self.video.iter_data(): if not self.play_video: break image = Image.fromarray(frame).resize((640, 520)) # <- 2x the default size of 320 x 260 image = ImageTk.PhotoImage(image) self.video_label.config(image=image) self.video_label.image = image self.root.update() self.video.close()You could do something similar to change the of your video images.
In addition to resolution, a video has a frame rate. The only settings I have available for recording video on my laptop is 30 fps. When I play back the 720p video the frame rate looks about right. Your program must be taking about 1/30 of a second to read the frame, convert to an image, and set the image in the label. When I play the 260p video it runs much too fast. It takes less time to read and process the smaller frames, so the program plays more frames per second.
What you need is something that throttles your program so it only reads and displays 30 frames per second (or whatever the frame rate should be for your video). The tkVideoPlayer that menator mentions will do this. If you want to stick with using imageio, you need to either write code to do this, or import a tool that will do it for you (like the pyav package I mentioned in my previous post). Writing code to do this yourself might be fun. Here's a simple and not very good attempt.
import tkinter as tk from PIL import Image, ImageTk import imageio class VideoPlayer: def __init__(self, root, video_path): self.root = root self.video_label = tk.Label(root) self.video_label.pack() self.video = imageio.get_reader(video_path) self.play_video = True self.frame_iter = self.video.iter_data() self.update() def update(self): if self.play_video: self.root.after(33, self.update) # call update again after 1/30 of a second. try: frame = next(self.frame_iter) image = Image.fromarray(frame) image = ImageTk.PhotoImage(image) self.video_label.config(image=image) self.video_label.image = image self.root.update() except StopIteration: self.play_video = False else: self.video.close() root.destroy() def stop(self): self.play_video = False if __name__ == "__main__": root = tk.Tk() root.title("Video Player") video_path = r"...\video.mp4" player = VideoPlayer(root, video_path) root.protocol("WM_DELETE_WINDOW", player.stop) root.mainloop()