views:

49

answers:

1

I've been using this little snippet to select random images. However I would like to change it to select only images of a certain size. I'm running into trouble checking against image size. If I use get_image_dimensions() I need to use a conditional statement, which then requires that I allow exceptions. So, I guess I need some pointers on just limiting by image dimensions. Thanks.

import os
import random
import posixpath
from django import template
from django.conf import settings

register = template.Library()

def is_image_file(filename):
    """Does `filename` appear to be an image file?"""
    img_types = [".jpg", ".jpeg", ".png", ".gif"]
    ext = os.path.splitext(filename)[1]
    return ext in img_types

@register.simple_tag
def random_image(path):
    """
    Select a random image file from the provided directory
    and return its href. `path` should be relative to MEDIA_ROOT.

    Usage:  <img src='{% random_image "images/whatever/" %}'>
    """
    fullpath = os.path.join(settings.MEDIA_ROOT, path)
    filenames = [f for f in os.listdir(fullpath) if is_image_file(f)]
    pick = random.choice(filenames)
    return posixpath.join(settings.MEDIA_URL, path, pick)
A: 

Well, a more direct way to get image dimensions is using the Python Imaging Library (which is what Django uses for get_image_dimensions in the backend anyway).

So, you use it like:

>> import Image
>> img = Image.open("foo.png")
>> img.size
(1729,828)

And your very simplest solution would be something like:

img_dimensions = lambda f: Image.open(f).size
filenames = filter(lambda f: is_image_file(f) and img_dimensions(f)==my_dimensions, os.listdir(fullpath))

where my_dimensions are -- your dimensions.

Problem is, that this (as well as any other method that checks file dimensions) actually has to open and read the images each time, which is not smart to do repeatedly. So depending on the load of your application, you will probably want to put img_dimensions() into a real, separate function and memoize it (http://code.activestate.com/recipes/52201-memoizing-cacheing-function-return-values/). Or if this is going to be a common task, just put images into folders pre-ordered by dimensions.

gilesc
So when I implement that change I get the syntax error 'lambda cannot contain assignment.'Also, how does that really differ from something like this:filenames = [f for f in os.listdir(fullpath) if is_image_file(f) and get_image_dimensions(f)=my_dimensions]
Jonathan
Doh! Should be:filenames = filter(lambda f: is_image_file(f) and img_dimensions(f)==my_dimensions, os.listdir(fullpath)).With a double equals sign for comparison instead of assignment.As to your second question, it's just stylistic -- I don't like long list comprehensions.
gilesc