57

Calling

image = Image.open(data)
image.thumbnail((36,36), Image.NEAREST)

will maintain the aspect ratio. But I need to end up displaying the image like this:

<img src="/media/image.png" style="height:36px; width:36px" />

Can I have a letterbox style with either transparent or white around the image?

Paul Tarjan
  • 48,968
  • 59
  • 172
  • 213

7 Answers7

169

PIL already has a function to do exactly that:

from PIL import Image, ImageOps
thumb = ImageOps.fit(image, size, Image.ANTIALIAS)
Eric Platon
  • 9,819
  • 6
  • 41
  • 48
Cesar Canassa
  • 18,659
  • 11
  • 66
  • 69
  • It was 2 years later... ;) This is a good answer for the question, the older answer is good to have too, in case you want to do something similar but not quite the same – R Thiede Mar 23 '12 at 12:30
  • Sorry for the double post. For the sake of completeness: the first line above should read "from PIL import Image, ImageOps". (Assuming no other relevant imports elsewhere in the code.) – R Thiede Mar 23 '12 at 13:45
  • 3
    This method somehow cropping my image to fit into the resolution. – Babu Sep 21 '12 at 07:24
  • That method does not apply transparency in my case. Is there any way to fix this? – Alp Jul 02 '13 at 22:21
  • 8
    Using this method does *NOT* produce a square image with transparent padding. – earthmeLon Sep 07 '13 at 21:59
  • 9
    Why so many upvotes? This doesn't answer the question at all. – James Hopkin Sep 13 '14 at 10:08
  • 5
    it gets lots of upvotes because this question comes up high in a Google search for `python pil resize and crop` and this is an elegant solution for that, its irrelevance to the OP notwithstanding. – jcomeau_ictx Dec 14 '16 at 17:07
  • Or `python PIL center image square`, but this seem to crop it, indeed. – luckydonald Dec 16 '16 at 16:56
83

Paste the image into a transparent image with the right size as a background

from PIL import Image
size = (36, 36)
image = Image.open(data)
image.thumbnail(size, Image.ANTIALIAS)
background = Image.new('RGBA', size, (255, 255, 255, 0))
background.paste(
    image, (int((size[0] - image.size[0]) / 2), int((size[1] - image.size[1]) / 2))
)
background.save("output.png")

EDIT: fixed syntax error

luckydonald
  • 5,976
  • 4
  • 38
  • 58
Nadia Alramli
  • 111,714
  • 37
  • 173
  • 152
3
from PIL import Image

import StringIO

def thumbnail_image():
    image = Image.open("image.png")
    image.thumbnail((300, 200))
    thumb_buffer = StringIO.StringIO()
    image.save(thumb_buffer, format=image.format)
    fp = open("thumbnail.png", "w")
    fp.write(thumb_buffer.getvalue())
    fp.close()
Naveen Agarwal
  • 930
  • 7
  • 7
2

Update of Cesar Canassa's answer.

This will create a thumbnail of image.jpg as image_thumb.jpg:

from PIL import Image, ImageOps
fname = 'image.jpg'
size = (48,48)
thumb = ImageOps.fit(Image.open(fname), size, Image.ANTIALIAS)
thumb.save('{}_thumb.jpg'.format(fname[:fname.rfind('.')]), "JPEG")
suhailvs
  • 20,182
  • 14
  • 100
  • 98
1

Or this, maybe... (forgive spaghetti)

from PIL import Image

def process_image(image, size):
    if image.size[0] > size[0] or image.size[1] > size[1]:
        #preserve original
        thumb = image.copy()
        thumb.thumbnail(size,Image.ANTIALIAS)
        img = thumb.copy()
    img_padded = Image.new("RGBA",size)
    img_padded.paste(image,(int((size[0]-image.size[0])/2),int((size[1]-image.size[1])/2)))
    return img_padded
luckydonald
  • 5,976
  • 4
  • 38
  • 58
Khymeira
  • 11
  • 1
1

Why not simply use the resize method ?

from PIL import Image
image = Image.open('/path/to/img.png')
image = image.resize((36,36), Image.ANTIALIAS)

See recommendations for image resizing in this release note: https://pillow.readthedocs.io/en/stable/releasenotes/5.3.0.html

titsitits
  • 104
  • 4
0

you can wrap Nadia's answer in this function, which gives you control over size and background.

def make_square(im, min_size=36, fill_color=(255, 255, 255, 0)):
    x, y = im.size
    size = min(min_size, x, y)
    new_im = Image.new('RGBA', (size, size), fill_color)
    im.thumbnail((256, 256))
    new_im.paste(im, (int((x - size) / 2), int((y -size) / 2))
    return new_im
Amir Imani
  • 3,118
  • 2
  • 22
  • 24