waitKey() function not working with tkinter - python

I designed a window with tkinter and wanted to show a video stream there, and I also wanted to be able to pause the video with the "p" key. I created a function which puts a picture captured with a webcam into a label on the window. Then I put the function into the while loop to call it repeatedly and make a continuous video in the window. And I used waitKey() functions in the loop to read the key presses. The waitKey() also sets the frame rate or speed of the video. I managed to get the video running but the program does not react to key presses. On the other hand, I can change the frame rate when I change the argument of the waitKey(), so the function seems to work but it doesn't read the key presses and nothing happens when a key is pressed. There are also no error messages. I would be thankful if somebody would show me how to use the waitKey() in this loop so that I could control the video stream with key presses, or suggest a different way to do it. This is my code, most of it is designing the window but the loop with waitKey() functions is at the end:
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter.ttk import *
from PIL import ImageTk, Image
from cv2 import cv2
mainwin = Tk() #create main window
appWinWidth = int(0.6 * mainwin.winfo_screenwidth()) #set main window size (depending on the screen)
appWinHeight = int(0.5 * mainwin.winfo_screenheight())
screen_width = mainwin.winfo_screenwidth() #get screen dimensions
screen_height = mainwin.winfo_screenheight()
appWinX = int((screen_width - appWinWidth)/2) #set coordinates of the main window so that it
appWinY = int((screen_height - appWinHeight)/2) #would be located in the middle of the screen
#create and place the frames
frame1 = tk.Frame(mainwin, background = "white", width = int(appWinWidth/2), height = appWinHeight)
frame2 = tk.Frame(mainwin, background = "black", width = int(appWinWidth/2), height = appWinHeight)
frame1.grid(row = 0, column = 0, sticky = "nsew")
frame2.grid(row = 0, column = 1, sticky = "nsew")
mainwin.grid_columnconfigure(0, weight = 1)
mainwin.grid_columnconfigure(1, weight = 1)
mainwin.grid_rowconfigure(0, weight = 1)
#set the geometry of the main window
mainwin.geometry("{}x{}+{}+{}".format(appWinWidth, appWinHeight, appWinX, appWinY))
mainwin.resizable(width = 0, height = 0)
#create labels in frames
frame2.grid_propagate(0) #labels and other widgets in frame don't change the size of the frame
frame2.grid()
labelF2 = Label(frame2)
labelF2.grid()
labelF2.place(relx=0.5, rely=0.5, anchor="center")
frame1.grid_propagate(0)
labelF1 = Label(frame1, background = "white")
labelF1.grid()
mainwin.update()
#get camera feed and frame size
cap = cv2.VideoCapture(0)
capFrameWidth = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
capFrameHeight = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
widthRelation = capFrameWidth/frame2.winfo_width()
heigthRelation = capFrameHeight/frame2.winfo_height()
#define the size of the video frame so that the video would fit into the frame of the main window
if widthRelation > 1 and widthRelation > heigthRelation:
fittedSize = (int(capFrameWidth/widthRelation), int(capFrameHeight/widthRelation))
elif heigthRelation > 1 and heigthRelation > widthRelation:
fittedSize = (int(capFrameWidth/heigthRelation), int(capFrameHeight/heigthRelation))
else:
fittedSize = (capFrameWidth, capFrameHeight)
#funtion for getting a video frame, resizing it for the main window and placing it into the frame 2
def video_to_mainwin():
chk, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgV = cv2.resize(cv2image, fittedSize)
img = Image.fromarray(imgV)
imgtk = ImageTk.PhotoImage(image=img)
labelF2.imgtk = imgtk
labelF2.configure(image=imgtk)
#run the video function continuously to create a stream, and be ready to react to key presses
while True:
key = cv2.waitKey(1)
if key == ord('p'): #if 'p' is pressed, pause the stream and write 'Paused' to frame 1 on the main window
video_to_mainwin()
labelF1.config(text = 'Paused')
mainwin.update()
key2 = cv2.waitKey(0) #remain paused until 'c' is pressed
if key2 == ord('c'):
labelF1.config(text = '')
else: #if no key is pressed, then just show the video stream
video_to_mainwin()
mainwin.update()

If you display cv2 image in tkinter then you don't need cv2.waitKey().
You can use tkinter for this
mainwin.bind("p", function_name)
to run function_name() when you press p.
And when you use bind() then you don't need while True to check pressed key because tkinter mainloop will do it for you.
You need only root.after(milliseconds, function_name) to run function which will update image in window every few milliseconds.
Working code
#from tkinter import * # PEP8: `import *` is not preferred
#from tkinter.ttk import * # PEP8: `import *` is not preferred
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
from cv2 import cv2
# --- constants --- (PEP8: UPPER_CASE_NAMES)
#FPS = 25
# --- classes --- (PEP8: CamelCaseNames)
# ... empty ...
# --- functions --- (PEP8: lower_case_names)
def video_to_mainwin():
"""Getting a video frame, resizing it for the main window and placing it into the frame 2."""
if not paused:
chk, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgV = cv2.resize(cv2image, fitted_size)
img = Image.fromarray(imgV)
imgtk = ImageTk.PhotoImage(image=img)
labelF2.imgtk = imgtk
labelF2.configure(image=imgtk)
# run it again after `1000/FPS` milliseconds
#mainwin.after(int(1000/FPS), video_to_mainwin)
mainwin.after(int(1000/cap_fps), video_to_mainwin)
def set_pause(event):
global paused
paused = True
labelF1.config(text = 'Paused')
def unset_pause(event):
global paused
paused = False
labelF1.config(text = '')
# --- main ---- (PEP8: lower_case_names)
paused = False # default value at start
# - cap -
cap = cv2.VideoCapture(0)
cap_frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
cap_frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
cap_fps = int(cap.get(cv2.CAP_PROP_FPS))
# - tk -
mainwin = tk.Tk() # create main window
app_win_width = int(0.6 * mainwin.winfo_screenwidth()) # set main window size (depending on the screen)
app_win_height = int(0.5 * mainwin.winfo_screenheight())
screen_width = mainwin.winfo_screenwidth() # get screen dimensions
screen_height = mainwin.winfo_screenheight()
app_win_x = int((screen_width - app_win_width)/2) # set coordinates of the main window so that it
app_win_y = int((screen_height - app_win_height)/2) # would be located in the middle of the screen
#create and place the frames
frame1 = tk.Frame(mainwin, background="white", width=int(app_win_width/2), height=app_win_height)
frame2 = tk.Frame(mainwin, background="black", width=int(app_win_width/2), height=app_win_height)
frame1.grid(row=0, column=0, sticky="nsew") # PEP8: without spaces around `=`
frame2.grid(row=0, column=1, sticky="nsew") # PEP8: without spaces around `=`
mainwin.grid_columnconfigure(0, weight=1) # PEP8: without spaces around `=`
mainwin.grid_columnconfigure(1, weight=1) # PEP8: without spaces around `=`
mainwin.grid_rowconfigure(0, weight=1)
#set the geometry of the main window
mainwin.geometry("{}x{}+{}+{}".format(app_win_width, app_win_height, app_win_x, app_win_y))
mainwin.resizable(width=0, height=0)
# create labels in frames
frame2.grid_propagate(0) # labels and other widgets in frame don't change the size of the frame
frame2.grid()
labelF2 = tk.Label(frame2)
labelF2.place(relx=0.5, rely=0.5, anchor="center")
frame1.grid_propagate(0)
labelF1 = tk.Label(frame1, background = "white")
labelF1.grid()
mainwin.update() # to create window and correctly calculate sizes
# get camera feed and frame size
width_relation = cap_frame_width/frame2.winfo_width()
heigth_relation = cap_frame_height/frame2.winfo_height()
# define the size of the video frame so that the video would fit into the frame of the main window
if width_relation > 1 and width_relation > heigth_relation:
fitted_size = (int(cap_frame_width/width_relation), int(cap_frame_height/width_relation))
elif heigth_relation > 1 and heigth_relation > width_relation:
fitted_size = (int(cap_frame_width/heigth_relation), int(cap_frame_height/heigth_relation))
else:
fitted_size = (cap_frame_width, cap_frame_height)
# assing functions to buttons
mainwin.bind("p", set_pause)
mainwin.bind("c", unset_pause)
# star video
video_to_mainwin()
# start tkinter mainloop (event loop)
mainwin.mainloop()
# - end -
cap.release()
PEP 8 -- Style Guide for Python Code
I have few examples with cv2 and tkinter on Github.
For example: python-cv2-streams-viewer uses cv2 in separted thread to read from webcam or file (even from file on internet). It also uses tk.Frame to create widget which display stream from cv2. It uses this widget many times to display many streams at the same time.
Other examples in python-examples - cv2 or tkinter

Related

How to center an image in tkinter with PIL

I want to center an image in tkinter canvas. The only thing I could think of is using anchor = 'c' but that does not seem to work. I have also tried using it on the stage.
def newsetup(filelocation):
global width, height
for widgets in root.winfo_children():
widgets.destroy()
stage = Canvas(root, width = 1000, height = 700, highlightbackground = 'red', highlightthickness = 2)
stage.pack()
imgtk = ImageTk.PhotoImage(Image.open(filelocation))
stage.create_image(stage.winfo_width() + 2, stage.winfo_height() + 2, image = imgtk, anchor = CENTER)
stage.image = imgtk
if you want to center on canvas then you need
stage.winfo_width()/2, stage.winfo_height()/2
(even without anchor= which as default has value center)
But if you put image before it runs mainloop() then stage.winfo_width() stage.winfo_height() gives 0,0 instead of expected width, height (because mainloop creates window and it sets all sizes for widgets) and it may need root.update() to run mainloop once and it will calculate all sizes.
import tkinter as tk
from PIL import ImageTk, Image
root = tk.Tk()
stage = tk.Canvas(root, width=1000, height=700)
stage.pack()
root.update() # force `mainloop()` to calculate current `width`, `height` for canvas
# and later `stage.winfo_width()` `stage.winfo_height()` will get correct values instead `0`
#imgtk = ImageTk.PhotoImage(Image.open('lenna.png'))
imgtk = ImageTk.PhotoImage(file='lenna.png')
stage.create_image((stage.winfo_width()/2, stage.winfo_height()/2), image=imgtk, anchor='center')
stage.image = imgtk
root.mainloop()
Result:
Image Lenna from Wikipedia.
EDIT:
If you want to keep it centered when you resize window then you can bind event <Configure> to canvas and it will execute assigned function - and it can move image.
But it needs image ID which you can get when you create image
img_id = stage.create_image(...)
Because <Configure> is executed when it creates window so code doesn't need root.update() because on_resize() will set correct position at start.
import tkinter as tk
from PIL import ImageTk, Image
# --- functions ---
def on_resize(event):
# it respects anchor
x = event.width/2
y = event.height/2
stage.coords(img_id, x, y)
# it DOESN'T respects anchor so you have to add offset
#x = (event.width - imgtk.width())/2
#y = (event.height - imgtk.height())/2
#stage.moveto(img_id, x, y) # doesn't respect anchor
#stage.itemconfigure(img_id, ...)
# --- main ---
root = tk.Tk()
stage = tk.Canvas(root, width=1000, height=700)
stage.pack(fill='both', expand=True)
#root.update() # force `mainloop()` to calculate current `width`, `height` for canvas
# and later `stage.winfo_width()` `stage.winfo_height()` will get correct values instead `0`
#imgtk = ImageTk.PhotoImage(Image.open('lenna.png'))
imgtk = ImageTk.PhotoImage(file='lenna.png')
img_id = stage.create_image((stage.winfo_width()/2, stage.winfo_height()/2), image=imgtk, anchor='center')
stage.image = imgtk
stage.bind('<Configure>', on_resize) # run function when Canvas change size
root.mainloop()

Record Video Button in tkinter GUI python

I'm pretty new to python and espcially tkinter and opencv.
I've got someway (a little way) to creating a gui that will eventually control a microscope and ai. But I've hit a stumbling block, trying to record the video that is displayed within the gui, I think its to do with the video feed already been captured in the display, but I can't find a way around it. It all works fine until I hit record then it crashes and i get the error: open VIDEOIO(V4L2:/dev/video0): can't open camera by index.
Apologies for the long code but I've cut it down to as much as I think possible.
The problem is in the root.recbtn and def rec sections.
import cv2
import tkinter as tk
import multiprocessing
from tkinter import *
from PIL import Image,ImageTk
from datetime import datetime
from tkinter import messagebox, filedialog
e = multiprocessing.Event()
p = None
# Defining CreateWidgets() function to create necessary tkinter widgets
def createwidgets():
root.cameraLabel = Label(root, bg="gray25", borderwidth=3, relief="ridge")
root.cameraLabel.grid(row=2, column=1, padx=10, pady=10, columnspan=3)
root.browseButton = Button(root, bg="gray25", width=10, text="BROWSE", command=destBrowse)
root.browseButton.grid(row=1, column=1, padx=10, pady=10)
root.recbtn = Button(root, bg="gray25", width=10, text="Record", command=rec)
root.recbtn.grid(row=1, column=5, padx=10, pady=10)
root.saveLocationEntry = Entry(root, width=55, textvariable=destPath)
root.saveLocationEntry.grid(row=1, column=2, padx=10, pady=10)
# Calling ShowFeed() function
ShowFeed()
# Defining ShowFeed() function to display webcam feed in the cameraLabel;
def ShowFeed():
# t5 # Capturing frame by frame
ret, frame = root.cap.read()
if ret:
# Flipping the frame vertically
frame = cv2.flip(frame, 1)
# Changing the frame color from BGR to RGB
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
# Creating an image memory from the above frame exporting array interface
videoImg = Image.fromarray(cv2image)
# Creating object of PhotoImage() class to display the frame
imgtk = ImageTk.PhotoImage(image = videoImg)
# Configuring the label to display the frame
root.cameraLabel.configure(image=imgtk)
# Keeping a reference
root.cameraLabel.imgtk = imgtk
# Calling the function after 10 milliseconds
root.cameraLabel.after(10, ShowFeed)
else:
# Configuring the label to display the frame
root.cameraLabel.configure(image='')
def destBrowse():
# Presenting user with a pop-up for directory selection. initialdir argument is optional
# Retrieving the user-input destination directory and storing it in destinationDirectory
# Setting the initialdir argument is optional. SET IT TO YOUR DIRECTORY PATH
destDirectory = filedialog.askdirectory(initialdir="YOUR DIRECTORY PATH")
# Displaying the directory in the directory textbox
destPath.set(destDirectory)
def rec():
vid_name = datetime.now().strftime('%d-%m-%Y %H-%M-%S')
# If the user has selected the destination directory, then get the directory and save it in image_path
if destPath.get() != '':
vid_path = destPath.get()
# If the user has not selected any destination directory, then set the image_path to default directory
else:
messagebox.showerror("ERROR", "No Directory Selected!")
# Concatenating the image_path with image_name and with .jpg extension and saving it in imgName variable
vidName = vid_path + '/' + vid_name + ".avi"
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
videoWriter = cv2.VideoWriter(vidName, fourcc, 30.0, (640, 480))
while (True):
ret, frame = capture.read()
if ret:
cv2.imshow('video', frame)
videoWriter.write(frame)
if cv2.waitKey(1) == 27:
break
capture.release()
videoWriter.release()
# Creating object of tk class
root = tk.Tk()
# Creating object of class VideoCapture with webcam index
root.cap = cv2.VideoCapture(0)
# Setting width and height
width, height = 1200, 1200
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
# Setting the title, window size, background color and disabling the resizing property
root.title("Test-AI-tes")
root.geometry("1600x1024")
root.resizable(True, True)
root.configure(background = "gray18")
# Creating tkinter variables
destPath = StringVar()
imagePath = StringVar()
createwidgets()
root.mainloop()
Thanks!
This answer is similar to #Art's answer but I removed the after_id and queue.
import cv2
import threading
import tkinter as tk
from PIL import Image, ImageTk
def stop_rec():
global running
running = False
start_button.config(state="normal")
stop_button.config(state="disabled")
def start_capture():
global capture, last_frame
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc("X", "V", "I", "D")
video_writer = cv2.VideoWriter(r"sample.avi", fourcc, 30.0, (640, 480))
while running:
rect, frame = capture.read()
if rect:
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
last_frame = Image.fromarray(cv2image)
video_writer.write(frame)
capture.release()
video_writer.release()
def update_frame():
if last_frame is not None:
tk_img = ImageTk.PhotoImage(master=video_label, image=last_frame)
video_label.config(image=tk_img)
video_label.tk_img = tk_img
if running:
root.after(10, update_frame)
def start_rec():
global running
running = True
thread = threading.Thread(target=start_capture, daemon=True)
thread.start()
update_frame()
start_button.config(state="disabled")
stop_button.config(state="normal")
def closeWindow():
stop_rec()
root.destroy()
running = False
after_id = None
last_frame = None
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", closeWindow)
video_label = tk.Label()
video_label.pack(expand=True, fill="both")
start_button = tk.Button(text="Start", command=start_rec)
start_button.pack()
stop_button = tk.Button(text="Stop", command=stop_rec, state="disabled")
stop_button.pack()
root.mainloop()
It uses the boolean flag running instead of using after_id. Also instead of storing the images in a queue then showing it, I only keep the last image. That way it can run in real time on my computer. Don't worry all of the frames are still being stored in the video file.
You cannot have infinite while loop along with the GUI's loop. You should instead make use of threading, whenever you have an IO operation to complete.
Example code:
import cv2
import threading
import tkinter as tk
from PIL import Image, ImageTk
from queue import Queue
def stop_rec():
global running, after_id
running = False
if after_id:
root.after_cancel(after_id)
after_id = None
with frame_queue.mutex:
frame_queue.queue.clear()
def start_capture():
global capture
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
video_writer = cv2.VideoWriter(r"sample.avi", fourcc, 30.0, (640, 480))
while running:
rect, frame = capture.read()
if rect:
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
videoImg = Image.fromarray(cv2image)
# current_frame = ImageTk.PhotoImage(image = videoImg)
frame_queue.put(videoImg)
video_writer.write(frame)
capture.release()
video_writer.release()
def update_frame():
global after_id
if not frame_queue.empty():
video_label.image_frame = ImageTk.PhotoImage(frame_queue.get_nowait())
video_label.config(image=video_label.image_frame)
after_id = root.after(10, update_frame)
def start_rec():
global running
stop_rec()
running = True
thread = threading.Thread(target=start_capture, daemon=True)
thread.start()
update_frame()
def closeWindow():
stop_rec()
root.destroy()
running = False
after_id = None
frame_queue = Queue()
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", closeWindow)
video_label = tk.Label()
video_label.pack(expand=True, fill="both")
tk.Button(text="Start", command=start_rec).pack()
tk.Button(text="stop", command=stop_rec).pack()
root.mainloop()
Quick explanation:
start the record function in a new thread.
Use Queue to store the frames.
Then make use of [widget].after to update the label at regular intervals.
To stop the recording make use of [widget].after_cancel(after_id)(after_id is returned when you use .after method) and set the running variable to False to stop the loop.

Is it possible to provide animation on image ? - Tkinter

I'm developing a GUI in Tkinter and want to apply animation in the below GIF on the image when it appears.
Here is my code,
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
frame = Frame(root)
frame.pack()
canvas = Canvas(frame, width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
canvas.pack()
background = PhotoImage(file="background.png")
canvas.create_image(300,300,image=background)
my_pic = PhotoImage(file="start000-befored.png")
frame.after(1000, lambda: (canvas.create_image(50,50,image=my_pic, anchor=NW))) #and on this image, I want to give the effect.
root.mainloop()
Instead of clicking on the play button as shown in GIF, the image should automatically appears after 1 second like this animation and stays on screen. (No closing option).
I'm not 100% sure I understood the problem, but I'll describe how to animate an image.
Tkinter does not contain functions for animating images so you'll have to write them yourself. You will have to extract all subimages, subimage duration and then build a sequencer to swap subimages on your display.
Pillow can extract image sequences. WEBP images seems to only support one frame duration whereas GIF images may have different frame duration for each subimage. I will use only the first duration for GIF images even if there is many. Pillow does not support getting frame duration from WEBP images as far as I have seen but you gan read it from the file, see WebP Container Specification.
Example implementation:
import tkinter as tk
from PIL import Image, ImageTk, ImageSequence
import itertools
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
filename = 'images/animated-nyan-cat.webp'
pil_image = Image.open(filename)
no_of_frames = pil_image.n_frames
# Get frame duration, assuming all frame durations are the same
duration = pil_image.info.get('duration', None) # None for WEBP
if duration is None:
with open(filename, 'rb') as binfile:
data = binfile.read()
pos = data.find(b'ANMF') # Extract duration for WEBP sequences
duration = int.from_bytes(data[pos+12:pos+15], byteorder='big')
# Create an infinite cycle of PIL ImageTk images for display on label
frame_list = []
for frame in ImageSequence.Iterator(pil_image):
cp = frame.copy()
frame_list.append(cp)
tkframe_list = [ImageTk.PhotoImage(image=fr) for fr in frame_list]
tkframe_sequence = itertools.cycle(tkframe_list)
tkframe_iterator = iter(tkframe_list)
def show_animation():
global after_id
after_id = root.after(duration, show_animation)
img = next(tkframe_sequence)
display.config(image=img)
def stop_animation(*event):
root.after_cancel(after_id)
def run_animation_once():
global after_id
after_id = root.after(duration, run_animation_once)
try:
img = next(tkframe_iterator)
except StopIteration:
stop_animation()
else:
display.config(image=img)
root.bind('<space>', stop_animation)
# Now you can run show_animation() or run_animation_once() at your pleasure
root.after(1000, run_animation_once)
root.mainloop()
There are libraries, like imgpy, which supports GIF animation but I have no experience in usig any such library.
Addition
The duration variable sets the animation rate. To slow the rate down just increase the duration.
The simplest way to put the animation on a canvas it simply to put the label on a canvas, see example below:
# Replace this code
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
# with this code
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack(padx=10, pady=10)
display = tk.Label(canvas)
window = canvas.create_window(250, 250, anchor='center', window=display)
Then you don't have to change anything else in the program.

how to give dynamic value for area selection in imagegrab library in python

Using this script i am trying to take a screenshot of my desktop of a particular area.(using Tkinter gui)
But with this code i can only take screenshot of the fix area (frame) of desktop. So what i want to do is try to set the value of (bbox of imagegrab) dynamic.
And by dynamic i mean it should only capture the screen area which is selected(highlighted) by my mouse cursor any where on screen and can be of any size.
import tkinter as tk
from tkinter import *
from PIL import Image, ImageGrab
root = tk.Tk()
def area_sel():
# using the grab method
img = ImageGrab.grab(bbox = (400,500,400,500)) #i want these values to be dynamic
img.show()
sel_btn = tk.Button(root, text='select area', width=20, command=area_sel)
sel_btn.pack()
root.mainloop()
example image
here is what i am trying to do is to take coordinates from your code and then recording that particular area of screen.
def recording_screen():
global recording
recording = True
while recording:
sel=area_sel()
img = ImageGrab.grab(bbox=(Click_x, Click_y, Release_x, Release_y))
frame = np.array(img)
out.write(frame)
You can use Pillow to do what you want:
import tkinter as tk
from PIL import Image, ImageTk, ImageGrab, ImageEnhance
root = tk.Tk()
root.resizable(0, 0)
def show_image(image):
win = tk.Toplevel()
win.image = ImageTk.PhotoImage(image)
tk.Label(win, image=win.image).pack()
win.grab_set()
win.wait_window(win)
def area_sel():
x1 = y1 = x2 = y2 = 0
roi_image = None
def on_mouse_down(event):
nonlocal x1, y1
x1, y1 = event.x, event.y
canvas.create_rectangle(x1, y1, x1, y1, outline='red', tag='roi')
def on_mouse_move(event):
nonlocal roi_image, x2, y2
x2, y2 = event.x, event.y
canvas.delete('roi-image') # remove old overlay image
roi_image = image.crop((x1, y1, x2, y2)) # get the image of selected region
canvas.image = ImageTk.PhotoImage(roi_image)
canvas.create_image(x1, y1, image=canvas.image, tag=('roi-image'), anchor='nw')
canvas.coords('roi', x1, y1, x2, y2)
# make sure the select rectangle is on top of the overlay image
canvas.lift('roi')
root.withdraw() # hide the root window
image = ImageGrab.grab() # grab the fullscreen as select region background
bgimage = ImageEnhance.Brightness(image).enhance(0.3) # darken the capture image
# create a fullscreen window to perform the select region action
win = tk.Toplevel()
win.attributes('-fullscreen', 1)
win.attributes('-topmost', 1)
canvas = tk.Canvas(win, highlightthickness=0)
canvas.pack(fill='both', expand=1)
tkimage = ImageTk.PhotoImage(bgimage)
canvas.create_image(0, 0, image=tkimage, anchor='nw', tag='images')
# bind the mouse events for selecting region
win.bind('<ButtonPress-1>', on_mouse_down)
win.bind('<B1-Motion>', on_mouse_move)
win.bind('<ButtonRelease-1>', lambda e: win.destroy())
# use Esc key to abort the capture
win.bind('<Escape>', lambda e: win.destroy())
# make the capture window modal
win.focus_force()
win.grab_set()
win.wait_window(win)
root.deiconify() # restore root window
# show the capture image
if roi_image:
show_image(roi_image)
tk.Button(root, text='select area', width=30, command=area_sel).pack()
root.mainloop()
During selecting region:
Show the capture image after selecting region:
Use pynput is a way to do this(Maybe only use tkinter can do this,but I don't know),You only need to know the position of mouse button pressed and mouse button released:
Read more about pynput module
import tkinter as tk
# from tkinter import *
from PIL import Image, ImageGrab,ImageTk
from pynput import mouse
from pynput.keyboard import Key, Listener
def getPostion():
def on_click(x, y, button, pressed):
global Click_x, Click_y, Release_x, Release_y, STOP
if pressed:
Click_x = x
Click_y = y
else:
Keyboardlistener.stop()
Release_x = x
Release_y = y
STOP = False
return False
def on_release(key):
global STOP
if key == Key.esc:
Mouselistener.stop()
STOP = True
return False
with mouse.Listener(on_click=on_click) as Mouselistener, Listener(on_release=on_release) as Keyboardlistener:
Mouselistener.join()
Keyboardlistener.join()
root = tk.Tk()
def area_sel():
global Click_x, Click_y, Release_x, Release_y, STOP
Click_x, Click_y, Release_x, Release_y= 0,0,0,0
# using the grab method
top = tk.Toplevel() # create a toplevel
top.wm_attributes('-alpha',0.3)
top.state('zoomed') # make window fullscreen
top.overrideredirect(1)
# background = ImageTk.PhotoImage(image=ImageGrab.grab()) # get a screenshot
fullCanvas = tk.Canvas(top) # make a fullscreen canvas
# fullCanvas.create_image(xx,xx) # create a screenshot image in this canvas.
fullCanvas.pack()
top.update()
getPostion()
if Click_x and Click_y and Release_x and Release_y:
if STOP:
return False
top.withdraw()
img = ImageGrab.grab(bbox = (Click_x, Click_y, Release_x, Release_y))
img.show()
STOP = False
sel_btn = tk.Button(root, text='select area', width=20, command=area_sel)
sel_btn.pack()
root.mainloop()
edit now it is a complete tool can take a scroonshot,it is needn't to use pynput
full code :
import tkinter as tk
# from tkinter import *
from PIL import Image, ImageGrab, ImageTk
import ctypes, sys
if sys.getwindowsversion().major == 10:
ctypes.windll.shcore.SetProcessDpiAwareness(2) # Set DPI awareness
root = tk.Tk()
def area_sel():
def getPress(event): # get press position
global press_x,press_y
press_x,press_y = event.x,event.y
def mouseMove(event): # movement
global press_x, press_y, rectangleId
fullCanvas.delete(rectangleId)
rectangleId = fullCanvas.create_rectangle(press_x,press_y,event.x,event.y,width=5)
def getRelease(event): # get release position
global press_x, press_y, rectangleId
top.withdraw()
img = ImageGrab.grab((press_x, press_y,event.x,event.y))
img.show()
top = tk.Toplevel()
top.state('zoomed')
top.overrideredirect(1)
fullCanvas = tk.Canvas(top)
background = ImageTk.PhotoImage(ImageGrab.grab().convert("L"))
fullCanvas.create_image(0,0,anchor="nw",image=background)
# bind event for canvas
fullCanvas.bind('<Button-1>',getPress)
fullCanvas.bind('<B1-Motion>',mouseMove)
fullCanvas.bind('<ButtonRelease-1>',getRelease)
fullCanvas.pack(expand="YES",fill="both")
top.mainloop()
rectangleId = None
sel_btn = tk.Button(root, text='select area', width=20, command=area_sel)
sel_btn.pack()
root.mainloop()

Cannot move webcam video in between tkinter window

hi i am just trying to keep video frame using webcam in python GUI using tkinter module
how can i move video to down or to some other place in GUI and how to decrease the video show screen size
i am trying to change lmain = tk.Label(master=window) in main function to
lmain = tk.Label(master=window).place(x=500,y=500)
if i do that i am getting the following error
[ WARN:0] global C:\projects\opencv-python\opencv\modules\videoio\src\cap_msmf.cpp (674)
SourceReaderCB::~SourceReaderCB terminating async callback
#This Python program is developed in order to open an internal camera and display the image within Tkinter window.
#importing modules required
from ttk import *
import tkinter as tk
from tkinter import *
import cv2
from PIL import Image, ImageTk
import os
import numpy as np
global last_frame #creating global variable
last_frame = np.zeros((480, 640, 3), dtype=np.uint8)
global cap
cap = cv2.VideoCapture(0)
def show_vid(): #creating a function
if not cap.isOpened(): #checks for the opening of camera
print("cant open the camera")
flag, frame = cap.read()
frame = cv2.flip(frame, 1)
if flag is None:
print("Major error!")
elif flag:
global last_frame
last_frame = frame.copy()
pic = cv2.cvtColor(last_frame, cv2.COLOR_BGR2RGB) #we can change the display color of the frame gray,black&white here
img = Image.fromarray(pic)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_vid)
if __name__ == '__main__':
window=tk.Tk() #assigning window variable for Tkinter as tk
lmain = tk.Label(master=window)
lmain.grid(column=0, rowspan=4, padx=5, pady=5)
window.title("Sign Language Processor") #you can give any title
window.geometry("1366x768")
show_vid()
window.mainloop() #keeps the application in an infinite loop so it works continuosly
cap.release()

Categories