views:

1212

answers:

3

Notice how for every image that google indexes it has a small thumbnail. These thumbnails are:

  1. Less than 10KB in size.
  2. The proportions of the width / height are the same as the ones in the original image.

I would like to write a function (in python) that would take an image and create a thumbnail with these to properties.

EDIT: So there are 3 answers right now and all of them are half right.

I need a function that not only resizes the image proportionally, but also makes sure the file size is smaller than 10KB. How do I do that?

+1  A: 

Did you read the PIL documentation? There is an image.thumbnail method.

Daniel Roseman
Please see my edit.
quilby
+3  A: 

In this short post, Mike Fletcher and the effbot show (and discuss detailed variation) an excellent approach for the task you want to do.

Edit: as for the 10K requirement, it's hard (to say the least;-) to predict how well an image will compress, depending on the image's format, since today's compression algorithms are so subtle. If you want your thumbnail to be just about as large (in pixels) as feasible while respecting a <10K requirement, you may have to use a "trial and error" approach making successively more refined guesses about the scaling factor, until you reach an acceptable result.

For example, here's a "binary search" approach to getting the correct size (there may well be better strategies!), with ample print statements &c to explain what's going on...:

import Image
import cStringIO
import math
import os
import stat

# try no more than 10 times, then give up
MAX_TRIES = 10

def getThumbnail(filename, max_bytes=(10*1024)):
    '''Get a thumbnail image of filename, <max_bytes'''
    original_size = os.stat(filename)[stat.ST_SIZE]
    print "Original file size: %.1f KB" % (original_size/1024.)
    image = Image.open(filename)
    image.load()
    print "Original image size: %dx%d pixels" % image.size
    min_bytes = int(0.9 * max_bytes)
    largest_side = max(image.size)
    smallest_side = 16
    for attempt in range(MAX_TRIES):
        try_side = (largest_side + smallest_side) / 2
    print "Attempt #%d of %d" % (attempt+1, MAX_TRIES)
    print "Side must be within [%d:%d], now trying %d" % (
        smallest_side, largest_side, try_side)
    thumb = image.copy()
    thumb.thumbnail((try_side,try_side), Image.ANTIALIAS)
    afile = cStringIO.StringIO()
    thumb.save(afile, "PNG")
    resulting_size = len(afile.getvalue())
    afile.close()
    print "Reduced file size: %.1f KB" % (resulting_size/1024.)
    print "Reduced image size: %dx%d pixels" % thumb.size
    if min_bytes <= resulting_size <= max_bytes:
        print "Success!"
        return thumb
    elif resulting_size > max_bytes:
        print "Too large (>%d), reducing more" % max_bytes
        largest_side = try_side
    else:
        print "Too small (<%d), reducing less" % min_bytes
        smallest_side = try_side
    print "too many attempts, returning what I've got!"
    return thumb

def main():
    thumb = getThumbnail("pyth.png")
    print "Reduced image size: %dx%d pixels" % thumb.size
    print "Saving to thumb.png"
    thumb.save("thumb.png")
    thumb_size = os.stat("thumb.png")[stat.ST_SIZE]
    print "Reduced file size: %.1f KB" % (thumb_size/1024.)
    print "Done, bye!"

if __name__ == '__main__':
    main()
Alex Martelli
Please see my edit.
quilby
+1  A: 

Use PIL, see sample code here to resize keeping aspect ratio

http://stackoverflow.com/questions/273946/how-do-i-resize-an-image-using-pil-and-maintain-its-aspect-ratio

see how to do similar thing on a dir

http://stackoverflow.com/questions/1048658/resize-images-in-directory/1048793#1048793

So above links describe how to resize images using PIL, now coming to your question of max size of 10KB, that can be achieved easily e.g.

Suppose size required is 100x100, and we use JPEG compression, 100% JPEG quality takes abt 9 bits per pixel(see http://en.wikipedia.org/wiki/JPEG), that means size of 100x100 image would be 100x100x9/(1024x8) = 11KB, so at quality=100 you are almost achieving your goal, but if you still want 10KB only you can set quality=90, and in general you can pass quality as param to resize function and reduce quality by 10% if image size is above 10KB but I think that is not needed, at 90% quality all JPEG images would be < 10KB.

Also note that without compression also image size is just 30KB for RGB image, and if you reduce size to 60x60 pixels image size would be only 10KB without any compression i.e you can use bmp images and if you want lesses sizer but lossless compression you can choose PNG.

So in conclusion your target of 10KB is very easy to achieve.

Anurag Uniyal
Please see my edit.
quilby
I have added "how to keep size limited" to my answer
Anurag Uniyal