Having some trouble converting a tk PhotoImage back to a PIL image to save
python-3.x, python-imaging-library, tkinter
Solution
Remove `pic = Image.open(tkPic2)` in `saveImage()`
To save file use:
def saveImage():
global pic
toSave = filedialog.asksaveasfile(mode='w',defaultextension='.jpg')
pic.save(toSave)
BTW: for object better use `None` in place of `''`
pic = None
tkPic = None
tkPic2 = None
picToConvert = None
canvas1 = None
Besides, `del` gives you `None`
del tkPic2 # now tkPic2 == None
EDIT: solution for problem with IntVar and rotation.
I create global variable `var = None` and then I can use it in functions (using `global var`)
I use `pic = pic.rotate(180)` to rotate "in place" without new variable.
I add radio button for `Rotation` and function `onConvert` to run `rotateIt` or `change_pixel` and now I create `tkPic2` in `onConvert`
from PIL import Image, ImageTk
#from graphics import GraphWin # not needed
# for Python 3.x
from tkinter import filedialog # Will be used to open the file from the user
import tkinter
# for Python 2.x
#import Tkinter as tkinter
#import tkFileDialog as filedialog # Will be used to open the file from the user
import os
# --- Global variables for radio buttons ---
radio1 = True
radio2 = False
radio3 = False
radio4 = False
#--------------------------------------------
# --- Global variables for picture ---
pic = None
tkPic = None
tkPic2 = None
picToConvert = None
picWidth = 0
picHeight = 0
canvas1 = None
var = None #create global variable
#----------------------------------------------------------------------
def rotateIt():
global pic
print "(debug) rotateIt:", pic
pic = pic.rotate(180)
# --- Function for radio buttons ---
def whichSelected(numberSelected):
global radio1
global radio2
global radio3
global radio4
if numberSelected == 4:
radio1 = False
radio4 = True
elif numberSelected == 3:
radio1 = False
radio3 = True
elif numberSelected == 2:
radio1 = False
radio2 = True
elif numberSelected == 1:
radio1 = True
# --- Gray Algorithms ---
def grayAverage(r, g, b):
return (r + g + b) // 3
def invertRGB(r,g,b):
return (255 - r, 255 - g, 255 - b)
def lightness(r,g,b):
return (max(r, g, b) + min(r, g, b)) // 2
def luminosity(r,g,b):
return int(((0.21 * r) + (0.71 * g) + (0.07 * b)))
def getRGB(r,g,b):
red = eval(input("Enter the value of red: "))
green = eval(input("Enter the value of green: "))
blue = eval(input("Enter the value of blue: "))
return red-r + green-g + blue-b // 3
# --- End Gray Algorithms ---
# Draws window, opens picture selected by user, packs the canvas
def drawWindow():
window = tkinter.Tk()
window.title(os.environ.get( "USERNAME" )) # sets the window title to the
return window
def drawCanvas():
global window
global canvas1
canvas1 = tkinter.Canvas(window, width = 820, height =340) # Draws a canvas onto the tkinter window
canvas1.pack()
return canvas1
# Global variables for window and canvas
window = drawWindow()
canvas1 = drawCanvas()
#----------------------------------------------------------------------
# --- Radio Button Code ---
def drawRadioButtons():
global window, var
var = tkinter.IntVar()
option1 = tkinter.Radiobutton(window, text ='Average Grayscale', variable = var, value = 1, command = lambda: whichSelected(1))
option2 = tkinter.Radiobutton(window, text ='Lightness Grayscale', variable = var, value = 2, command = lambda: whichSelected(2))
option3 = tkinter.Radiobutton(window, text ='Luminosity Grayscale', variable = var, value = 3, command = lambda: whichSelected(3))
option4 = tkinter.Radiobutton(window, text ='Invert', variable = var, value = 4, command = lambda: whichSelected(4))
option5 = tkinter.Radiobutton(window, text ='Rotate 180', variable = var, value = 5)
# Sets the first button to clicked
option1.select()
# Pack Radio Buttons
option1.pack(anchor = 'sw')
option2.pack(anchor = 'sw')
option3.pack(anchor = 'sw')
option4.pack(anchor = 'sw')
option5.pack(anchor = 'sw')
# End Radio Button code
def openImage():
global window
global canvas1
global pic
global picWidth
global picHeight
global tkPic
global tkPic2
global picToConvert
canvas1.delete('all')
del pic
del tkPic
picToConvert = filedialog.askopenfilename(defaultextension='.jpg') # Used to open the file selected by the user
pic = Image.open(picToConvert).convert('RGB')
picWidth, picHeight = pic.size # PIL method .size gives both the width and height of a picture
tkPic = ImageTk.PhotoImage(pic, master = window) # Converts the pic image to a tk PhotoImage
canvas1.create_image(10,10,anchor='nw', image = tkPic)
#----------------------------------------------------------------------
def saveImage():
global pic
toSave = filedialog.asksaveasfile(mode='w',defaultextension='.jpg')
pic.save(toSave)
#----------------------------------------------------------------------
def onConvert():
global var
global tkPic2
global window
if var.get() == 5:
rotateIt(pic)
else:
change_pixel()
# Converting to a tkinter PhotoImage
if tkPic2:
del tkPic2
tkPic2 = ImageTk.PhotoImage(pic, master = window)
canvas1.create_image(815,170, anchor='e',image = tkPic2)
#----------------------------------------------------------------------
def change_pixel():
global window
global canvas1
global pic
global radio1
global radio2
global radio3
global radio4
# Treats the image as a 2d array, iterates through changing the
#values of each pixel with the algorithm for gray
rgbList = pic.load() #Get a 2d array of the pixels
for row in range(picWidth):
for column in range(picHeight):
rgb = rgbList[row,column]
#print rgb
r,g,b = rgb # Unpacks the RGB value tuple per pixel
if radio1 == True:
grayAlgorithm1 = grayAverage(r,g,b)
rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1)
elif radio2 == True:
grayAlgorithm1 = lightness(r,g,b)
rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1)
elif radio3 == True:
grayAlgorithm1= luminosity(r,g,b)
rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1) # Gives each pixel a new RGB value
elif radio4 == True:
rgbList[row,column] = invertRGB(r,g,b) # Gives each pixel a new RGB value
#----------------------------------------------------------------------
# Function to create a button, takes the button text and the function to be called on click
def tkButtonCreate(text, command):
tkinter.Button(window, text = text, command = command).pack()
#----------------------------------------------------------------------
def main():
drawRadioButtons()
tkButtonCreate('Open Image',openImage)
tkButtonCreate('Convert', onConvert)
tkButtonCreate('Save',saveImage)
window.mainloop()
#convertButton = tkinter.Button(window,text = 'Convert', command = change_pixel).pack()
#----------------------------------------------------------------------
main()
Problem
I'm working on a program that will convert an image to grayscale, or invert the colors. It's using different algorithms for grayscale and most everything is working fine but I have a couple of problems that I've been trying to overcome. One issue I have is trying to save the image out. So that I could handle different file types I used PIL to open the image, then convert it to a tk `PhotoImage`. Now that the image is a `PhotoImage` I can't figure out a way to save, and Tkinter's documentation mentions a way but this fails. Another problem I have is that I want to use a checkbox to rotate the image, I have a function `rotateIt` that does this, but I wasn't able to get the checkbox working because of the `IntVar()` doesn't seem to change, note my use of lambda to make it work with the radio buttons. (Thanks to another member here) Here is the code for the program. (I'm still learning, having trouble figuring out how not to use the global variables to accomplish the same tasks.) ``` from PIL import Image, ImageTk from graphics import GraphWin from tkinter import filedialog # Will be used to open the file from the user import tkinter import os # Global variables for radio buttons---- radio1 = True radio2 = False radio3 = False radio4 = False #-------------------------------------------- # Global variables for picture----------- pic = '' tkPic = '' tkPic2 = '' picToConvert = '' picWidth = 0 picHeight = 0 canvas1 = '' #--------------------------------------------- def rotateIt(pic1): pictureRotated = pic1.rotate(180) return pictureRotated # Function for radio buttons def whichSelected(numberSelected): global radio1 global radio2 global radio3 global radio4 if numberSelected == 4: radio1 = False radio4 = True if numberSelected == 3: radio1 = False radio3 = True if numberSelected == 2: radio1 = False radio2 = True if numberSelected == 1: radio1 = True # Gray Algorithms--------------------------------------------- def grayAverage(r,g,b): algorithm = (r + g + b) // 3 return (algorithm) def invertRGB(r,g,b): r = 255 - r g = 255 - g b = 255 - b return (r,g,b) def lightness(r,g,b): algorithm = (max(r, g, b) + min(r, g, b)) // 2 return (algorithm) def luminosity(r,g,b): algorithm = int(((0.21 * r) + (0.71 * g) + (0.07 * b))) return (algorithm) def getRGB(r,g,b): red = eval( input ("Enter the value of red: ")) green = eval(input ("Enter the value of green: ")) blue = eval(input ("Enter the value of blue: ")) algorithm = red-r + green-g + blue-b // 3 return (algorithm) # End Gray Algorithms----------------------------------------------------------------------------- # Draws window, opens picture selected by user, packs the canvas def drawWindow(): window = tkinter.Tk() window.title(os.environ.get( "USERNAME" )) # sets the window title to the return window def drawCanvas(): global window global canvas1 canvas1 = tkinter.Canvas(window, width = 820, height =340) # Draws a canvas onto the tkinter window canvas1.pack() return canvas1 # Global variables for window and canvas window = drawWindow() canvas1 = drawCanvas() # ----------------------------------------------------------------------------------- # Radio Button Code--------------------------------------------------------- def drawRadioButtons(): global window var = tkinter.IntVar() option1 = tkinter.Radiobutton(window, text ='Average Grayscale ',variable = var, value = 1,command = lambda: whichSelected(1)) option2 = tkinter.Radiobutton(window, text ='Lightness Grayscale ',variable = var, value = 2, command = lambda: whichSelected(2)) option3 = tkinter.Radiobutton(window, text ='Luminosity Grayscale ',variable = var, value = 3, command = lambda: whichSelected(3)) option4 = tkinter.Radiobutton(window, text ='Invert',variable = var, value = 4, command = lambda: whichSelected(4)) option1.select() # Sets the first button to clicked # Pack Radio Buttons option1.pack(anchor = 'sw') option2.pack(anchor = 'sw') option3.pack(anchor = 'sw') option4.pack(anchor = 'sw') # End Radio Button code --------------------------------------------------------- def openImage(): global window global canvas1 global pic global picWidth global picHeight global tkPic global tkPic2 global picToConvert canvas1.delete('all') del pic del tkPic picToConvert = filedialog.askopenfilename(defaultextension='.jpg') # Used to open the file selected by the user pic = Image.open(picToConvert) picWidth, picHeight = pic.size # PIL method .size gives both the width and height of a picture tkPic = ImageTk.PhotoImage(pic, master = window) # Converts the pic image to a tk PhotoImage canvas1.create_image(10,10,anchor='nw', image = tkPic) def saveImage(): global pic global tkPic2 pic = Image.open(tkPic2) toSave = filedialog.asksaveasfile(mode='w',defaultextension='.jpg') pic.save(toSave) def change_pixel(): global window global canvas1 global tkPic2 global pic global radio1 global radio2 global radio3 global radio4 # Treats the image as a 2d array, iterates through changing the #values of each pixel with the algorithm for gray rgbList = pic.load() #Get a 2d array of the pixels for row in range(picWidth): for column in range(picHeight): rgb = rgbList[row,column] r,g,b = rgb # Unpacks the RGB value tuple per pixel if radio1 == True: grayAlgorithm1 = grayAverage(r,g,b) rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1) elif radio2 == True: grayAlgorithm1 = lightness(r,g,b) rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1) elif radio3 == True: grayAlgorithm1= luminosity(r,g,b) rgbList[row,column] = (grayAlgorithm1, grayAlgorithm1, grayAlgorithm1) # Gives each pixel a new RGB value elif radio4 == True: r,g,b= invertRGB(r,g,b) rgbList[row,column] = (r, g, b) # Gives each pixel a new RGB value # Converting to a tkinter PhotoImage del tkPic2 tkPic2 = ImageTk.PhotoImage(pic, master = window) canvas1.create_image(815,170, anchor='e',image = tkPic2) # Function to create a button, takes the button text and the function to be called on click def tkButtonCreate(text, command): tkinter.Button(window, text = text, command = command).pack() def main(): drawRadioButtons() tkButtonCreate('Open Image',openImage) tkButtonCreate('Convert', change_pixel) tkButtonCreate('Save',saveImage) window.mainloop() #convertButton = tkinter.Button(window,text = 'Convert', command = change_pixel).pack() main() ```