I'm currently trying to setup a GUI in Tkinter so that I can show a sequence of images (named file01.jpg, file02.jpg, etc. etc.). Currently I'm doing it by creating a Sequence object to manage the list of images that I care about:
class Sequence:
def __init__(self,filename,extension):
self.fileList = []
#takes the current directory
listing = os.listdir(os.getcwd())
#and makes a list of all items in that directory that contains the filename and extension
for item in listing:
if filename and extension in item:
self.fileList.append(item)
#and then sorts them into order
self.fileList.sort()
print self.fileList
def nextImage(self):
#returns a string with the name of the next image
return self.fileList.pop(0)
And then I'm using a rather simple Tkinter script I found online to generate the window and place an image there:
window = Tkinter.Tk()
window.title('Image Analysis!')
sequence = Sequence('test','jpg')
image = Image.open("test01.jpg")
image = image.convert('L')
imPix = image.load()
canvas = Tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
window.bind("<space>", lambda e: nextFrame(sequence_object=sequence,event=e))
Tkinter.mainloop()
where nextFrame is defined as:
def nextFrame(sequence_object,event=None):
nextImage = sequence_object.nextImage()
print 'Next Image is: ',nextImage
image = Image.open(nextImage)
image = image.convert('L')
imPix = image.load()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.update()
In my python buffer I see the correct image sequence pop up ('Next Image is: test02,jpg', etc.) but the new image never pops up!
Does anyone have any explanation for why the image doesn't pop up?
Thanks!
nathan lachenmyer
Probably what is happening is that the image is getting destroyed by the garbage collector since the only reference to the image is a local variable.
Try keeping a permanent reference to the image, for example:
...
self.image_tk = ImageTk.PhotoImage(image)
...
Related
I am preparing data for deep running. So I have to get certain pixel coordinates for each picture. Only one coordinate per photo is required.
So when I use PIL to input one click, I try to implement coordinates so that I can go to the next picture.
However, when I write the code as below, the coordinates are output in duplicate to only one picture, and the next picture does not appear on the screen.
How can I make sure that only one coordinate is recorded on a single picture?
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
print(imgList)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
def callback(event):
print("clicked at: ", event.x, event.y)
window = tkinter.Tk(className='pla')
for file in sorted(imgList):
a=True
image = Image.open(os.path.join(URL, file))
print(image)
canvas = tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
tkinter.mainloop()
I am not 100% sure I understand what you need but it looks like to me you are trying to get one set of cord's for each image in a list of images.
I would do this by creating a function and a tracking variable to loop through each image on at a time and on click update a new list with the image and cord's then loop to next image.
Let me know if you have any questions.
Example:
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
tracker = 0
list_images_with_cords = [] # added list for final results
def callback(event):
# Added global's.
global tracker,list_images_with_cords
# Used to append final results to list.
list_images_with_cords.append([imgList[tracker], event.x, event.y])
# This tracker lets us go through each item on the list.
tracker += 1
# After appending list go to next image.
open_next()
window = tkinter.Tk(className='pla')
# Creates just one canvas that we can update later.
canvas = tkinter.Canvas(window)
canvas.pack()
def open_next():
# Adding global's.
global image, canvas, image_tk, tracker
# Clearing canvas before drawing new image.
canvas.delete("all")
# Checking for valid index in list.
if tracker < len(imgList):
image = Image.open(os.path.join(URL, imgList[tracker]))
# use config() to update canvas.
canvas.config(width=image.size[0], height=image.size[1])
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
else:
# This else statement is just for when we run out of images.
# It will display all the results in a textbox.
canvas.destroy()
txt = tkinter.Text(window, width=25)
txt.pack()
for item in list_images_with_cords:
txt.insert("end", "{}\n\n".format(item))
open_next()
tkinter.mainloop()
I am trying to use Tkinter to display an image in Python 2.7. Below is an example of a code I tried to use, which displays all images in the folder (from
http://code.activestate.com/recipes/521918-pil-and-tkinter-to-display-images/ ):
import os, sys
import Tkinter
import Image, ImageTk
def button_click_exit_mainloop (event):
event.widget.quit() # this will cause mainloop to unblock.
root = Tkinter.Tk()
root.bind("<Button>", button_click_exit_mainloop)
root.geometry('+%d+%d' % (100,100))
dirlist = os.listdir('.')
old_label_image = None
for f in dirlist:
try:
image1 = Image.open(f)
root.geometry('%dx%d' % (image1.size[0],image1.size[1]))
tkpi = ImageTk.PhotoImage(image1)
label_image = Tkinter.Label(root, image=tkpi)
label_image.place(x=0,y=0,width=image1.size[0],height=image1.size[1])
root.title(f)
if old_label_image is not None:
old_label_image.destroy()
old_label_image = label_image
root.mainloop() # wait until user clicks the window
except Exception, e:
# This is used to skip anything not an image.
# Image.open will generate an exception if it cannot open a file.
# Warning, this will hide other errors as well.
pass
For instance, a cross (top) will lead to the following displayed image in the Tkinter window (bottom):
I have tried other codes (including some to display live my webcam), they all lead to the same result: I can see an image, it has the correct dimension, but each column has a constant pixel value (the one of the corresponding column of the first row of the original image).
I have a folder of images which I add to a list and process. At the end of the process, I would like to display the images next to each other in a TkInter window, at present with the code I have, only the last image in the folder is displayed. I'm also using only one panel to display these images and I'm passing a list of images to the panel.image method but I suppose it's the wrong way to do it, because it does not work
images = list()
for item in os.listdir(mypath):
image = cv2.imread(os.path.join(mypath, item))
if image is not None:
if image.shape[0] > image.shape[1]:
image = imutils.rotate_bound(image, 90)
images.append(image)
r = 400.0 / image.shape[1]
dim = (400, int(image.shape[0] * r))
resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
###[Image processing]###
imageslist = list()
original = Image.fromarray(resized)
original = ImageTk.PhotoImage(original)
imageslist.append(original)
if panelA is None:
for image in imageslist:
panelA = Label(image=image)
panelA.image = image
panelA.pack(side="top", padx=10, pady=10)
else:
for image in imageslist:
panelA.configure(image=image)
panelA.image = image
root = Tk()
panelA = None
root.mainloop()
UPDATE
Now, with the code below, all images are shown (vertically though) however, whenever i reset the loop with the click of a button (in order to process images from another folder) the first image from the first folder remainst there, while the rest of the images are indeed from the new folder, this is very strange and i really can't understand why this happens, I destroyed the panel, i checked the list and it contains the images from the new folder after every loop, but that first images is constantly sitting there for some reason
if panelA is None:
# the first panel will store our original image
for image in imageslist:
panelA = Label(image=image)
panelA.image = image
panelA.pack(side="top", padx=10, pady=10)
# otherwise, update the image panels
else:
panelA.destroy()
for image in imageslist:
panelA = Label(image=image)
panelA.image = image
panelA.pack(side="top", padx=10, pady=10)
Here's how it looks like, first loop:
Second loop, images are taken from another folder, first image from first folder is still there at the top, that image is not in the new folder and the list has been cleared at the beginning of the second loop so it's not taken from imageslist:
I am trying to Make a simple Photo editor. I already got all the functions for editing photos. Just trying to make it look nice with a GUI. When the user presses file>open, the open dialogue box comes up and they can choose an image. Then I want that image to load inside of a Layer in the GUI.
Here is my code so far:
p.s. When I put the code that is inside the open_img function outise the function, it works, but when i put it inside the function, no image loads
import os
from tkinter import *
import tkinter.messagebox
from PIL import Image, ImageTk
from tkinter.filedialog import askopenfilename
def g_quit():
mExit=tkinter.messagebox.askyesno(title="Quit", message="Are You Sure?")
if mExit>0:
mGui.destroy()
return
#open menu
def open_img():
file = tkinter.filedialog.askopenfilename(initialdir='D:/Users/')
w_box = 500
h_box = 500
pil_image = Image.open(file)
w, h = pil_image.size
pil_image_resized = resize(w, h, w_box, h_box, pil_image)
# wr, hr = pil_image_resized.size
tk_image = ImageTk.PhotoImage(pil_image_resized)
label2.config(image=tk_image, width=w_box, height=h_box)
def resize(w, h, w_box, h_box, pil_image):
'''
resize a pil_image object so it will fit into
a box of size w_box times h_box, but retain aspect ratio
'''
f1 = 1.0*w_box/w # 1.0 forces float division in Python2
f2 = 1.0*h_box/h
factor = min([f1, f2])
#print(f1, f2, factor) # test
# use best down-sizing filter
width = int(w*factor)
height = int(h*factor)
return pil_image.resize((width, height), Image.ANTIALIAS)
mGui = Tk()
mGui.title('Photo Filters')
mGui.geometry('650x500')
mGui.resizable(0, 0) #Disable Resizeability
photoFrame = Frame(mGui, bg="orange", width=500, height=500)
photoFrame.pack(side=LEFT)
filtersFrame = Frame(mGui, bg="yellow", width=150, height=500)
filtersFrame.pack(side=LEFT, fill=Y)
label2 = Label(photoFrame)
#Create Buttons for All the Possible Filters
negative_btn = Button(filtersFrame, text="Negative")
negative_btn.pack()
weighted_grayscale_btn = Button(filtersFrame, text="weighted Grayscale")
weighted_grayscale_btn.pack()
solarize_btn = Button(filtersFrame, text="Solarize")
solarize_btn.pack()
black_and_white_btn = Button(filtersFrame, text="Black and White")
black_and_white_btn.pack()
black_and_white_and_gray_btn = Button(filtersFrame, text="Black and White and Gray")
black_and_white_and_gray_btn.pack()
extreme_contrast_btn = Button(filtersFrame, text="Extreme Contrast")
extreme_contrast_btn.pack()
sepia_tint_btn = Button(filtersFrame, text="Sepia Tint")
sepia_tint_btn.pack()
adjust_component_btn = Button(filtersFrame, text="Adjusts Components")
adjust_component_btn.pack()
posterize_btn = Button(filtersFrame, text="Posterize")
posterize_btn.pack()
simplify_btn = Button(filtersFrame, text="Simplify")
simplify_btn.pack()
detect_edges_btn = Button(filtersFrame, text="Detect Edges")
detect_edges_btn.pack()
detect_edges_better_btn = Button(filtersFrame, text="Detect Edges Better")
detect_edges_better_btn.pack()
blur_btn = Button(filtersFrame, text="Blur")
blur_btn.pack()
flip_image = Button(filtersFrame, text="Flip Horizontal")
flip_image.pack()
#Menu Bar
menubar = Menu(mGui)
filemenu = Menu(menubar)
#Create the Menu Options that go under drop down
filemenu.add_command(label="New")
filemenu.add_command(label="Open", command=open_img)
filemenu.add_command(label="Save As")
filemenu.add_command(label="Close", command=g_quit)
#Create the Main Button (e.g file) which contains the drop down options
menubar.add_cascade(label="File", menu=filemenu)
mGui.config(menu=menubar)
mGui.mainloop()
You've actually got two problems here. One of them, you found for yourself—you never call label2.pack(), so the label itself never gets displayed. But, even after you fix that, the label is blank.
As the Label documentation says:
You can use the label to display PhotoImage and BitmapImage objects. When doing this, make sure you keep a reference to the image object, to prevent it from being garbage collected by Python’s memory allocator.
You're not doing that. You store the PhotoImage in a local variable, tk_image, which goes away as soon as the function exits.
That should explain why it works if you move the code outside a function. If you do that, tk_image becomes a global variable, which doesn't go away until the whole program exits. And, since you're already using global variables all over the place implicitly, one possible fix is to use an explicit global for this:
def open_img():
global tk_image
# the rest of your code
However, a much better trick is to just cram the image into an attribute of something else that's going to stick around, like the Label object itself:
def open_img():
# your existing code
label2.tk_image = ImageTk.PhotoImage(pil_image_resized)
label2.config(image=label2.tk_image, width=w_box, height=h_box)
Now, the image can't be garbage collected until the label that's using it gets garbage collected (or until you replace it with a new image).
In fact, now that I look at the docs, this is exactly what the example of using a PhotoImage with a Label does:
photo = PhotoImage(file="icon.gif")
w = Label(parent, image=photo)
w.photo = photo
w.pack()
I am trying to display two image objects in my Text widget. Initially, both objects showed up as empty images. After some research, I learned that I need to keep a reference to the image. I added an extra line to my code. I now see the 2nd image displayed correctly, however the 1st image is still blank.
I suspect my "reference" is being over written during the for loop. Is this the case? And if so, how would I maintain multiple references if I need to display multiple image objects inside a Text widget?
Here is my code so far:
from Tkinter import *
from PIL import Image, ImageTk
import os
class TestApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent)
self.main_container.pack()
self.text_box = Text(self.main_container, height=50, width=80)
self.text_box.pack()
image_names = ['img1.jpg', 'img2.jpg']
for name in image_names:
img = Image.open(os.getcwd() + '\\' + name)
img_obj = ImageTk.PhotoImage(img)
self.text_box.image_create(INSERT, image=img_obj)
self.text_box.insert(END, "\n")
# Added this extra reference
self.text_box.image = img_obj
root = Tk()
root.title("This is a test app")
mainapp = TestApp(root)
root.mainloop()
Yes, your reference is being overwritten.
The simplest might be to just add the reference to a list. For example:
class TestApp:
def __init__(...):
...
self.images = []
...
for name in image_names:
...
img_obj = ImageTk.PhotoImage(img)
self.images.append(img_obj)