Search code examples
pythonimagingimage-resizingpython-imaging-library

What's the simplest way to resize an image to a given bounded area?


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.


Solution

  • 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,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)
    
        # for centering use half the size difference of the image and the crop area
        dx = (imagewidth-cropwidth)/2
        dy = (imageheight-cropheight)/2
    
        # return bounding box of centered crop area on source image
        return dx,dy, cropwidth+dx,cropheight+dy
    
    
    if __name__=='__main__':
    
        print("===")
        bbox = cropbbox(1024,768, 128,128)
        print("cropbbox(1024,768, 128,128): {}".format(bbox))
    
        print("===")
        bbox = cropbbox(768,1024, 128,128)
        print("cropbbox(768,1024, 128,128): {}".format(bbox))
    
        print("===")
        bbox = cropbbox(1024,1024, 96,128)
        print("cropbbox(1024,1024, 96,128): {}".format(bbox))
    
        print("===")
        bbox = cropbbox(1024,1024, 128,96)
        print("cropbbox(1024,1024, 128,96): {}".format(bbox))
    

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