updating image in window using loop - python

from PIL import Image, ImageTk
import numpy as np
import tkinter as tk
import time
def put_pixel(row, col):
pixels[row, col] = [255, 255, 255]
width = 20
height = 10
pixels = np.full((height, width, 3), 0, dtype=np.uint8)
root = tk.Tk()
root.geometry("300x300")
root.configure(background='grey')
img = ImageTk.PhotoImage(Image.open("maze.png"))
panel = tk.Label(root, image=img)
panel.pack(side = "top")
b = tk.Button(root, text="Sure!")
b.pack(side="bottom", fill="both")
for i in range(1, width-2, 2):
put_pixel(5, i)
time.sleep(2)
img = Image.fromarray(pixels, 'RGB')
panel.configure(image=img)
panel.image = img
root.mainloop()
The script just adds white pixels on a black image. But I want it to be animated to see the adding of every pixel step by step. So I tried to update an image in a label after every single pixel being added. But I got an error
_tkinter.TclError: image "" doesn't exist
If I don't use loop and just put an image into a label it works fine. How do I fix that?

You need to convert the PIL.Image into a tkinter.PhotoImage:
img = Image.fromarray(pixels, 'RGB')
tkimg = ImageTk.PhotoImage(img)
panel.configure(image=tkimg)
panel.image = img
tkinter.Label will only accept this type, not PIL images.

Related

UnboundLocalError: local variable 'input_img' referenced before assignment Tkinter

I tried to detect blue color from entry input image using both Tkinter and OpenCV where when 'Display Image 2' button is pressed second window will display and I was expecting the image where blue color from image is detected to displays on the second window but turned out it doesn't. Why?
Output Error: UnboundLocalError: local variable 'input_img' referenced before assignmentTkinter
from tkinter import *
from PIL import ImageTk, Image
import tkinter as tk
import cv2
import numpy as np
window = Tk()
window.title("Color detector")
window.geometry("800x500")
input_img = tk.Entry(window)
input_img.pack()
def detect_color():
new_wind = Toplevel()
new_wind.title('Ur mom')
# Get Image
input_img = str(input_img.get())
global img
path = r'C:\\users\\HP\\Documents\\' + input_img
img = ImageTk.PhotoImage(Image.open(path))
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_blue = np.array([[90,50,50]])
upper_blue = np.array([[130,255,255]])
mask = cv2.inRange(hsv,lower_blue,upper_blue)
result = cv2.bitwise_and(img,img,mask=mask)
display_result = Label(new_wind,image=result)
display_result.pack()
display_img = tk.Button(window, text='Display Image 2',command=detect_color)
display_img.pack()
window.mainloop()
So this is the image I use in this case:
The output:
Output I expected:

How to open the cv2 window inside a tkinter window

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()

Mouse events on cv2 images in Tkinter window

I'm working on a program that allows you to free-form crop an image and simultaneously show you the resultant image. The following code achieves this.
import numpy as np
import cv2
def crop(pts):
global res
pts = np.array(pts)
mask = np.zeros((height, width), dtype=np.uint8)
cv2.fillPoly(mask, [pts], (255))
res = cv2.bitwise_and(img,img,mask = mask)
rect = cv2.boundingRect(pts) # returns (x,y,w,h) of the rect
cropped = res[rect[1]: rect[1] + rect[3], rect[0]: rect[0] + rect[2]]
cv2.imshow('res', res)
def mouse_callback(event, x, y, flags, params):
global selecting, pts, img
if event == cv2.EVENT_LBUTTONDOWN:
selecting = True
elif event == cv2.EVENT_MOUSEMOVE:
if selecting == True:
pts.append((x, y))
crop(pts)
print((x,y))
elif event == cv2.EVENT_LBUTTONUP:
selecting = False
res = None
selecting = False
pts = []
img = cv2.imread("girl.png", -1)
height = img.shape[0]
width = img.shape[1]
cv2.namedWindow('image')
cv2.setMouseCallback('image', mouse_callback)
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.imwrite("res.png", res)
My challenge is to encapsulate the above code into an interface as follows:
So far I have followed a tutorial to achieve something similar:
# import the necessary packages
from tkinter import *
from PIL import Image
from PIL import ImageTk
from tkinter import filedialog as tkf
import cv2
def select_image():
# grab a reference to the image panels
global panelA, panelB
# open a file chooser dialog and allow the user to select an input
# image
path = tkf.askopenfilename()
# ensure a file path was selected
if len(path) > 0:
# load the image from disk, convert it to grayscale, and detect
# edges in it
image = cv2.imread(path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.Canny(gray, 50, 100)
# OpenCV represents images in BGR order; however PIL represents
# images in RGB order, so we need to swap the channels
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# convert the images to PIL format...
image = Image.fromarray(image)
edged = Image.fromarray(edged)
# ...and then to ImageTk format
image = ImageTk.PhotoImage(image)
edged = ImageTk.PhotoImage(edged)
# if the panels are None, initialize them
if panelA is None or panelB is None:
# the first panel will store our original image
panelA = Label(image=image)
panelA.image = image
panelA.pack(side="left", padx=10, pady=10)
# while the second panel will store the edge map
panelB = Label(image=edged)
panelB.image = edged
panelB.pack(side="right", padx=10, pady=10)
# otherwise, update the image panels
else:
# update the pannels
panelA.configure(image=image)
panelB.configure(image=edged)
panelA.image = image
panelB.image = edged
# initialize the window toolkit along with the two image panels
root = Tk()
panelA = None
panelB = None
# create a button, then when pressed, will trigger a file chooser
# dialog and allow the user to select an input image; then add the
# button the GUI
btn = Button(root, text="Select an image", command=select_image)
btn.pack(side="bottom", fill="both", expand="yes", padx="10", pady="10")
# kick off the GUI
root.mainloop()
What I have not managed to accomplish is implement the mouse event in tkinter, as the function takes in a window name - but I only want it to get applied to the image. How can I achieve this?
Instead of bind your command to your image, bind it to the tk.Label that contains the image:
import tkinter as tk
from PIL import Image
from PIL import ImageTk
root = tk.Tk()
ima = Image.open('test.png')
imag = ImageTk.PhotoImage(ima)
lab = tk.Label(root, image=imag)
lab.pack()
lab.bind('<Button-1>', *function)
root.mainloop()
Explaination
Tkinter provides a powerful mechanism to let you deal with events
yourself. For each widget, you can bind Python functions and methods
to events.
(...)
Events are given as strings, using a special event syntax:

Cropping an image in tkinter

I'm using tkinter and I have a "sprite sheet" and I want to cut it into multiple images. I tried PIL:
img = Image.open("test.png").convert("RGBA")
img2 = img.crop([300,300,350,350])
image = ImageTk.PhotoImage(img2)
win = tk.Tk()
label = tk.Label(win, image = image)
label.pack()
but on my window, there is only an empty white rectangle and I don't understand why. Moreover I tried img2.show() just to make shure that img2 wasn't empty and it wasn't.
Here is your code, with a few changes. Note the call to Tk() at the top, and mainloop() at the bottom. The other modification is that it obtains the width and height of the image and then crops 25% from each of the four sides to leave the middle 50% of the image.
#!/usr/bin/python
from tkinter import *
from PIL import ImageTk,Image
root = Tk()
img = Image.open("test.png").convert("RGBA")
w, h = img.size
left = w/4
right = 3*w/4
upper = h/4
lower = 3*h/4
img2 = img.crop([ left, upper, right, lower])
image = ImageTk.PhotoImage(img2)
label = Label(root, image = image)
label.pack()
root.mainloop()

tkinter - When resizing color image, it shows up in red scale only

I have the following piece of code:
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk
import cv2
import os
import glob
import numpy as np
image_path = ""
image_list = []
class Proj:
def __init__(self, master):
self.master = master
#GUI height and width
w = 1250
h = 600
# open folder manager to select image folder
image_path = filedialog.askdirectory()
master.geometry("%dx%d%+d%+d" % (w ,h ,0,0))
master.resizable(True,True)
#read in images from folder
self.read_images(master, image_path)
#cv2.imshow('image',cv2.imread(image_list[0], 1))
self.img = cv2.imread(image_list[0])
self.img = Image.fromarray(np.array(self.img).copy())
self.img.thumbnail((w//2, w//2+10))
self.img = ImageTk.PhotoImage(self.img)
image_frame = Frame(master)
image_frame.grid(row = 0, column = 0, columnspan = 3, rowspan = 5)
left_panel = Canvas(image_frame, width=w//2, height=h-70)
left_panel.grid(row=0, column=0, columnspan=4)
imgA_handler = left_panel.create_image((0,0), image = self.img, anchor="nw")
right_panel = Canvas(image_frame, width=w//2, height=h-70)
right_panel.grid(row=0, column=5, columnspan=4)
def read_images(self, master, path):
images = path + '/*.tif'
for img in glob.glob(images): #will only read in tif images
image_list.append(img)
root = Tk()
example = Proj(root)
root.mainloop()
I am reading in color .tif images and then trying to display them in the left_panel. However, when I go to do that, it shows the normal color image only in red scale even though I never extracted just the red signal. I am completely unable to diagnose the issue. How do I fix this issue?
Ultimately, what I want to do is display two images on this GUI. An original image on the left and a modified image on the right. Currently my gui layout is set up as I've coded above. However, if you think there is an easier way to do this, then I'd be interested to hear.
This is because opencv uses BGR instead of RGB. When you are using this line: self.img = Image.fromarray(np.array(self.img).copy()) Blue and Red colors are being swapped. Just before you use the above code, convert the BGR to RGB in opencv and you should be good to go.
self.img = cv2.cvtColor(self.img,cv2.COLOR_BGR2RGB)
I see you are using opencv. Why don't you use the opencv command to resize.
It is very simple and works pretty well.
Take a look at this page:
OpenCV Resize
This is an example to make a image 50% smaller, or instead of the fx and fy, you can put the exact size you want.
thumbnail = cv2.resize(image, (0,0), fx=0.5, fy=0.5)

Categories