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

Original source

Related problems