views:

55

answers:

2

I'd like to create a function, like:

def generateThumbnail(self, width, height):
     """
     Generates thumbnails for an image
     """
     im = Image.open(self._file)
     im.thumbnail((width, height), Image.ANTIALIAS)
     im.save(self._path + str(width) + 'x' + 
             str(height) + '-' + self._filename, "JPEG")

Where a file can be given and resized.

The current function works great except it does not crop when necessary.

In the case that a rectangular image is given, and a square resize is required (width = height), some centered-weighted cropping will have to be done.

How can I do this?

Thanks!

A: 

You have Image#crop. Is this your question? or how to calculate the crop coordinates? (which depends how you want to make it)

tokland
+1  A: 

You need to crop the image properly before resizing it. The basic idea is to determine the largest rectangular area of the source image having the same aspect (width to height) ratio as the thumbnail image and then trim off (crop) any excess around it before resizing to the thumbnail's dimensions). Here's a function which will compute the size and location of such a cropping area:

def cropbbox(imagewidth,imageheight, thumbwidth,thumbheight):
    """ cropbbox(imagewidth,imageheight, thumbwidth,thumbheight)

        Compute a centered image crop area for making thumbnail images.
          imagewidth,imageheight are source image dimensions
          thumbwidth,thumbheight are thumbnail image dimensions

        Returns bounding box pixel coordinates of the cropping area 
        in this order (left, upper, right, and lower).
    """
    # determine scale factor
    fx = float(imagewidth)/thumbwidth
    fy = float(imageheight)/thumbheight
    f = fx if fx < fy else fy

    # calculate size of crop area
    cropheight,cropwidth = int(thumbheight*f),int(thumbwidth*f)

    # center the crop area on the source image
    dx = (imagewidth-cropwidth)/2
    dy = (imageheight-cropheight)/2

    # return bounding box of crop area
    return dx,dy, cropwidth+dx,cropheight+dy

if __name__=='__main__':

    print "==="
    bbox = cropbbox(1024,768, 128,128)
    print "cropbbox(1024,768, 128,128):", bbox

    print "==="
    bbox = cropbbox(1024,768, 128,128)
    print "cropbbox(1024,768, 128,128):", bbox

    print "==="
    bbox = cropbbox(1024,1024, 96,128)
    print "cropbbox(1024,1024, 96,128):", bbox

    print "==="
    bbox = cropbbox(1024,1024, 128,96)
    print "cropbbox(1024,1024, 128,96):", bbox

After determining the crop area, call im.crop(bbox) and then call im.thumbnail(...) on the image returned.

martineau
Hey this was really helpful. Thanks so much.
ensnare
You could avoid having to crop the original image by figuring out how much to scale it so that the result would fit within the thumbnail's boundaries, but not necessarily filling it completely.
martineau