Back to blog

Clever Image Resizing In Python

In the not-too-distant past, I had a bit of trouble with a client who wanted to have images resized on the fly that were of a completely different aspect ratio - i.e. they wanted images that were orignally landscape to be square, with no skewing of the image. Now some people will note that that’s impossible, because while I’m quite handy with PHP, my class for distoring the laws of Physics still gets odd errors. I ended up turning to a class sourced from White Hat Web Design, which was quite clever and took the largest possible area from the centre of the image that matched the target size ratio, and then just resized that. This meant there was a little cropping, but no skewing, and the client was happy.

More recently, I’ve decided that it was absolutely necessary to re-write this particular feature in Python, which will allow me to run it from the command-line and cleanly resize batches of images. I can’t imagine I’ll have much use for this particular script, but someone who takes a lot of pictures may.

And here’s an example of how it works:

Original file, 2048x1365 (courtesy of Charlie Newey):

Full-size photo of a castle

Photograph of the castle, resized to a square and cropped

The same photograph of a castle but cropped to a banner-shape

Here’s the script, available for download here:

#!/usr/bin/python

import PIL
from PIL import Image
import sys
import os

"""
Resizes images to a given size, retaining the target aspect ratio, with no stretching. WARNING: Will crop images

Resizing on the SimpleImage PHP class by Simon Jarvis
http://www.white-hat-web-design.co.uk/articles/php-image-resizing.php
"""

#Divides two numbers
def divide(x,y):
        return float(x)/float(y)

#Gets the filenames from the command-line arguments
def get_files_from_args():
        files = []
        if(len(sys.argv) > 3):
                for args in sys.argv[1:len(sys.argv) -2]:
                        files.append(args)
        return files

#Gets the target size from the command-line arguments
def get_size_from_args():
        sys_len = len(sys.argv)
        return (int(sys.argv[sys_len - 1]), int(sys.argv[sys_len - 2]))

def resize(image, size):
        return image.resize(size,Image.ANTIALIAS)

#Regular resizing - not very good
def resize_images(files,size):
        for file in files:
                try :
                        image = Image.open(file)
                        filename, ext = os.path.splitext(file)
                        image.thumbnail(size, PIL.Image.ANTIALIAS)
                        image.save(filename + "-resized" + ext)
                except IOError:
                        print "File " + file + " not found"

#Crops an image to a height
def crop_to_height(image,theight,twidth):
        width,height = image.size
        diff = height - theight
        y = diff/2

        left = 0
        right = width
        bottom = height - y
        top = y
       return image.crop((left,top,right,bottom))

#Crops an image to a width
def crop_to_width(image,theight,twidth):
        width,height = image.size
        diff = width - twidth
        x = diff/2

        left = x
        right = width - x
        bottom = height
        top = 0
        return image.crop((left,top,right,bottom))

#Resizes an image to a width
def resize_to_width(image,twidth):
        width,height = image.size
        ratio = divide(twidth,width)
        height = height * ratio
        return resize(image, (int(twidth), int(height)))

#Resies an image to a height
def resize_to_height(image,theight):
        width,height = image.size
        ratio = divide(theight,height)
        width = width * ratio
        return resize(image,(int(width),int(theight)))

#Clean resizing, with no stretching
def resize_super_intelligent(files,size):
        for file in files:
                try:
                        image = Image.open(file)
                        filename, ext = os.path.splitext(file)
                        width, height = image.size
                        twidth, theight = size
                        w_ratio = divide(twidth, width)
                        h_ratio = divide(theight,height)
                        if((height * w_ratio) >= theight):
                                image = resize_to_width(image,twidth)
                                image = crop_to_height(image,theight,twidth)
                        elif((width * h_ratio) >= twidth):
                                image = resize_to_height(image,theight)
                                image = crop_to_width(image, theight, twidth)

                       image.resize(image,size)
                       image.save(filename + "-resized" + ext)
                except IOError:
                       print "File " + file + " not found"

resize_super_intelligent(get_files_from_args(), get_size_from_args())

Then to run you can just use:

python resize.py file1.ext file2.ext... height width