Lomo Deconstructed…

Something for python programmers and photographers alike…

In the comments of my last post Emmet Connolly pointed me in the direction of PIL – Python’s answer to PerlMagick. I’m just kicking the tyres on PIL at the moment but the signs are promising.
To test PIL’s abilities I decided to see if I could programmatically Lomoize photos using PIL.
I think the process of adding a Lomo effect to a photo is interesting enough that it warrants some deconstruction.
Let’s start with the original image – A nice autumnal photo taken just a few weeks ago in Fota Gardens, Cork…
Kate in Autumn
Adding a Lomo effect will create a darkened vignette-like shadow around the photo and saturate the colors a little, creating a warm ‘artsy’ feel…
Kate with Lomo
This new Lomo-fied version is constructed from 3 distinct layers…

  1. A Saturated lower layer
  2. A Darkened upper layer
  3. A “Mask” which will be used when superimposing the 2 layers

First I create a Saturated lower layer …
kate_saturated.jpg
… then a Darkened upper layer …
kate_darker.jpg
… Next comes the hard part. The mask which will be used when overlaying the saturated and darker images.
Masks are (as their name implies) special layers which let you specify what parts of the lower image show through when sticking another image on top of it. For a Lomo effect, I use a fuzzy circular mask so that the center of the lower image is visible. I start by using a basic 256 pixel square image…
mask256×256.jpg
… then stretch it to match the photo along it’s longest side…
mask_resized.jpg
… then crop it so that it’s size matches that of the photo …
mask_cropped.jpg

Compositing the saturated and darkened images using the above mask results in the following Lomo-effect photo…
Kate with Lomo

Now all of this is pretty easy if you’re a power photoshop or GIMP user. Most power users are familiar with the common photo-editing concepts of Layers, Channels and Alph-masks etc.
The tricky part (and I’m not trying to blow my own trumpet here) is commiting and codifying that knowledge in cold hard source code. A lot of Pixenate’s operations involve compositing of one kind or another, so compositing, and general mucking about with Alpha channels and masks, is a good litmus test for any Image Librarie’s abilities. So far, Python’s PIL is scoring pretty well – at least as good as Perl’s PerlMagick.
To paraphrase Jennifer Aniston – “here’s the source part” (it’s my first publicly-posted Python code so please be gentle :-) )…

from PIL import Image
from PIL import ImageEnhance
from PIL import ImageColor

def lomoize (image,darkness,saturation):
    """
        Add a 'Lomo' effect to an image. This is achieved by compositing 
        two versions of the image on top of each other using a vignette 
        mask so that the image appears bright and saturated in the middle,
        but darker in the corners.
        a. Saturate the lower layer
        b. Darken the upper layer
        c. overlay the two layers using a special "Vignette" mask.
        @param image The image to be lomoized
        @param darkness 0 - 1.0 How much darker the upper layer will be 
                    0 = black 1.0 = original
        @param saturation How much the more saturated the lower layer 
                    will be (1.0 = no change - +1 = more saturated)
        @return lomoized image
    """
    #
    # Get the Image size
    #
    (width,height) = image.size
    
    #
    # resize the mask appropriately
    #
    max = width
    if height > width :
        max = height
    
    mask = Image.open("d:/home/mask256x256.jpg").resize((max,max))
    #
    # crop the mask to match the image size
    #
    left = (max - width) / 2
    upper = (max - height) / 2
    
    mask = mask.crop((left,upper,left+width,upper + height))
    
    #
    # create the darkened upper layer for the corners
    #
    darker = ImageEnhance.Brightness(image).enhance(darkness)    

    #
    # create the saturated lower layer for the center
    #
    saturated = ImageEnhance.Color(image).enhance(saturation)

    #
    # Composite the darker upper layer on top of the saturated 
    # lower layer using the mask
    #
    lomoized = Image.composite(saturated,darker,mask)
    
    return lomoized

"""
   Main program begins here 
"""
kate = Image.open("d:/home/kate.jpg")

lomoized = lomoize(kate,0.4,2.5)

lomoized.save("d:/home/lomo.jpg", "JPEG", quality = 100)

So far I like PIL but I really need to investigate how difficult it will be to deploy a PIL-based solution.

Advertisements

4 thoughts on “Lomo Deconstructed…

  1. Emmet says:

    Nice work, Walter — you don’t waste any time, do you?!

    I’m the last person in the world to be picking apart someone else’s code, but since you asked, could the image resize section be changed to

    max = width
    min = height
    if height > width :
    max = height
    max = width

    mask = Image.open(“d:/home/mask256x256.jpg”).resize((max,min))

    and then leave out the cropping part? Seems to me that this would mean you would have a little bit of that lovely vignetting at the top and bottom of the image too.

  2. Emmet says:

    Oops, sorry about that, looks like the pre tags got stripped. Python doesn’t look quite the same without indentation, does it? :)

  3. sxoop says:

    Hi Emmet,
    I need to resize the vignette to the largest dimension – then crop so that I get a cut-off vignette – If I resize but don’t crop then the vignette is oval-shaped. Oval masks aren’t bad but I think a circular mask for Lomo is more authentic.

  4. […] Lomo Deconstructed… « Sxoop Journal from PIL import Image from PIL import ImageEnhance from PIL import ImageColor (tags: pil python image sample code) […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: