Generating thumbnails from a collection of pictures is an easy task and there are many tools for the job. But I didn’t find a tool who can clip out a centered box of my pictures to get quadratic thumbs of my originals. Because I am a developer and I dont want to try out many tools: I will hack a short python script (with help of the PIL) myself.
The following python script can be find on many webpages. It will take all command line arguments as image filenames and convert it to 200×200 thumbnails with the default extension “_thumb.jpg” (build from the original filename):
import Image, os, sys for filename in sys.argv[1:]: img = Image.open(filename).resize( (200,200) ) out = file(os.path.splitext(filename)+"_thumb.jpg", "w") try: img.save(out, "JPEG") finally: out.close()
With the command line tool find all images and converted:
find . -regex .*jpg | xargs python resize.py
But the result is not ok:
- the only good part: the result image is 200×200 pixel
- aspect ratio ignored
- ugly quality
- performance bad: 1.025 sec (example image on my eeeBox)
- no value to choose the jpg quality for saving
For my web gallery I need identical thumbnails for a clean design and the aspect ratio should not be ignored!
Challenge: Find the biggest box with correct aspect ratio and clip the image, resize it fast and with the best quality to the given thumbnail size!
resize tips you should know
When choosing an image libary (with python is the build-in variant PIL good enough, but Samuel will add an article for java image libraries) for resizing images try to find out the following three parts:
choose the right algorythm
There should be a option to choose the image transforming algorithm. “Fast and ugly” or “slow and high quality”.
With the python imaging library there are included algorithm for image translations (ordered from fastest to best quality): NEAREST, BILINEAR, BICUBIC, ANTIALIAS. If you resize an image with NEAREST the quality will be poor but the difference in calculation time to ANTIALIAS is factor 3 (depending on images). The first example has the NEAREST as default value.
img = Image.open(filename).resize( (200,200), Image.ANTIALIAS)
Resizing the image with the best algorithm will produce a good quality, but needs more than 3 sec.
use two resize steps
The trick for resizing an 1600×1600 image to 200×200:
- first resize it from 1600×1600 to double the target size (400×400) with the fastest allgorithm
- resize the smaller image to the target resolution with the best algorithm
You get the fast transformation for the big amount and the last step to the target resolution with the best quality.
img = Image.open(filename).resize( (400,400) ) img = img.resize( (200,200), Image.ANTIALIAS)
This 2-pass resize part runs in exactly the same time (1.02 sec) like the first example with better quality.
resize vs. thumbnail
Because our picture collection contain 90% JPEG there is an other optimization step. To resize JPEGs you dont need all pixels for downsizing with factor 8. Its adequate to read only the starting point of an minimum coded Unit (MCU -> wikipedia). You have to try out if it is supported of your image lib – the transformation time is the answer.
The python image library offer this option with the Image.thumbnail-method. An other aspect is that the thumbnail parameter mean a target box size instead the target image size – so the aspect ratio will be saved.
img = Image.open(filename) img.thumbnail( (200,200) )
The thumbnail method works on the image object and dont produce a new image object. Its faster: 0.25 sec.
img = Image.open(filename) img.thumbnail( (400,400) ) img.thumbnail( (200,200), Image.ANTIALIAS)
The two-pass variant is slower but has better quality on the edges. Its runs 0.42 sec.
You should be careful by loading every image. I can produce a pixel bomb by saving an 40000×40000 pixel black picture as a png. This image file is not expensive but your memory will not hold the complete bitmap. Check the resolution or have an idea to handle the OutOfMemory-exception – before loading!
There are many corrupt example images. Try these images to check if your image library is robust.
benchmark for the different steps
- 1.02 sec: direct resize with bad quality
- over 3 sec: direct resize with best quality
- 1.02 sec: two resize steps with best quality
- 0.25 sec: direct thumbnail call with bad quality
- 0.42 sec: two thumbnail calls with best quality
This is factor 8 for different, best quality resize operation and worth for code-checking. If you plan to resize images on-the-fly for webpages there are other technics than these basic tips …
Finding the center box
I will construct two extreme examples for defining the clipping algorithm. If the two examples works the quadratic thumbnail work too.
Get from a portait picture a landscape thumb
- Original: bmw.jpg (2048×3072 pixel)
- thumbnail-box: 200×100 pixel
The clipping algorithm should find the centered box:
This picture is ideal for a wide thumbnail because the dominant radiator grill of the old bmw is THE part of the picture!
Get from a landscape picture a portait thumb
- Second Example: ford.jpg (3072×2048 pixel)
- thumbnail-box: 100×200 pixel
The clipping algorithm should find the centered box:
This old ford T-Model image has to many people on the image – a portrail thumbnail crop some from the front.
the complete function
def resize(img, box, fit, out): '''Downsample the image. @param img: Image - an Image-object @param box: tuple(x, y) - the bounding box of the result image @param fix: boolean - crop the image to fill the box @param out: file-like-object - save the image into the output stream ''' #preresize image with factor 2, 4, 8 and fast algorithm factor = 1 while img.size/factor > 2*box and img.size*2/factor > 2*box: factor *=2 if factor > 1: img.thumbnail((img.size/factor, img.size/factor), Image.NEAREST) #calculate the cropping box and get the cropped part if fit: x1 = y1 = 0 x2, y2 = img.size wRatio = 1.0 * x2/box hRatio = 1.0 * y2/box if hRatio > wRatio: y1 = int(y2/2-box*wRatio/2) y2 = int(y2/2+box*wRatio/2) else: x1 = int(x2/2-box*hRatio/2) x2 = int(x2/2+box*hRatio/2) img = img.crop((x1,y1,x2,y2)) #Resize the image with best quality algorithm ANTI-ALIAS img.thumbnail(box, Image.ANTIALIAS) #save it into a file-like object img.save(out, "JPEG", quality=75) #resize
Additional tip: Cropping from the smaller image will save memory and memcopy-operation.
- Line 13: fast pre-resize
- Line 27: cropping the pre-sized image with the calculcated cropping values
- Line 30: resize the image to the target box (aspecti ratio with using of method thumbnail is considerd).
- Line 33: Saving a thumbnail with 75% is acceptable for the quality
- the complete script with doctest and command line option is includet here in the article for free download.
- the bmw and ford t model pictures are taken while the Leo Classic 2009 (Leonberg – Germany) and are for free use (download here).
How to do this in java
The questions for efficent and high quality resizing with java libs is open for the next article … be free to give improvements in the comments or trackbacks!