Display video size on label tkinter - multithreading

The below code displays video on the label. But, problem is that, it is displayed in a very zoom (large) manner. I want to resize it to display correctly on label. When I use the option image=image.resize(), I get an error
ValueError: cannot resize this array: it does not own its data
import tkinter as tk, threading
import imageio
from PIL import Image, ImageTk
video_name = "e.mp4"
video = imageio.get_reader(video_name)
#video = video.resize(20,20)
def stream(label):
for image in video.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image))
label.config(image=frame_image)
label.image = frame_image
root = tk.Tk()
my_label = tk.Label(root, width=500,height=500)
my_label.place(x=0,y=0)
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()

You can call resize() on the return image of Image.fromarray(image):
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize((100,100)))

Related

Tkinter resizing images in labels arranged in a grid

To start, I'm totally new to Tkinter and am trying to make a Raspberry Pi Media Player of sorts...
I grab the directories of all .mp4 files on a USB drive, and use PIL to put the thumbnails of the videos into a 3x3 grid of labels, with the grid inside of a frame (frame2 in the code).
Right now, with the thumbnails of varying sizes, the labels are also inconsistently sized. Also, only the top-right portion of larger thumbnails are displayed, rather than the entire image.
How can I to scale and fit the thumbnails into consistently sized labels, in grid form?
Here is part of my code (It's quite large so I try to include only the relevant parts):
import tkinter as tk
from subprocess import Popen
from time import sleep
import os
from random import randint
import imageio
from PIL import ImageTk, Image
from pathlib import Path
#putting 100th frame of video with 'path' into the label
def pack_thumbnail(path, label):
#this is probably not a good way to do this
video = imageio.get_reader(path)
for i in range(100):
try:
image = video.get_next_data()
except:
video.close()
break
frame_image = ImageTk.PhotoImage(Image.fromarray(image))
label.config(image=frame_image)
label.image = frame_image
window = tk.Tk()
window.attributes("-fullscreen", True)
frame1 = tk.Frame(master=window, width=200, height=100, bg="white")
frame1.pack(fill=tk.Y, side=tk.LEFT)
#frame2 contains the grid of labels
frame2 = tk.Frame()
for i in range(3):
frame2.columnconfigure(i, weight=1, minsize=75)
frame2.rowconfigure(i, weight=1, minsize=50)
for j in range(0, 3):
frame = tk.Frame(master=frame2, relief=tk.RAISED, borderwidth=1)
frame.grid(row=i, column=j, padx=5, pady=5)
#path to video to get thumbnail (i only have 3 videos so i randomize it)
vid_path=f"/media/pi/{os.listdir('/media/pi/')[0]}/{folder_name}/{videos[randint(0, 2)]}"
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
pack_thumbnail(vid_path, label)
label.pack(padx=5, pady=5)
frame2.pack()
window.bind("<Escape>", lambda x: window.destroy())
window.mainloop()
You can use Image.thumbnail() to resize the image:
# putting 100th frame of video with 'path' into the label
def pack_thumbnail(path, label):
with imageio.get_reader(path) as video:
image = video.get_data(100) # use get_date() instead of get_next_data()
w, h = 200, 200 # thumbnail size
image = Image.fromarray(image)
image.thumbnail((w, h)) # resize the image
frame_image = ImageTk.PhotoImage(image)
label.config(image=frame_image, width=w, height=h)
label.image = frame_image

how to add image in tkinter?

I can't add image (gif image) to tkinter window.
from tkinter import *
from tkinter import filedialog
from PIL import ImageTk, Image
root = Tk()
def open_image():
qr_select = filedialog.askopenfilename(title = "open")
im = PhotoImage(file=qr_select)
w1 = Label(window, image = im)
w1.image = im
w1.config(image=im)
w1.pack(side="right")
def window_function():
global window
window=Tk()
window.geometry("800x550+650+250")
window.title("QR_Scanner")
btn = Button(window,text = "open a gif picture",command = open_image)
btn.pack()
root.iconify()
window.mainloop()
btn = Button(root,text = "open window",command = window_function)
btn.pack()
root.mainloop()
my error is (_tkinter.TclError: image "pyimage1" doesn't exist)
The reason you can't see your gif in the window is that you haven't made a reference to the image so it is collected in Tkinters garbage collector. Read More About This Here. To Add a reference to the image you can do this:
w1.image = im
And add it in your code here:
def open_image():
qr_select = filedialog.askopenfilename(title = "open")
im = PhotoImage(file=qr_select)
w1 = Label(root, image = im)
w1.image = im #Keep A Reference To The Image
w1.config(image=im)
w1.pack(side="right")
The reason you are getting pyimage1 doesn't exist is because you have more than one instance the Tk and there is only meant to be 1. You have to make your window a Toplevel() by replacing: window=Tk() with window=TopLevel()

Flickering video for tkinter video

I am trying to make a simple play/pause application in tkinter. Basically I want to show a video and have a play/pause button underneath.
So, after some research I found this suitable post to show a video using tkinter and opencv:
to show video streaming inside frame in tkinter
When using the code, given in the accepted answer to show a video, there is no problem and I don't see any flickering. Here is the code:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
root = tk.Tk()
# Create a frame
app = tk.Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = tk.Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture(r'PATH_TO_VIDEO_FILE')
# function for video streaming
frame_number = 0
def video_stream():
global frame_number
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
frame_number += 1
video_stream()
root.mainloop()
Now, I slightly altered the code to be able to use the grid manager and add a play button:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
self.window = tk.Tk()
self.window.title("Video_Player")
self.videocap = cv2.VideoCapture(path_to_video)
self.frame_number = 0
# Initalize
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
success, self.frame = self.videocap.read()
cv2image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA)
self.img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=self.img)
# Show frames
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
self.btn_next_image=tk.Button(self.window, text="Play", width=50, bg ="green",command=self.video_stream).grid(row=1,column=0)
self.window.mainloop()
def video_stream(self):
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
sucess, frame = self.videocap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')
The problem is that when I execute the above code, the video flickers as if tkinter need to reload something in-between frames. I have no clue why this happens.
P.S. This post here Flickering video in opencv-tkinter integration did not help me.
You need to make two changes: split your self.picture_label line to create a proper reference to your Label object, and then use self.picure_label.config(...) to change the image.
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
...
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE)
self.picture_label.grid(row=0, column=0)
...
def video_stream(self):
...
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label.config(image=self.imgtk)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')

tkinter show images of a database

I have a dataset called "df_db" in which I have stored images in base64 format in a column called "Images". Now, I would like to use tkinter to scroll through the images one after the other. So far I have implemented a scrollbar and I use that value to extract a base64 string from my database which I then convert to an image and try to show in tkinter:
from tkinter import *
from PIL import ImageTk, Image
import os
def show_frame(val):
#Covert from base64
b64 = df_db["Images"][int(val)]
imgdata = base64.b64decode(b64)
#Show image
img = ImageTk.PhotoImage(Image.open(io.BytesIO(imgdata)))
panel = Label(root, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
root = Tk()
w1 = Scale(root, from_=0, to=42, orient=HORIZONTAL, command=show_frame)
w1.pack()
root.mainloop()
So far it doesn't work.

Trouble adding Images into a Gui using tkinter

OK so i am trying to make a program that displays an image when pressing a button, and i am having trouble getting the images into the program
this is my full code:
# Nicolas Bart
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import *
window = tk.Tk()
window.title('Bad Meme Generator')
window.geometry('500x500')
window.configure(bg='saddle brown')
meme_label = tk.Label(window, text='PRESS BUTTON FOR BAD MEMES:',
fg='blue4', bg='brown4', font=('comicsans', '20'))
meme_label.grid(pady=25, padx=25, column=0, row=0)
def button_command():
meme_window = tk.Tk()
meme_window.title('I Warned You')
meme_window.grid()
image = Image.open('pexels-photo-247932.jpg')
photo = ImageTk.PhotoImage(image)
label = tk.Label(meme_window, image=photo)
label.image = photo
label.place(x = 0, y = 0)
button = tk.Button(window, text='Dont Do It!', command=button_command,
padx=100, pady=75, font=('comicsans', '20'),
bg='brown4', fg='blue4')
button.grid(column=0, row=1)
warning_label = tk.Label(window, text="Really shit tier memes incoming:",
bg='brown4', fg='blue4',
font=('comicsans', '20'))
warning_label.grid(pady=75)
window.mainloop()
every time i run this program, when i press the button to open the image, it gives the error "AttributeError: type object 'Image' has no attribute 'open'"
the specific part of the program that is giving the error is:
def button_command():
meme_window = tk.Tk()
meme_window.title('I Warned You')
meme_window.grid()
image = Image.open('pexels-photo-247932.jpg')
photo = ImageTk.PhotoImage(image)
label = tk.Label(meme_window, image=photo)
label.image = photo
label.place(x = 0, y = 0)
any help would be appreciated. Thank you :)
This is a good example of why you shouldn't do from tkinter import *. Tkinter has an Image class, so by doing this import after importing Image from PIL you overwrite the PIL class with the tkinter class.
Since you're already importing tkinter the preferred way (import tkinter as tk), you don't need to import tkinter a second time. You need to remove the statement from tkinter import *.
You also make the mistake of creating more than one instance of Tk. I don't know if it contributes to the problem or not, but it's not something you should be doing. If you need additional windows then you should create instances of Toplevel.

Resources