How can i display a resized image in python tkinter - python-2.7

I'm developing a GUI in Python using Tkinter to learn image processing. GUI's process flow would be as
Load image (jpg|png|...) => Resize/ thumbnail image (240 * 240) => Preview image
from Tkinter import *
import PIL
class Window:
def __init__(self, master):
master.title("Image Processing test")
master.minsize(800, 400)
from PIL import Image
im = Image.open("IMG_0562.png")
size = 240, 240
im.thumbnail(size)
p = im.tobytes()
# photo = PhotoImage(file="IMG_0562.gif")
# photo = BitmapImage(data=p)
w = Label(root, image=photo, width=240, height=240).grid(row=20, column=2)
self.photo = photo
root = Tk()
window = Window(root)
root.mainloop()
My problem is I couldn't get the image in a proper format to use it in Label. As Label only accepts PhotoImage and BitmapImage. PhotoImage doesn't support png or jpg file. So I used Image from PIL to load and resize my colored image. I've tried Image.tobitmap() and Image.tobytes() too but not useful in this case.

Solved the problem by saving the image in memory using io.BytesIO()
from Tkinter import *
from PIL import Image
import io
class Window:
def __init__(self, master):
master.title("Image Processing test")
master.minsize(800, 400)
im = Image.open("IMG_0562.png")
size = 240, 240
im.thumbnail(size)
b = io.BytesIO()
im.save(b, 'gif')
p = b.getvalue()
photo = BitmapImage(data=p)
w = Label(root, image=photo, width=240, height=240).grid(row=20, column=2)
self.photo = photo
root = Tk()
window = Window(root)
root.mainloop()

Related

How to save pillow processed image in already existing Django object

I have created a object model as below
from django.db import models
# Create your models here.
class ImageModel(models.Model):
image = models.ImageField(upload_to='images/')
editedImg = models.ImageField(upload_to='images/')
def delete(self, *args, **kwargs):
self.image.delete()
self.editedImg.delete()
super().delete(*args, **kwargs)
And here is what i am trying to do in a function
from django.shortcuts import render
from EditorApp.forms import ImageForm
from EditorApp.models import ImageModel
from django.http import HttpResponseRedirect
from PIL import Image
def edit_column(request):
codArr = request.POST.getlist('codArr[]')
imgs = ImageModel.objects.first()
orgImage = ImageModel.objects.first().image
orgImage = Image.open(orgImage)
croppedImg = orgImage.crop((int(codArr[0]), int(codArr[1]), int(codArr[2]), int(codArr[3])))
# croppedImg.show()
# imgs.editedImg = croppedImg
# imgs.save()
return HttpResponseRedirect("/editing/")
What i am trying to do is the codArr consists of coordinates of top(x, y) and bottom(x, y) in the array form(Which is not an issue and is tested(croppedImg.show() showed the desired cropped image) and handled and used to crop the image). Image crop is working fine. But what i am trying to do is to save the cropped image in editedImg of the model used above. The above commented one is what i tried but throw a error AttributeError: _committed
As i have not used any name for image in model as its not required.
Kindly help please, Would be very thankfull.
you should do it like this:
from io import BytesIO
from api.models import ProductPicture
from django.core import files
codArr = request.POST.getlist('codArr[]')
img_obj = ImageModel.objects.first()
orgImage = img_obj.image
orgImage = Image.open(orgImage)
croppedImg = orgImage.crop((int(codArr[0]), int(codArr[1]), int(codArr[2]), int(codArr[3])))
thumb_io = BytesIO() # create a BytesIO object
croppedImg.save(thumb_io, 'png')
editedImg = files.File(thumb_io, name=file_name)
img_obj.editedImg = editedImg
img_obj.save()
You can use Python's context manager to open the image and save it to the desired storage in that case I'm using the images dir.
Pillow will crop the image and image.save() will save it to the filesystem and after that, you can add it to Django's ImageField and save it into the DB.
The context manager takes care of the file opening and closing, Pillow
takes care of the image, and Django takes care of the DB.
from PIL import Image
with Image.open(orgImage) as image:
file_name = image.filename # Can be replaced by orgImage filename
cropped_path = f"images/croped-{file_name}"
# The crop method from the Image module takes four coordinates as input.
# The right can also be represented as (left+width)
# and lower can be represented as (upper+height).
(left, upper, right, lower) = (20, 20, 100, 100)
# Here the image "image" is cropped and assigned to new variable im_crop
im_crop = image.crop((left, upper, right, lower))
im_crop.save(cropped_path)
imgs.editedImg = cropped_path
imgs.save()
Pillow's reference

How to find the mouse hover positions (preferably timestamps) on a video?

I am trying to save the mouse hover positions in a given video. I need to load the video on a 'Load Video' button press. When the mouse is outside the canvas no (x,y) should be saved. I also want the video to streamed at much lower rate (say, 4 times slower). Presently, I have the following code:`
import Tkinter as tk
from Tkinter import *
import PIL.Image,PIL.ImageTk
import time
import cv2
class App:
def __init__(self, window, window_title, video_source=0):
self.window = window
self.window.title(window_title)
self.video_source = video_source
self.video_loaded=False
# open video source (by default this will try to open the computer webcam)
self.vid = MyVideoCapture(self.video_source)
# Create a canvas that can fit the above video source size
self.canvas = tk.Canvas(window, width = self.vid.width, height =
self.vid.height)
self.canvas.pack()
self.canvas.bind('<Motion>',self.canvas.motion)
#self.canvas.bind("<Enter>", self.on_enter)
#self.canvas.bind("<Leave>", self.on_leave)
# Button that lets the user take a snapshot
self.btn_snapshot=tk.Button(window, text="Snapshot", width=50,
command=self.snapshot)
self.btn_snapshot.pack(anchor=tk.CENTER, expand=True)
self.btn_collapse=tk.Button(window, text="Collapse", width=50,
command=self.collapse)
self.btn_collapse.pack(anchor=tk.CENTER, expand=True)
self.btn_load_video=tk.Button(window, text="Load Video", width=50,
command=self.load_video)
self.btn_load_video.pack(anchor=tk.CENTER, expand=True)
#if self.video_loaded==True:
# After it is called once, the update method will be automatically
called every delay milliseconds
self.delay = 15
self.update()
self.window.mainloop()
def load_video(self):
# open video source (by default this will try to open the computer
webcam)
self.vid = MyVideoCapture(self.video_source)
self.video_loaded=True
def snapshot(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg",
cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
def collapse(self):
self.window.quit()
def motion(self):
self.x=self.canvas.winfo_pointerx
self.y=self.canvas.winfo_pointery
print('{},{}'.format(self.x, self.y))
#self.canvas.itemconfigure(text='({x},{y})'.format(x = self.x,
y=self.y))
#print('{},{}'.format(self.x, self.y))
#def motion(self):
# x, y = self.x, self.y
# print('{}, {}'.format(x, y))
#def on_enter(self, event):
# self.l2.configure(text="Hello world")
#def on_leave(self, enter):
# self.l2.configure(text="")
def update(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
self.photo = PIL.ImageTk.PhotoImage(image =
PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)
self.window.after(self.delay, self.update)
class MyVideoCapture:
def __init__(self, video_source=0):
# Open the video source
video_source='./videofilename.wmv'
self.vid = cv2.VideoCapture(video_source)
if not self.vid.isOpened():
raise ValueError("Unable to open video source", video_source)
# Get video source width and height
self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
def get_frame(self):
if self.vid.isOpened():
ret, frame = self.vid.read()
if ret:
# Return a boolean success flag and the current frame converted
to BGR
return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
else:
return (ret, None)
else:
return (ret, None)
# Release the video source when the object is destroyed
def __del__(self):
if self.vid.isOpened():
self.vid.release()
# Create a window and pass it to the Application object
root = tk.Tk()
App(root, "Tkinter and OpenCV")
When I run this, I get the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/home/anaconda2/envs/my_env/lib/python2.7/lib-tk/Tkinter.py", line
1541, in __call__
return self.func(*args)
TypeError: motion() takes exactly 1 argument (2 given)
I want the function motion() to return the mouse hover positions. Appreciate help. Thanks in advance.
Here is the link from which the I got the main code.
The bind() function sends an event object to the callback function, but the motion() function only accepts self. Try:
def motion(self, event):
self.x=event.x
self.y=event.y
Binding the function for saving mouse positions can be done as in teh example below.
from tkinter import *
root = Tk()
root.geometry('300x200+800+50')
c = Canvas(root, bg='tan')
c.pack(fill='both', expand='yes')
def motion(event):
if follow:
print(event.x, event.y)
follow = False
def follow_motion(event):
global follow
follow = not follow
c.bind('<Motion>', motion)
c.bind('<Button-1>', follow_motion)
root.mainloop()
Click the left mouse button on the canvas and the function motion() is enabled. One more click disables it.

Radio Buttons won't select jpg

Shortened the code to minimum for the question. I want to toggle between images to apply on the canvas using radiobuttons. The code will only apply the images if I enter the image name, (ex. tkimg2) into the 'stamp' event. The radiobuttons are not selecting the images, nor does the link work if I enter the image name in picture=[]. Do you know why the image name is sufficient in one location and not the other and why the radiobuttons don't work? Thank you for any help
from Tkinter import *
import PIL
from PIL import ImageTk, Image
import random
import os.path
root = Tk()
shapes = []
#load 2 images for stamping
__dir__ = os.path.dirname(os.path.abspath(__file__))
filename = os.path.join(__dir__, 'balloon.jpg')
img = PIL.Image.open(filename)
tkimg=PIL.ImageTk.PhotoImage(img)
filename2 = os.path.join(__dir__, 'bird1.jpg')
img2 = PIL.Image.open(filename2)
tkimg2=PIL.ImageTk.PhotoImage(img2)
picture =[]
image=picture
# A Radiobutton to toggle between images
radio = [0]*2
v = IntVar()
def call():
if int(float(str(v.get())))==1:
picture=tkimg
else:
picture=tkimg2
Label(root, text ="Select an image to place.").grid(row=1, column=0,
columnspan=5, sticky=S)
R1=Radiobutton(root, text="Bird 1", variable=v, value=1, command=call)
R1.grid(row=2, column=0, sticky=N+E)
R1.select()
R2=Radiobutton(root, text="Bird 2", variable=v, value=2, command=call)
R2.grid(row=2, column=1, sticky=N+E)
# A canvas for mouse events and image drawing
canvas = Canvas(root, height=1000, width=1000, bg='#2EEAFF')
canvas.grid(column=5, row=0, rowspan=4, sticky=W)
# Bind a function to the left mouse button down event.
def stamp(event):
canvas.create_image(event.x,event.y,image)
canvas.bind('<ButtonPress-1>', stamp)
# Enter event loop
root.mainloop()
I removed most of the code not relevant to the question and changed the images to ordinary PhotoImages for simplicity. Also I changed the first positioning of the image on the canvas.
If you want to assign a value to a variable inside a function you'll have to make it global or it will not work. The variable will be defined in the local function scope and will be garbage collected when the function ends.
I don't think you can change an image on a canvas by updating the variable you used to create it. That's how a StringVar functions.
As the image is the only widget on the canvas I delete ALL items and then create a new image when I toggle images.
Also: I use Python 3.6 so I spell tkinter without the capital T.
from tkinter import *
root = Tk()
#load 2 images for stamping
tkimg = PhotoImage(file='test.gif') # Test image
tkimg2 = PhotoImage(file='tesu.gif') # Test image
# A Radiobutton to toggle between images
v = IntVar()
def call():
canvas.delete(ALL)
if v.get() == 1:
canvas.create_image((2, 2), image=tkimg, anchor=NW)
else:
canvas.create_image((2, 2), image=tkimg2, anchor=NW)
Label(root, text ="Select an image to place.").grid(row=1, column=0, columnspan=5, sticky=S)
R1=Radiobutton(root, text="Bird 1", variable=v, value=1, command=call)
R1.grid(row=2, column=0, sticky=N+E)
R1.select()
R2=Radiobutton(root, text="Bird 2", variable=v, value=2, command=call)
R2.grid(row=2, column=1, sticky=N+E)
# A canvas for mouse events and image drawing
canvas = Canvas(root, height=200, width=200, bg='#2EEAFF')
canvas.grid(column=5, row=0, rowspan=4, sticky=W)
canvas.create_image((2, 2), image=tkimg, anchor=NW)
# Enter event loop
root.mainloop()

Thumbnailctrl size (trying to get it to fullscreen)

I've been trying to insert two Thubmnailctrl under a Multisplitter, I have managed to put them in there, but I can't manage to make them ocuppy the full space. On thumbnailctrl.py I've seen that on the the maximum size it can be is 350x280:
def SetThumbSize(self, width, height, border=6):
"""
Sets the thumbnail size as width, height and border.
:param `width`: the desired thumbnail width;
:param `height`: the desired thumbnail height;
:param `border`: the spacing between thumbnails.
"""
if width > 350 or height > 280:
return
self._tWidth = width
self._tHeight = height
self._tBorder = border
self.SetScrollRate((self._tWidth + self._tBorder)/4,
(self._tHeight + self._tBorder)/4)
self.SetSizeHints(self._tWidth + self._tBorder*2 + 16,
self._tHeight + self._tBorder*2 + 8)
But on the other hand on the demo under ThumbnailCtrl, it uses an Splitter to create a Thumbnailctrl as big as you want, so I don't know if I'm doing something wrong (maybe with Sizers) or is some feature from Splitter (totally diferent than multisplitter) that allows the Thumbnailctrl to occupy it's full space.
Thumbnailctrl + Splitter Demo:
import wx
import os
import sys
try:
dirName = os.path.dirname(os.path.abspath(__file__))
except:
dirName = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.split(dirName)[0])
try:
from agw import thumbnailctrl as TC
except ImportError: # if it's not there locally, try the wxPython lib.
import wx.lib.agw.thumbnailctrl as TC
class MainFrame(wx.Frame):
def __init__(self, redirect=False, filename=None):
wx.Frame.__init__(self, None, title="Elephant")
# self.SetMenuBar(self.CreateMenuBar())
splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE)
self.panel = wx.Panel(splitter, -1)
sizer = wx.BoxSizer(wx.HORIZONTAL)
scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.NativeImageHandler)
scroll.ShowFileNames()
if os.path.isdir("../bitmaps"):
scroll.ShowDir(os.path.normpath(os.getcwd() + "/../bitmaps"))
else:
scroll.ShowDir(os.getcwd())
self.TC = scroll
splitter.SplitVertically(scroll, self.panel, 180)
splitter.SetMinimumPaneSize(140)
self.SetMinSize((700, 590))
self.CenterOnScreen()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
# import wx.lib.inspection
# wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
My attempt at a Multisplitter with two thumbnails (and when that works a third panel with text and stuff):
import wx
import os
import cv2
import ctypes
from PIL import Image
from wx.lib.splitter import MultiSplitterWindow
try:
from agw import thumbnailctrl as TC
except ImportError: # if it's not there locally, try the wxPython lib.
import wx.lib.agw.thumbnailctrl as TC
class SamplePane(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.thumbnail11 = TC.ThumbnailCtrl(self, imagehandler=TC.NativeImageHandler, thumboutline=4)
self.thumbnail11.EnableDragging(True)
# self.thumbnail11.SetThumbSize(350, screensize[0] / 15, 25) # For images -> Max values 350,280
# ################VID################ #
topmostSizer = wx.BoxSizer(wx.VERTICAL)
topmostSizer.Add(self.thumbnail11, proportion=0, flag=wx.EXPAND)
self.SetSizer(topmostSizer)
self.MaxSize
# topmostSizer.Fit(self)
class MainFrame(wx.Frame):
""""""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Elephant")
splitter = MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE)
# t1Sizer = wx.BoxSizer(wx.VERTICAL)
# self.thumbnail11 = TC.ThumbnailCtrl(splitter, imagehandler=TC.NativeImageHandler, thumboutline=4)
panel = SamplePane(splitter)
splitter.AppendWindow(panel)
panel2 = SamplePane(splitter)
splitter.AppendWindow(panel2)
# t1Sizer.Add(panel, proportion=0, flag=wx.EXPAND)
self.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
# import wx.lib.inspection
# wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
As you can see there are two thumbnails and they expand left to right, but they are capped at a maximum height.
Thanks a lot for the help!
Not 100% sure what it is that you're trying achieve with this but I suspect that your problem is with the topmostSizer's proportion attribute.
Try:
topmostSizer.Add(self.thumbnail11, proportion=1, flag=wx.EXPAND)
From the manual:
proportion - Although the meaning of this parameter is undefined in
wx.Sizer, it is used in wx.BoxSizer to indicate if a child of a sizer
can change its size in the main orientation of the wx.BoxSizer - where
0 stands for not changeable and a value of more than zero is
interpreted relative (a proportion of the total) to the value of other
children of the same wx.BoxSizer.
In this case, you have defined topmostSizer as VERTICAL

Python 2.7, Tkinter and PIL, grab the screen and show the Image I grabbed when I click a button

I've been trying all day to get my program to grab the screen when I click a button then display the image in a label on the screen. For some ridiculous reason or whatever the program works fine before I put the code into a function and run it with the button.
this does not work
import Tkinter, ImageTk, ImageGrab
def takePic():
img = ImageGrab.grab()
dispImg = ImageTk.PhotoImage(img)
label1 = Tkinter.Label(imgFrame, image = dispImg)
label1.pack()
root = Tkinter.Tk()
btn1 = Tkinter.Button(root, text = "Click", command = takePic)
btn1.pack(side = "top")
imgFrame = Tkinter.Frame(root)
imgFrame.pack()
root.mainloop()
this work:
import Tkinter, ImageTk, ImageGrab
root = Tkinter.Tk()
imgFrame = Tkinter.Frame(root)
imgFrame.pack()
img = ImageGrab.grab()
dispImg = ImageTk.PhotoImage(img)
label1 = Tkinter.Label(imgFrame, image = dispImg)
label1.pack()
root.mainloop()
Any1 know why or how to do it damn