TIMEX
TIMEX

Reputation: 271714

How can I take an image and get a 3x2 ratio image cropped from the center?

Is there an easy way to do this? In Python, I created a script to get a "square box" from an image...based on the center.

However, that killed some of my brain cells. Is there an easy way to do this for a 3x2 (3 width, 2 height), based on the center?

This is my "square box" script, but I don't feel like modifying it for the 3x2.

def letterbox(f,thumb_w=None,thumb_h=None):
    try:
        im = Image.open(StringIO(f))
        imagex = int(im.size[0])
        imagey = int(im.size[1])
        thumb_size = (thumb_w,thumb_h) #what if it is too small!?? fix it.
        if imagex > imagey:
            setlen = imagey
            left = (imagex - setlen)/2
            top = 0
            height = setlen
            width = setlen
        if imagey > imagex:
            setlen = imagex
            left = 0
            top = (imagey - setlen)/2
            heigth = setlen
            width = setlen
        if imagex == imagey:
            left = 0
            top = 0
            height = imagey
            width = imagex
        box = (left,top,left+width,top+height)
        im = im.crop(box)
        #im.thumbnail(thumb_size,Image.ANTIALIAS)
        new_file = StringIO()
        im.save(new_file,'JPEG')
        new_file.seek(0)
    except Exception, e:
        pass
    return new_file

Is there a script online that can do what I need?

Upvotes: 0

Views: 1621

Answers (2)

martineau
martineau

Reputation: 123443

First of all, the name of your function is misleading because what it does is not letterbox images (regardless of the specific height/width [aspect ratio] involved) -- so here it's been renamed aspectcrop() to better describe what it does.

def aspectcrop(f, ratio=1.0):
    try:
        im = Image.open(StringIO(f))
        imagewidth,imageheight = im.size

        # determine the dimensions of a crop area
        # which is no wider or taller than the image
        if int(imagewidth*ratio) > imageheight:
            cropheight,cropwidth = imageheight,int(imageheight/ratio)
        else:
            cropwidth,cropheight = imagewidth,int(imagewidth*ratio)

        # center the crop area on the image (dx and/or dy will be zero)
        dx,dy = (imagewidth-cropwidth)/2,(imageheight-cropheight)/2

        # crop, save, and return image data
        im = im.crop((dx,dy,cropwidth+dx,cropheight+dy))
        new_file = StringIO()
        im.save(new_file,'JPEG')
        new_file.seek(0)
    except Exception, e:
        new_file = None # prevent exception on return
        pass
    return new_file

If any aspect ratio argument you pass to it is not naturally a whole number, make sure it's in floating point, as in 3./2. not 3/2. The default (1.0) could have been integer, but I explicitly made it floating point as a reminder. It might be better to pass it in as two separate integers and compute the ratio internally.

Lastly, I noticed that your example script had some visible traces of what looks like an attempt to create a thumbnail of the image, so my answer to the related question What's the simplest way in Python to resize an image to a given bounded area? might also be of interest (and could probably be integrated into this function without too much trouble).

Upvotes: 0

jilles de wit
jilles de wit

Reputation: 7138

Use an aspect ratio which is defined as imagex/imagey, so you use 3/2 for 3:2, 16/9 for 16:9 etc.

def letterbox(f,aspect_ratio=1):
    try:
        im = Image.open(StringIO(f))
        imagex = int(im.size[0])
        imagey = int(im.size[1])
        width = min(imagex, imagey*aspect_ratio)
        height = min(imagex/aspect_ratio, imagey)
        left =(imagex - width)/2
        top = (imagey - height)/2
        box = (left,top,left+width,top+height)
        im = im.crop(box)
        new_file = StringIO()
        im.save(new_file,'JPEG')
        new_file.seek(0)
    except Exception, e:
        pass
    return new_file

You might want to check for roundoff errors at some point, but otherwise this does it.

Upvotes: 2

Related Questions