Generating image thumbnails with django-thumbnail-works.

Uploading images to our Django application can be as easy as adding an ImageField to our model. However, ImageField falls short in one key aspect, thumbnails. Usually when uploading images, we want to create smaller versions for them, for using in for example, galleries or previews. This however, has to be done manually using the appropriate imaging libraries available.

Enter django-thumbnail-works:

The django-thumbnail-works package can solve this for us by providing the EnahncedImageField class. We can easily replace all of the ImageFields in our models with EnhancedImageFields (which actually subclasses Django’s ImageField), and pass the additional arguments that describe what has to be done to the image after uploading.

Here are the steps for getting django-thumbnails-works up and running in no time:

1. First things first, installation

Installing django-thumbnail-works should be as easy as using pip to install the package was well as its dependencies, cropresize and PIL, the python imaging library.

pip install django-thumbnail-works cropresize pil

If you’re running Ubuntu (and possibly other Linux distributions) you might notice the following disheartening message when compiling PIL:

--------------------------------------------------------------------
PIL 1.1.7 SETUP SUMMARY
--------------------------------------------------------------------
version       1.1.7
platform      linux2 2.7.2+ (default, Jul 20 2012, 22:12:53)
              [GCC 4.6.1]
--------------------------------------------------------------------
*** TKINTER support not available
*** JPEG support not available
*** ZLIB (PNG/ZIP) support not available
*** FREETYPE2 support not available
*** LITTLECMS support not available
--------------------------------------------------------------------

This is caused by the install script not being able to find the necessary development libraries and thus dropping support for the different file formats. So in order to add support to PIL for the different imaging libraries, we need to first install the missing development libraries.

sudo apt-get install libjpeg-dev libfreetype6-dev zlib1g-dev

There is unfortunately another step that we have to take for the setup scripts to be able to find these libraries. If you take a look at the installation path of the different packages, you will be able to see that they were installed into the /usr/lib/i386-linux-gnu/ and /usr/lib/x86-64 directories for 32bit and 64bit systems respectively. Pip’s setup scripts expect to find the libraries inside the /lib directory though. Fixing this is as easy as creating symlinks pointing to the correct location of the files. In order to do so, cd into your /lib directory:

cd /lib

or if you’re using virtualenv:

cd /$VIRTUAL_ENV/lib

Then link the libraries:

ln -s /usr/lib/`uname -i`-linux-gnu/libjpeg.so
ln -s /usr/lib/`uname -i`-linux-gnu/libz.so
ln -s /usr/lib/`uname -i`-linux-gnu/libfreetype.so_

please remember that you may need to run these commands as root or prepend sudo to them.

Finally, reinstall the PIL package

pip install -U pil
--------------------------------------------------------------------
PIL 1.1.7 SETUP SUMMARY
--------------------------------------------------------------------
version       1.1.7
platform      linux2 2.7.2+ (default, Jul 20 2012, 22:12:53)
              [GCC 4.6.1]
--------------------------------------------------------------------
*** TKINTER support not available
--- JPEG support available
--- ZLIB (PNG/ZIP) support available
--- FREETYPE2 support available
*** LITTLECMS support not available
--------------------------------------------------------------------

And now you have PIL with JPEG, ZLIB and FREETYPE2 support 😁

2. Updating your models

Adding thumbnail support should be fairly simple. The first thing that has to be done is to import from the thumbnail_works.fields_ module the EnhancedImageField class and replace with it all instances of the ImageField class. Remember that since EnhancedImageField is a subclass of ImageField, all arguments that you may already be passing to your ImageFields you can continue to do so.

Before continuing, we need to talk about image_processing_options. In order to create thumbnails, we need to give our EnhancedImageField instance a dictionary (called image_processing_options) containing the size we want our thumbnails to be and which (if any) filters to apply.

The image_processing_options dictionary can contain the following options:

size: Defines what size we want the thumbnail to be. The size option must be a string of the format widthxheight, for example 96x128.

format: A string that specifies what format we want the thumbnail to be. This is of course limited to what PIL can do and with support for what it was compiled with.

upscale: A boolean that if set will cause smaller images to be enlarged to the value specified in the size argument.

sharpen and detail: These boolean values enable applying the ImageFilter.SHARPEN and the ImageFilter.DETAIL filters respectively.

With that out of the way, it’s time to finish editing our model. To each of the models you edited earlier, there’s two arguments you can pass. The first one is the process_source argument which defines how you want the original image to processed before it is saved. If you want to leave the image as is, just skip this argument. If you want to do any processing, pass an image_processing_options dictionary. The second argument is the thumbnails dictionary which should contain strings for keys representing the name of each thumbnail being generated and an `image_processing_options dictionary for each of those names.

As an example, I created the following model. When an image is uploaded it is resized to 1024 x 768 pixels and saved as a PNG file. The sharpen and detail filters are also applied to it. There are also three different thumbnails being generated for it. tiny, which is just a 25 by 25 pixel image, thumb which is a 85 by 85 pixel image with the detail filter applied to it, and finally half which is half the size of the original image and has the detail and sharpen filters applied to it.

3. Using your thumbnails in your template

Now that you have your thumbnails ready, all that is left is showing them off!

It is possible to access the thumbnails as fields inside the EnhangedImageField field. Imagine we were passing our template an instance of our model called some_image.

Accessing the original image is as easy as adding {{ some_image.image }} to our template. Displaying the different thumbnails is as easy as displaying the image. Say I wanted to show the tiny thumbnail from my model, all I would have to do would be to add

{{ some_image.image.tiny }}

There you have it, with django-thumbnail-works no manual work is needed, just replace your ImageFields with EnhancedImageFields, define how you want your thumbnails to be, and voilà!