[Image-SIG] proposal for additional method...

kcazabon kcazabon" <kcazabon@home.com
Fri, 18 Feb 2000 22:48:49 -0800

This is a multi-part message in MIME format.

Content-Type: text/plain;
Content-Transfer-Encoding: 7bit

In a lot of the image processing I've needed to do, there is the need to
crop and resize an image to a different aspect ratio.  Doing so has been a
bit of a pain, so I've written a little function that will do so for you, as
well as providing a few extra capabilities.

It is similar to the im.thumbnail() method, but instead of fitting entirely
within the size you give, it will crop to fill the exact size you want.  The
method of cropping is user-definable as well (from the center, from the
side, 30% from the top, whatever you want).

This way (assuming my function was implemented as a method in PIL), if you
had for example a 300x400 pixel image that you wanted to center-crop and
resize to be 512x768 (without distorting it), you could simply use:

im = im.fit((512,768))

alternatively, you could be a little more specific, setting the following

-"method" of interpolation when resizing (0,2,3, same as in
im.resize((size), method) )
-"bleed", a default amount (in decimal percent, 0.0 - 0.49999) to crop off
all edges, useful for removing the edges of a scanned image that may include
frame edges, etc.
-"centering" to control where the crop happens, and if it's centered or
biased towards cropping more from some sides than others

I've attached my 'ImageFit' module that demonstrates these functions.  The
only difference is that the first argument needs to be a PIL image object.
Feel free to try it out, and if it's easy to implement, I'd like to see it
as part of the standard PIL methods.

Kevin Cazabon.

Content-Type: text/plain;
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;

import Image

def fit(inputImage, outputSize, method=3D0, bleed=3D0.0, =
        This method returns a sized and cropped version of the image, =
cropped to the aspect ratio and
        size that you request.  It can be customized to your needs with =
"method", "bleed", and "centering"
        as required.
        inputImage =3D an PIL Image object, could easily be changed to =
"self" to make this a method of an Image object.
        outputSize =3D (width, height) in pixels of image to return
        method =3D resizing method:  0=3D(replication), 2=3D(bi-linear), =
        bleed =3D decimal percentage (0-0.49999) of width/height to crop =
off as a minumum around the outside of the image
                    This allows you to remove a default amount of the =
image around the outside, for
                    'cleaning up' scans that may have edges of negs =
showing, or whatever.
                    This percentage is removed from EACH side, not a =
total amount.
        centering =3D [left, top], percentages to crop of each side to =
fit the aspect ratio you require.
                    This function allows you to customize where the crop =
occurs, whether it is a=20
                    'center crop' or a 'top crop', or whatever. Default =
is center-cropped.
                    [0.5, 0.5] is center cropping (i.e. if cropping the =
width, take 50% off of the left side (and therefore 50% off the right =
side), and same with Top/Bottom)
                    [0.0, 0.0] will crop from the top left corner (i.e. =
if cropping the width, take all of the crop off of the right side, and =
if cropping the height, take all of it off the bottom)
                    [1.0, 0.0] will crop from the bottom left corner, =
etc. (i.e. if cropping the width, take all of the crop off the left =
side, and if cropping the height take none from the Top (and therefore =
all off the bottom))=20
                    by Kevin Cazabon, Feb 17/2000
                    http://members.home.com/kcazabon """
    # ensure inputs are valid
    if type(centering) !=3D type([]):
        centering =3D [centering[0], centering[1]]

    if centering[0] > 1.0 or centering[0] < 0.0:
        centering [0] =3D 0.50
    if centering[1] > 1.0 or centering[1] < 0.0:
        centering[1] =3D 0.50
    if bleed > 0.49999 or bleed < 0.0:
        bleed =3D 0.0
    if method not in [0, 2, 3]:
        method =3D 0

    # calculate the area to use for resizing and cropping, subtracting =
the 'bleed' around the edges          =20
    bleedPixels =3D (int((float(bleed) * float(inputImage.size[0])) + =
0.5), int((float(bleed) * float(inputImage.size[1])) + 0.5)) # number of =
pixels to trim off on Top and Bottom, Left and Right
    liveArea =3D (bleedPixels[0], bleedPixels[1], inputImage.size[0] - =
bleedPixels[0] - 1, inputImage.size[1] - bleedPixels[1] - 1)
    liveSize =3D (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1])
    # calculate the aspect ratio of the liveArea
    liveAreaAspectRatio =3D float(liveSize[0])/float(liveSize[1])
    # calculate the aspect ratio of the output image
    aspectRatio =3D float(outputSize[0])/float(outputSize[1])
    # figure out if the sides or top/bottom will be cropped off
    if liveAreaAspectRatio >=3D aspectRatio:
        # liveArea is wider than what's needed, crop the sides
        cropWidth =3D int((aspectRatio * float(liveSize[1])) + 0.5)
        cropHeight =3D liveSize[1]
        #liveArea is taller than what's needed, crop the top and bottom
        cropWidth =3D liveSize[0]
        cropHeight =3D int((float(liveSize[0])/aspectRatio) + 0.5)
    # make the crop   =20
    leftSide =3D int(liveArea[0] + (float(liveSize[0] - cropWidth) * =
    if leftSide < 0:
        leftSide =3D 0
    topSide =3D int(liveArea[1] + (float(liveSize[1] - cropHeight) * =
    if topSide < 0:
        topSide =3D 0
    outputImage =3D inputImage.crop((leftSide, topSide, leftSide + =
cropWidth, topSide + cropHeight))
    # resize the image and return it   =20
    return outputImage.resize(outputSize, method)