How to open the cv2 window inside a tkinter window - python

I found on the internet this python code for counting people with the Open CV library. I would need to open the window that opens with CV2, inside a Tkinter window in order to then add the commands for the settings.
This is the code from GitHub: https://github.com/Gupu25/PeopleCounter
How can I make the two OpenCv windows open inside a Tkinter window?

Here is a minimal example of opening a video in a tkinter window with OpenCV's VideoCapture object:
from tkinter import NW, Tk, Canvas, PhotoImage
import cv2
def photo_image(img):
h, w = img.shape[:2]
data = f'P6 {w} {h} 255 '.encode() + img[..., ::-1].tobytes()
return PhotoImage(width=w, height=h, data=data, format='PPM')
def update():
ret, img = cap.read()
if ret:
photo = photo_image(img)
canvas.create_image(0, 0, image=photo, anchor=NW)
canvas.image = photo
root.after(15, update)
root = Tk()
root.title("Video")
cap = cv2.VideoCapture("video.mp4")
canvas = Canvas(root, width=1200, height=700)
canvas.pack()
update()
root.mainloop()
cap.release()
To display 2 OpenCV videos, simply make a few adjustments, and use the np.hstack() method or the np.vstack() method, depending on whether you want your videos to be displayed side by side horizontally or vertically:
from tkinter import NW, Tk, Canvas, PhotoImage
import cv2
import numpy as np
def photo_image(img):
h, w = img.shape[:2]
data = f'P6 {w} {h} 255 '.encode() + img[..., ::-1].tobytes()
return PhotoImage(width=w, height=h, data=data, format='PPM')
def update():
ret1, img1 = cap1.read()
ret2, img2 = cap2.read()
if ret1:
photo = photo_image(np.hstack((img1, img2)))
canvas.create_image(0, 0, image=photo, anchor=NW)
canvas.image = photo
root.after(15, update)
root = Tk()
root.title("Video")
cap1 = cv2.VideoCapture("video1.mp4")
cap2 = cv2.VideoCapture("video2.mp4")
canvas = Canvas(root, width=1200, height=700)
canvas.pack()
update()
root.mainloop()
cap.release()

Related

How do I keep this image in the center of the window?

I'm making a hangman-like game; for that, I need all the different stages of the man to be visualized, hence images. I want the entire tkinter window to be an image, when I change the size it pushes the image right.
from tkinter import *
root=Tk()
root.geometry("600x350")
canvas = Canvas(root, width=1600, height=900)
canvas.pack()
img = PhotoImage(file="4.png")
canvas.create_image(470,190, image=img, )
root.mainloop()
If canvas is bigger than window then when you resize then it show more canvas but and it can looks like it moves image.
But if you use smaller canvas then pack() will try to keep centered horizontally. And if you add pack(expand=True) then it will try to keep it centered vertically.
In example code I added red background to window to show where is canvas
import tkinter as tk # PEP8: import *
root = tk.Tk()
root.geometry("600x350")
root['bg'] = 'red'
img = tk.PhotoImage(file="lenna.png")
canvas = tk.Canvas(root, width=600, height=350)
canvas.pack(expand=True)
canvas.create_image(300, 175, image=img)
root.mainloop()
Image Lenna from Wikipedia
PEP 8 -- Style Guide for Python Code
Before resizing:
After resizing:
If you want to draw only image then you could use Label(image=img)
import tkinter as tk # PEP8: import *
root = tk.Tk()
root.geometry("600x350")
root['bg'] = 'red'
img = tk.PhotoImage(file="lenna.png")
label = tk.Label(root, image=img)
label.pack(expand=True)
root.mainloop()
Before resizing:
After resizing:
BTW:
tkinter can bind() some function to event <Configure> and it will execute this function everytime when you resize window (and/or move window) - and this function may also move or resize image in window.
import tkinter as tk # PEP8: import *
from PIL import Image, ImageTk
def resize(event):
global img
lower = min(event.width, event.height)
#print(event.width, event.height, 'lower:', lower)
new_image = original_image.resize((lower, lower))
img = ImageTk.PhotoImage(new_image) # need to assign to global variable because there is bug in PhotoImage
label['image'] = img
# --- main ---
root = tk.Tk()
root.geometry("350x350")
root['bg'] = 'red'
original_image = Image.open("lenna.png")
img = ImageTk.PhotoImage(original_image)
label = tk.Label(root, image=img)
label.pack(expand=True)
root.bind('<Configure>', resize)
root.mainloop()
Before (it resized image at start to fit window):
After (it resized image to fit window):

Problems connecting Tello Drone camera in Python for a GUI

I'm a very Python and Tkinter beginner. I've developed a program for Tello Drone in Python 3.6. The program works well but now I'm programming the user interface with Tkinter.
When i use the image from the laptop camera it works well, but when I try to connect drone's camera as I've aready do in all my projects, I've next error:
succes, img = me.get_frame_read().frame
TypeError: 'NoneType' object is not iterable
I know what's a NoneType, but I dont't understand why is getting this value. Looks like the camera have a delay or is not ready when the program starts.
This's a sample of the code:
from tkinter import *
import tkinter as tk
from PIL import ImageTk, Image
import cv2
#img = img3
# Capture from camera
from djitellopy import tello
'''
me = tello.Tello()
me.connect()
print(me.get_battery())
me.send_rc_control(0,0,0,0)
me.streamon()
#'''
w,h = 660, 360
#succes, img = me.get_frame_read().frame
cap = cv2.VideoCapture(0)
if __name__ == '__main__':
def return_pressed(event):
print('Return key pressed.')
root = Tk()
root.title("Inventory control")
root.iconbitmap(r'Ico/drone3.ico')
# Create a frame
app = Frame(root, bg="white")
app.grid(row=1, column=0)
# Create a label in the frame
lmain = Label(app)
lmain.grid()
droneCamera_label = Label(root, text="Drone camera", font=("Arial 20 bold"))
textBox_label = Label(root, text="Inventory results", font=("Arial 20 bold"))
textBox_label.grid(row=0, column=2)
droneCamera_label.grid(row=0, column=0)
text_box = Text(root, height=25, width=40)
text_box.grid(row=1, column=2)
text_box.config(font=("consolas", 12), undo=True, wrap='word')
text_box.config(borderwidth=3, relief="sunken")
text_box.insert('end', "Inventory results:", )
text_box.config(state='disabled')
scrollb = tk.Scrollbar(command=text_box.yview)
scrollb.grid(row=1, column=3, sticky='nsew')
text_box['yscrollcommand'] = scrollb.set
def manualControl(event):
global message
text_box.config(state='normal')
print(1)
text_box.insert(tk.INSERT, "l")
text_box.config(state='disabled')
# function for video streaming
def video_stream():
_, frame = cap.read()
# succes, frame = me.get_frame_read().frame
img = cv2.resize(frame, (w, h))
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img2 = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img2)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
root.bind('<Button-1>', manualControl)
video_stream()
root.mainloop()
And this's how GUI looks like with the laptop webcam:
GUI
I belive .frame is a function that turns the tello 'backgroundframeread' type into a cv2/numpy array.
Your tello might be failing to grab the first few groups of frames, if it has no frame then the objects value is NONE. If you attempt to .frame an object you will get TypeError: 'NoneType' object is not iterable.
I think you can solve this by putting a delay between your framecapture and your .frame that is as long as your tello, or camera takes to grab a frame. A time.sleep() is usually a safe bet.
try this:
img = me.get_frame_read()
time.sleep(5)
img = img.frame

Why does this code make the Tkinter window continuously resize/grow automatically?

It was supposed to make the image label resize with the window, but the whole window (along with the image) resizes with a mind of its own.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, height=500, width=500)
canvas.pack()
from PIL import Image, ImageTk
def resize_image(event):
new_width = event.width
new_height = event.height
image = copy_of_image.resize((new_width, new_height))
photo = ImageTk.PhotoImage(image)
label.configure(image = photo)
label.image = photo
image = Image.open("*file path to image*")
copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
label = tk.Label(canvas, image=photo)
label.bind('<Configure>', resize_image)
label.pack(fill='both', expand='yes')
root.mainloop()
You are calling a function whenever the widget changes size. In that function, you are resizing an image to be the size of the label. However, there is likely a border or padding around the image, so this causes the label to grow slightly. Because the label grows, it triggers the <Configure> event which again resizes the image, which causes the label to change size, which triggers the <Configure> event which again resizes the image, which causes the label to change size, ....
The solution is to make sure that the borderwidth and highlightthickness are zero, and/or resize the image to be a couple pixels smaller than the label so that you don't force the label to grow.
Ok after some testing I think I have found the solution you are looking for.
Instead of placing the label on the canvas we can place the label in a frame and then resize the frame to match the canvas size. At the same time we can resize the image in the label and even if it is slightly larger than the frame it will trigger another resizing until you resize the window directly.
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
canvas = tk.Canvas(root, height=500, width=500)
canvas.pack(fill='both', expand=True)
frame = tk.Frame(canvas)
canvas.create_window((0, 0), anchor='nw', window=frame, tags='my_frame')
def resize_image(event):
canvas.itemconfigure("my_frame", width=event.width, height=event.height)
image = copy_of_image.resize((event.width, event.height ))
photo = ImageTk.PhotoImage(image)
label.configure(image=photo)
label.image = photo
image = Image.open("*file path to image*")
copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
label = tk.Label(frame, image=photo)
label.pack(fill='both', expand=True)
canvas.bind('<Configure>', resize_image)
root.mainloop()
For an OOP solution see below:
import tkinter as tk
from PIL import Image, ImageTk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.canvas = tk.Canvas(self)
self.frame = tk.Frame(self.canvas)
self.canvas.pack(fill='both', expand=True)
image = Image.open('./Images/green.png')
self.copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
self.label = tk.Label(self.frame, image=photo)
self.label.pack(fill='both', expand=True)
self.label.image = photo
self.canvas.create_window((0, 0), anchor='nw', window=self.frame, tags='my_frame')
self.canvas.bind("<Configure>", self.on_canvas_configure)
def on_canvas_configure(self, event):
self.canvas.itemconfigure("my_frame", width=event.width, height=event.height)
image = self.copy_of_image.resize((event.width, event.height))
photo = ImageTk.PhotoImage(image)
self.label.configure(image=photo)
self.label.image = photo
if __name__ == "__main__":
App().mainloop()

python 3 tkinter images

I have set up a Rasbperry Pi 3 and using Python 3. I can take images but need to refresh them on screen. VERY new to tkinker. I have some code but would like to know how to place a nice sized image on the canvas and to refresh it when one of my buttons is pressed.
#!/usr/bin/python
import tkinter
import RPi.GPIO as GPIO
import picamera
import time
from PIL import Image, ImageTk
def startup():
def callback(a):
if a==4:
thiscolour="yellow"
camera.capture('/home/pi/Desktop/image.jpg')
if a==2:
thiscolour="red"
lbl.configure(text=thiscolour+" has been pressed")
path = "/home/pi/Desktop/image.jpg"
img = Image.open(path)
new_width = 400
new_height = 600
#img = img.resize((new_width, new_height), Image.ANTIALIAS)
img.save('thisimage.jpg')
path="thisimage.jpg"
path="thisimage.jpg"
image = Image.open(path)
photo = ImageTk.PhotoImage(image)
img = ImageTk.PhotoImage(Image.open(path))
panel.configure(image=photo)
panel.pack()
camera = picamera.PiCamera()
path = "/home/pi/Desktop/image.jpg"
img = Image.open(path)
new_width = 400
new_height = 600
img = img.resize((new_width, new_height), Image.ANTIALIAS)
img.save('thisimage.jpg')
path="thisimage.jpg"
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
buttonyellow = 4
buttonred = 2
t=0
GPIO.setup(buttonyellow, GPIO.IN, GPIO.PUD_UP)
GPIO.setup(buttonred, GPIO.IN, GPIO.PUD_UP)
window=tkinter.Tk()
window.title("Photobooth")
window.geometry("1000x800")
lbl=tkinter.Label(window,text="Instructions")
ent=tkinter.Entry(window)
btn=tkinter.Button(window,text="Press here", bg='red')
btn=tkinter.Button(window,text="Click me",bg='red',command=callback)
btn.pack()
lbl.pack()
ent.pack()
btn.pack()
GPIO.add_event_detect(buttonyellow, GPIO.FALLING, callback=callback, bouncetime=100)
GPIO.add_event_detect(buttonred, GPIO.FALLING, callback=callback, bouncetime=100)
path="thisimage.jpg"
image = Image.open(path)
photo = ImageTk.PhotoImage(image)
img = ImageTk.PhotoImage(Image.open(path))
panel = tkinter.Label(window, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
window.mainloop()
startup()
The image is displayed in a label. The update button triggers a command that first take the new picture (or whatever you want, I haven't written this part of the code), then loads the new image and finally updates the image inside the label using configure(image=...).
import tkinter as tk
from PIL import Image, ImageTk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self) # create window
# load initial image
self.img = ImageTk.PhotoImage(Image.open("path/to/image"))
# display it in a label
self.label = tk.Label(self, image=self.img)
self.label.pack(fill='both', expand=True)
tk.Button(self, text="Update", command=self.update_image).pack()
self.mainloop()
def update_image(self):
# code to capture new image here
# ...
# load new image
self.img = ImageTk.PhotoImage(Image.open("path/to/image"))
# update label image
self.label.configure(image=self.img)
if __name__ == '__main__':
App()

Problems displaying video in tkinter

I'm trying to create a GUI that opens a video and an image below it:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
import Image, ImageTk
import Tkinter, tkMessageBox
import ttk
import cv2
import sys
width, height = 800, 600
banner = cv2.imread('../data/banner.png')
b,g,r = cv2.split(banner)
banner = cv2.merge((r,g,b))
im = Image.fromarray(banner)
cap = cv2.VideoCapture('../data/sample.mov')
root = Tkinter.Tk()
root.bind('<Escape>', lambda e: root.quit())
root.title("Contador")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
lmain = Tkinter.Label(root)
lmain.grid(row=0,column=0,sticky='nsew')
bmain = Tkinter.Label(root)
bmain.grid(row=1,column=0,sticky='nsew')
baner = ImageTk.PhotoImage(image=im)
bmain.configure(image=baner)
def show_frame():
_, frame = cap.read()
if frame is None:
return
# labelWidth = root.winfo_screenwidth()
# labelHeight = root.winfo_screenheight()
# maxsize = (labelWidth, labelHeight)
# frame = frame.resize(maxsize)
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(frame)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_frame)
show_frame()
root.mainloop()
The problems I'm having are the following:
I need to resize the image to fit the label.
the commented part i got from here (how to fit image to label in Python) but it gives a channel number error(line 40) and further down the code gives a NoneType error(line 41) and a invalid type of image(numpy array) in line 42
the image and video don't change size when resizing the window
So I need solution for this tkinter code(or even a better framework for python)
I found out what the problem was.
Turns out the line I got from another question:
frame = frame.resize(maxsize)
should be:
frame = cv2.resize(frame, maxsize)
Also to get the label's size the command is:
labelWidth = lmain.winfo_width()
labelHeight = lmain.winfo_height()
since
labelWidth = root.winfo_width()
labelHeight = root.winfo_height()
gets the root's size not the label's.

Categories