14 : Personalising Photographs

You want to share your photographs. You want to add captions to your photographs which can't be missed. You also want to send 'postcard' size images. You need to use the Python imaging library!

For manipulating individual images, products like GIMP will be most appropriate. However, if similar actions need to be applied to a group of images, programming them using Python imaging module should be considered.

In order to keep the code simple, the assumption is that you have just downloaded the pictures from your camera. So, all the pictures you wish to process are in the same directory. Create a sub-directory save/ in which you will keep your processed photos.

After that, you will see how you can write a Python generator and use the imaging library to create image transition effects when viewing photographs.

Transformation of Images

Write a class photos which will get a list of all the files in the current directory. You will get one photo at a time and resize it to a pre-defined size. You will modify this photo and add a caption to it. Finally, you will want to save the modified photograph in the save/ directory.

import os

import Image, ImageTk, ImageDraw

class photos:

def __init__(self,new_size):

self.file_generator = (fn for fn in os.listdir('.'))

self.new_size = new_size

self.image = None

Your initialisation method is simple enough. You create a generator so that you can conveniently fetch the next file when needed. You may add the flexibility of passing the directory as a parameter.

def get_next_photo(self):

while True:


self.file_name = self.file_generator.next()

image = Image.open(self.file_name)


except StopIteration, e:

return None

except Exception, e:

pass # do nothing if not an image

self.image = image.resize(self.new_size)

return self.image

You get the next file; but since not all files in the directory may be images, you need to ignore the other files by using exception handling and iterating till you find an image.

Image.open will create an image object from the file but will raise an exception if the file does not contain a valid image. The method resize on the image object will create a new image of the desired size. You can find out more about what you can do with the python imaging module at http://www.pythonware.com/library/pil/handbook/index.htm.

Finally, return the resized image; but if there are no more files, return a null value.

Now, examine the code to add caption to the image.

def photo_with_caption(self, caption):

self.im_caption = self.image.copy()

draw = ImageDraw.Draw(self.im_caption)

draw.text((50,self.new_size[1] - 50), caption)

return self.im_caption

Since you may wish to change the caption, you will want to work with the copy of the image. ImageDraw module of the imaging library allows you to draw on the image object. In this case, you are drawing some text on the image. The position chosen is 50 pixels from the left and bottom edges. The revised image is returned, as you will see, to the gui object which will display it.

def save(self):


self.im_caption.save('save/' + self.file_name)

except Exception, e:

self.image.save('save/' + self.file_name)

The save method will save the image with the caption with the same name as the existing file but in the save/ directory. However, if one has not been created, it will save the resized image.

Interactive Transformations

Now, you will need a gui class to use the above class. The gui should show you one image. Allow you to add a caption to it. Once you are satisfied, you save the photograph and move on to the next one. Or, you may decide to skip a photograph.

import Tkinter

class gui:

def __init__(self, photos):

# Save the photo application object context

self.photos = photos

self.root = Tkinter.Tk()

# The photo frame

self.foto = Tkinter.Canvas(self.root)


# The application interaction frame

self.frame = Tkinter.Frame(self.root)


# Text caption

self.caption = Tkinter.Entry(self.frame,width=72)


self.caption.insert(Tkinter.END, 'Press Enter to Apply Caption')

self.caption.bind('<Return>' , self.apply_caption)

# The Buttons

self.save = Tkinter.Button(self.frame, text='Save and Next', command=self.save_and_next)

self.skip = Tkinter.Button(self.frame, text='Next', command=self.next_image)



# Show the first image and start the event loop



Your gui consists of two parts, a canvas on which the photograph will be displayed and a frame for interacting with the application.

The frame has an text entry widget, and two buttons. Save and next button will save the current image and display the next one. The skip button will just display the next image.

The text entry widget is triggered by the return or the enter key to copy the text you have entered onto the image.

Incidentally, it is not appropriate to import all from Tkinter (i.e. 'from Tkinter import *') because Tkinter also has a class Image which will conflict with the import of the Image module.

The rest of the code in the gui class will be as follows.

def display_image(self, image):

self.foto['width'] = image.size[0]

self.foto['height'] = image.size[1]

self.tk_image = ImageTk.PhotoImage(image)


The method to display the image changes the size of the photo frame to the size of the image. The ImageTk module of Python imaging is used to convert the image object into an image object for Tkinter. The create image method of the canvas displays the image.

def next_image(self):

self.image = self.photos.get_next_photo()

if self.image == None:




The above method gets the next image from the photos object and calls the display image method. If there are no more images, the application quits.

def save_and_next(self):



The above method calls the save method of the photos object and then continues to display the next image.

def apply_caption(self, event):

text = self.caption.get()

self.image = self.photos.photo_with_caption(text)


The apply_caption method is called when the return or enter key is pressed after entering the caption text. The modified image is displayed.

The code to create and start the application:

my_photos = photos((800,600)) # convert images to 800x600

app = gui(my_photos)

Font Selection

If the text is too small, you can load and select your own font. The following lines of code will allow you to use your own font and size.

import ImageFont

self.font = ImageFont.truetype('/usr/share/fonts/lohit-hindi/lohit_hi.ttf',30)

draw.text((50,self.new_size[1] - 50), caption,font=self.font,fill='#00f')

Not, surprisingly, you can now enter the caption in Hindi in blue colour.

Jazz up the Transitions

You can use your little application as a simple viewer as well. Just keep skipping each photograph! That's just a little justification to explore some more capabilities of the imaging module.

You would like to have a fading effect whenever the next photograph is displayed. You need to write a method which will generate a sequence of images, starting with the current image and ending up with the new one. In the photos class, add the method:

def generate_photo_transition(self):


old_image = self.image

new_image = self.get_next_photo()

for transition in range(10):

self.transition_image = Image.blend(old_image, new_image, 0.1*(transition + 1))

yield self.transition_image

except Exception,e:

yield new_image

The generator for the sequence of images is simple. Just use a 'yield' statement to return an image. The blend function from the image module is used to create a new image which is a linear combination of the two images - (1 – r)*old + r*new. The factor r is the third parameter.

If there is no old or no new image, an exception will be raised. If no old image exists, the new image will be displayed with no transition effect. If no new image exists, a null value will be returned and the gui will terminate.

The gui program will need to iterate over the generator. The revised next_image method will be more complex

def next_image(self):

for self.image in self.photos.generate_photo_transition():

if self.image == None:






The key difference is that you are now iterating over the generator of the transition images. Each intermediary image is displayed and the application sleeps for a while. However, by default, it will not be updated until the control reverts to mainloop. The method update_idletasks forces the image to be displayed.

Obviously, Python imaging module can do a lot more than can be discussed in one article. You can use it to convert between formats, apply filters, enhance images, apply geometric transformations, manipulate pixels, crop and paste regions, manipulate frames in animated images. In short, you can use it convert your collection of photographs into a memorable set of pictures which you would love to see over and over. Also, you will not bore your friends with an endless stream of random pictures, where the good ones are lost in the clutter.

<Prev>  <Next>