Lightweight Django Thumbnails

I needed a good thumbnail solution for a recent Django project but didn’t want the overhead of using sorl – I just needed a simple template tag that would let me generate the correct size of thumbnail from the main picture.

I found a solution on djangosnippets.org but it needed a little tweeking to suit my needs.

The filter is applied directly to an image field using a template tag such as

{{ object.image|make_thumbnail:'100x200' }}

and supposing the image filename is “image.jpg”, it checks if there is a file called “image_100x200.jpg”, if the file isn’t there, it resizes and saves the original image, finally it returns the proper url to the resized image.

The updated filter is given below (you will need PIL installed):

import os
import Image
 
from django.template import Library, Node
from django.template import Variable, resolve_variable
from django.core.urlresolvers import reverse
from django.contrib.contenttypes.models import ContentType
 
register = Library()
 
def make_thumbnail(file, size='200x200'):
    """
    Example:
    <img src="object.get_image_url" alt="original image" />
    <img src="object.image|make_thumbnail" alt="image resized to default 200x200 format" />
    <img src="object.image|make_thumbnail:'200x300'" alt="image resized to 200x300" />
 
    The filter is applied to a image field (not the image url get from
    get_image_url method of the model), supposing the image filename is
    "image.jpg", it checks if there is a file called "image_200x200.jpg" or
    "image_200x300.jpg" on the second case, if the file isn't there, it resizes
    the original image, finally it returns the proper url to the resized image.
    """
 
    if file:
        # defining the size
        x, y = [int(x) for x in size.split('x')]
        # defining the filename and the miniature filename
        basename, format = file.path.rsplit('.', 1)
        baseurl, _format = file.url.rsplit('.', 1)
        miniature_filename = basename + '_' + size + '.' +  format
        miniature_url = baseurl + '_' + size + '.' +  format
 
        # if the image wasn't already resized, resize it
        if not os.path.exists(miniature_filename):
            image = Image.open(file.path)
            pw = image.size[0]
            ph = image.size[1]
            nw = x
            nh = y
            pr = float(pw) / float(ph)
            nr = float(nw) / float(nh)
 
            if image.mode not in ('L', 'RGB'):
                image = image.convert('RGB')
 
            if pw == nw and ph == nh:
                pass
            else:
                if pr > nr:
                    # image aspect is wider than destination ratio
                    tw = int(round(nh * pr))
                    image = image.resize((tw, nh), Image.ANTIALIAS)
                    l = int(round(( tw - nw ) / 2.0))
                    image = image.crop((l, 0, l + nw, nh))
                elif pr < nr:
                    # image aspect is taller than destination ratio
                    th = int(round(nw / pr))
                    image = image.resize((nw, th), Image.ANTIALIAS)
                    t = int(round(( th - nh ) / 2.0))
                    image = image.crop((0, t, nw, t + nh))
                else:
                    # image aspect matches the destination ratio
                    image = image.resize(size, Image.ANTIALIAS)
            image.save(miniature_filename, image.format)
        return miniature_url
 
    return ''
 
register.filter(make_thumbnail)

Save it as something like thumbs_filter.py in a templatetag folder in one of your django apps and then use it like this:

{% load thumbs_filter %}
{{ object.image|make_thumbnail:'100x200' }}

2 Responses to “Lightweight Django Thumbnails”

  1. Jon Says:
    March 26th, 2011 at 18:52

    Thanks, this is awesome.

  2. Jon Says:
    April 13th, 2011 at 10:47

    # image aspect matches the destination ratio
    image = image.resize(size, Image.ANTIALIAS)

    I think this should be

    image = image.resize((x,y), Image.ANTIALIAS)

Leave a Reply