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

1

3

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.

Harper answered 6/10, 2010 at 14:59 Comment(0)
P
6

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.

Pattipattie answered 6/10, 2010 at 19:42 Comment(2)
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.Pattipattie
For anybody interested, I did something similar using Ruby: https://mcmap.net/q/1174406/-crop-image-to-biggest-centred-part-with-a-ratio-closedDefect

© 2022 - 2024 — McMap. All rights reserved.