Batch convert images to sepia tone with python

Posted by Mike on Jan 13th, 2010

The Python Imaging Library (PIL) offers easy photo manipulation from python scripts. There’s some handy sample code on effbot.org that demonstrates how to alter an image’s palette to generate a sepia tone effect. It first desaturates the image, then applies a new palette based on a linear ramp.

I’ve cleanup up that sample code and tucked it into a script. You can pass a list of files to the script, and it will apply a sepia effect to each, making sure to backup the originals.

#!/usr/bin/python
"""
Apply sepia filter in batch to images
 
Usage:
    python batch_sepia.py [--no-backup] file1 [file2] ...
"""
 
import Image as PIL_Image
import shutil, os
from optparse import OptionParser
 
 
def open_image(filename):
    """ grab a PIL image from the given location
    """
    image = PIL_Image.open(filename)
    image.load()
    return image
 
def save_image(image, filename, quality=95):
    """ save the PIL image to disk
    """
    image.save(filename, "JPEG", quality=quality)
 
def make_linear_ramp(white):
    """ generate a palette in a format acceptable for `putpalette`, which
        expects [r,g,b,r,g,b,...]
    """
    ramp = []
    r, g, b = white
    for i in range(255):
        ramp.extend((r*i/255, g*i/255, b*i/255))
    return ramp
 
def apply_sepia_filter(image):
    """ Apply a sepia-tone filter to the given PIL Image
        Based on code at: http://effbot.org/zone/pil-sepia.htm
    """
    # make sepia ramp (tweak color as necessary)
    sepia = make_linear_ramp((255, 240, 192))
 
    # convert to grayscale
    orig_mode = image.mode
    if orig_mode != "L":
        image = image.convert("L")
 
    # optional: apply contrast enhancement here, e.g.
    #image = ImageOps.autocontrast(image)
 
    # apply sepia palette
    image.putpalette(sepia)
 
    # convert back to its original mode
    if orig_mode != "L":
        image = image.convert(orig_mode)
 
    return image
 
def convert_image(filename, make_backup):
    """ convert an image at the given path to sepia tone.
        @param filename
        @param make_backup - if True, will copy original file to file.bak
    """
    if not os.path.exists(filename):
        print 'Skipping %s' % filename
        return
    print 'Processing %s...' % filename
    if make_backup:
        shutil.copyfile(filename, '%s.bak' % filename)
    save_image(apply_sepia_filter(open_image(filename)), filename)
    print 'Done.'
 
def convert_images(files, make_backup=True):
    """ convert the list of filenames to sepia tone.
        @param filename
        @param make_backup - if True, will copy original file to file.bak
    """
    map(lambda f: convert_image(f, make_backup), files)
 
 
if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option("-x", "--no-backup", dest="no_backup", default=False,
            action="store_true")
    (options, files) = parser.parse_args()
    convert_images(files, make_backup=not options.no_backup)

You can execute this from the command line as:

$ python batch_sepia.py image1.jpg image2.jpg image3.jpg

If you want to recursively apply the filter to a bunch of images, you might consider mixing this with some find/xargs-fu:

$ find $HOME/pictures -name "*.jpg" | xargs python batch_sepia.py

Once you’ve got your photos in order, head over to photoworks.com to get them printed!</shameless-plug>

  • Twitter
  • Facebook
  • StumbleUpon
  • Google Reader
  • Reddit
  • Share/Bookmark

2 Responses

  1. DragonDon Says:

    This is an awesome script! makes my life easier! Although I am wondering why it wants to skip when I try only one image….

  2. DragonDon Says:

    nevermind…..typo in filename :P Thanks again!

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

Categories