ChatGPT解决这个技术问题 Extra ChatGPT

How do I resize an image using PIL and maintain its aspect ratio?

Is there an obvious way to do this that I'm missing? I'm just trying to make thumbnails.

Since this question is quite old but useful, and pillow is rather preferred, for a pillow-based tutorial take a look at this: pillow.readthedocs.org/en/latest/handbook/…
I have created a small library for resizing images, it can be of any help : github.com/charlesthk/python-resize-image
The last release of PIL was in 2006. The pillow packge is the replacement as far as I know. Pillow's latest release was April 2, 2020.

o
oberbaum

Define a maximum size. Then, compute a resize ratio by taking min(maxwidth/width, maxheight/height).

The proper size is oldsize*ratio.

There is of course also a library method to do this: the method Image.thumbnail.
Below is an (edited) example from the PIL documentation.

import os, sys
import Image

size = 128, 128

for infile in sys.argv[1:]:
    outfile = os.path.splitext(infile)[0] + ".thumbnail"
    if infile != outfile:
        try:
            im = Image.open(infile)
            im.thumbnail(size, Image.ANTIALIAS)
            im.save(outfile, "JPEG")
        except IOError:
            print "cannot create thumbnail for '%s'" % infile

Like it says, the example was from the pil documentation, and that example (still) doesn't use the antialias flag. Since it's probably what most people would want, though, I added it.
@Eugene: try something like s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (that's for floating point division though :)
Note that ANTIALIAS is no longer preferred for users of the popular Pillow fork of PIL. pillow.readthedocs.org/en/3.0.x/releasenotes/…
The Python 3 documentation for PIL says that thumbnail only works if the resulting image is smaller than the original one. Because of that I would guess that using resize is the better way.
By default PIL save() method is poor quality, you can use image.save(file_path, quality=quality_value) to change the quality.
r
rubo77

This script will resize an image (somepic.jpg) using PIL (Python Imaging Library) to a width of 300 pixels and a height proportional to the new width. It does this by determining what percentage 300 pixels is of the original width (img.size[0]) and then multiplying the original height (img.size[1]) by that percentage. Change "basewidth" to any other number to change the default width of your images.

from PIL import Image

basewidth = 300
img = Image.open('somepic.jpg')
wpercent = (basewidth/float(img.size[0]))
hsize = int((float(img.size[1])*float(wpercent)))
img = img.resize((basewidth,hsize), Image.ANTIALIAS)
img.save('somepic.jpg')

If you are using this script in Zope as an External method you will need the line "from PIL import Image" to avoid namespace clashes with Zope's "Image".
This code gets me a 0 byte output file sompic.jpg. Why does this happen? I'm using Python 3.x
– Update: the same happens in Python 2.7.
I may have figured out. If you are saving a .jpeg, use img.save('sompic.jpg', 'JPEG').
nit: there is no PIL.Image.ANTIALIAS option for resize, should actually be PIL.Image.LANCZOS, although they are both 1 in value, see pillow.readthedocs.io/en/3.1.x/reference/…
F
Franz

I also recommend using PIL's thumbnail method, because it removes all the ratio hassles from you.

One important hint, though: Replace

im.thumbnail(size)

with

im.thumbnail(size,Image.ANTIALIAS)

by default, PIL uses the Image.NEAREST filter for resizing which results in good performance, but poor quality.


With this, you can only decrease the size of an image. It is not possible to increase the size with Image.thumbnail.
m
muZk

Based in @tomvon, I finished using the following (pick your case):

a) Resizing height (I know the new width, so I need the new height)

new_width  = 680
new_height = new_width * height / width 

b) Resizing width (I know the new height, so I need the new width)

new_height = 680
new_width  = new_height * width / height

Then just:

img = img.resize((new_width, new_height), Image.ANTIALIAS)

Your variables are all mixed up. Your post says resizing width, and then resizes height. And in the resize call, you are using the new_width for both height and width?
Suggested a fix for that @Zachafer
Better convert them to integer
Got error : integer argument expected, got float
佚名

If you are trying to maintain the same aspect ratio, then wouldn't you resize by some percentage of the original size?

For example, half the original size

half = 0.5
out = im.resize( [int(half * s) for s in im.size] )

It could be that the images were of varying sizes and the resize result was required to be of uniform size
This was a very simple and elegant solution for me
Very sexy. This example uses a list comprehension. Use of a generator (wrapping in parenthesis) also works: out = im.resize( (int(half * s) for s in im.size) )
Least complex answer if you need a relative resize and not a specific uniform size. Easy to change as well if you need bigger instead of smaller.
Can clean even further (integer division): out = image.resize((s//2 for s in image.size))
B
BenT
from PIL import Image

img = Image.open('/your image path/image.jpg') # image extension *.png,*.jpg
new_width  = 200
new_height = 300
img = img.resize((new_width, new_height), Image.ANTIALIAS)
img.save('output image name.png') # format may what you want *.png, *jpg, *.gif

This does not keep the aspect ratio of the source image. It forces the image to 200x300 and will result in a squeezed or stretched image.
This does not answer the question in any way.
Wrong answer for the op. The question was 'How do I resize an image using PIL and maintain its ASPECT RATIO?"
g
guettli
from PIL import Image
from resizeimage import resizeimage

def resize_file(in_file, out_file, size):
    with open(in_file) as fd:
        image = resizeimage.resize_thumbnail(Image.open(fd), size)
    image.save(out_file)
    image.close()

resize_file('foo.tif', 'foo_small.jpg', (256, 256))

I use this library:

pip install python-resize-image

Could've just used .thumbnail from PIL, your solution doesn't work on increasing size, just decreasing.
h
hoohoo-b

If you don't want / don't have a need to open image with Pillow, use this:

from PIL import Image

new_img_arr = numpy.array(Image.fromarray(img_arr).resize((new_width, new_height), Image.ANTIALIAS))

V
Vito Gentile

You can combine PIL's Image.thumbnail with sys.maxsize if your resize limit is only on one dimension (width or height).

For instance, if you want to resize an image so that its height is no more than 100px, while keeping aspect ratio, you can do something like this:

import sys
from PIL import Image

image.thumbnail([sys.maxsize, 100], Image.ANTIALIAS)

Keep in mind that Image.thumbnail will resize the image in place, which is different from Image.resize that instead returns the resized image without changing the original one.

EDIT: Image.ANTIALIAS raises a deprecation warning, and will be removed in PIL 10 (July 2023). Instead, you should use Resampling.LANCZOS:

import sys
from PIL import Image
from PIL.Image import Resampling

image.thumbnail([sys.maxsize, 100], Resampling.LANCZOS)

S
Shanness

Just updating this question with a more modern wrapper This library wraps Pillow (a fork of PIL) https://pypi.org/project/python-resize-image/

Allowing you to do something like this :-

from PIL import Image
from resizeimage import resizeimage

fd_img = open('test-image.jpeg', 'r')
img = Image.open(fd_img)
img = resizeimage.resize_width(img, 200)
img.save('test-image-width.jpeg', img.format)
fd_img.close()

Heaps more examples in the above link.


resize_contain looks actually quite useful!
R
RockOGOlic

I will also add a version of the resize that keeps the aspect ratio fixed. In this case, it will adjust the height to match the width of the new image, based on the initial aspect ratio, asp_rat, which is float (!). But, to adjust the width to the height, instead, you just need to comment one line and uncomment the other in the else loop. You will see, where.

You do not need the semicolons (;), I keep them just to remind myself of syntax of languages I use more often.

from PIL import Image

img_path = "filename.png";
img = Image.open(img_path);     # puts our image to the buffer of the PIL.Image object

width, height = img.size;
asp_rat = width/height;

# Enter new width (in pixels)
new_width = 50;

# Enter new height (in pixels)
new_height = 54;

new_rat = new_width/new_height;

if (new_rat == asp_rat):
    img = img.resize((new_width, new_height), Image.ANTIALIAS); 

# adjusts the height to match the width
# NOTE: if you want to adjust the width to the height, instead -> 
# uncomment the second line (new_width) and comment the first one (new_height)
else:
    new_height = round(new_width / asp_rat);
    #new_width = round(new_height * asp_rat);
    img = img.resize((new_width, new_height), Image.ANTIALIAS);

# usage: resize((x,y), resample)
# resample filter -> PIL.Image.BILINEAR, PIL.Image.NEAREST (default), PIL.Image.BICUBIC, etc..
# https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

# Enter the name under which you would like to save the new image
img.save("outputname.png");

And, it is done. I tried to document it as much as I can, so it is clear.

I hope it might be helpful to someone out there!


n
noEmbryo

I was trying to resize some images for a slideshow video and because of that, I wanted not just one max dimension, but a max width and a max height (the size of the video frame).
And there was always the possibility of a portrait video...
The Image.thumbnail method was promising, but I could not make it upscale a smaller image.

So after I couldn't find an obvious way to do that here (or at some other places), I wrote this function and put it here for the ones to come:

from PIL import Image

def get_resized_img(img_path, video_size):
    img = Image.open(img_path)
    width, height = video_size  # these are the MAX dimensions
    video_ratio = width / height
    img_ratio = img.size[0] / img.size[1]
    if video_ratio >= 1:  # the video is wide
        if img_ratio <= video_ratio:  # image is not wide enough
            width_new = int(height * img_ratio)
            size_new = width_new, height
        else:  # image is wider than video
            height_new = int(width / img_ratio)
            size_new = width, height_new
    else:  # the video is tall
        if img_ratio >= video_ratio:  # image is not tall enough
            height_new = int(width / img_ratio)
            size_new = width, height_new
        else:  # image is taller than video
            width_new = int(height * img_ratio)
            size_new = width_new, height
    return img.resize(size_new, resample=Image.LANCZOS)

K
Kanish Mathew

Have updated the answer above by "tomvon"

from PIL import Image

img = Image.open(image_path)

width, height = img.size[:2]

if height > width:
    baseheight = 64
    hpercent = (baseheight/float(img.size[1]))
    wsize = int((float(img.size[0])*float(hpercent)))
    img = img.resize((wsize, baseheight), Image.ANTIALIAS)
    img.save('resized.jpg')
else:
    basewidth = 64
    wpercent = (basewidth/float(img.size[0]))
    hsize = int((float(img.size[1])*float(wpercent)))
    img = img.resize((basewidth,hsize), Image.ANTIALIAS)
    img.save('resized.jpg')

This works fine. Instead of saving the image with a new name, can I pass it directly to a canvas or frame?
u
user391339

Open your image file

from PIL import Image
im = Image.open("image.png")

Use PIL Image.resize(size, resample=0) method, where you substitute (width, height) of your image for the size 2-tuple.

This will display your image at original size:

display(im.resize((int(im.size[0]),int(im.size[1])), 0) )

This will display your image at 1/2 the size:

display(im.resize((int(im.size[0]/2),int(im.size[1]/2)), 0) )

This will display your image at 1/3 the size:

display(im.resize((int(im.size[0]/3),int(im.size[1]/3)), 0) )

This will display your image at 1/4 the size:

display(im.resize((int(im.size[0]/4),int(im.size[1]/4)), 0) )

etc etc


What is display() and where is it located?
@Anthony, display() is an iPython function and can be used in Jupyter Notebook to display images.
A
Alex

A simple method for keeping constrained ratios and passing a max width / height. Not the prettiest but gets the job done and is easy to understand:

def resize(img_path, max_px_size, output_folder):
    with Image.open(img_path) as img:
        width_0, height_0 = img.size
        out_f_name = os.path.split(img_path)[-1]
        out_f_path = os.path.join(output_folder, out_f_name)

        if max((width_0, height_0)) <= max_px_size:
            print('writing {} to disk (no change from original)'.format(out_f_path))
            img.save(out_f_path)
            return

        if width_0 > height_0:
            wpercent = max_px_size / float(width_0)
            hsize = int(float(height_0) * float(wpercent))
            img = img.resize((max_px_size, hsize), Image.ANTIALIAS)
            print('writing {} to disk'.format(out_f_path))
            img.save(out_f_path)
            return

        if width_0 < height_0:
            hpercent = max_px_size / float(height_0)
            wsize = int(float(width_0) * float(hpercent))
            img = img.resize((max_px_size, wsize), Image.ANTIALIAS)
            print('writing {} to disk'.format(out_f_path))
            img.save(out_f_path)
            return

Here's a python script that uses this function to run batch image resizing.


R
Ruhul Amin

To make the new image half the width and half the height of the original image, Use below code :

  from PIL import Image
  im = Image.open("image.jpg")
  resized_im = im.resize((round(im.size[0]*0.5), round(im.size[1]*0.5)))
    
  #Save the cropped image
  resized_im.save('resizedimage.jpg')

To resize with fixed width with ration:

from PIL import Image
new_width = 300
im = Image.open("img/7.jpeg")
concat = int(new_width/float(im.size[0]))
size = int((float(im.size[1])*float(concat)))
resized_im = im.resize((new_width,size), Image.ANTIALIAS)
#Save the cropped image
resized_im.save('resizedimage.jpg')

M
Mohamed TOUATI
# Importing Image class from PIL module
from PIL import Image

# Opens a image in RGB mode
im = Image.open(r"C:\Users\System-Pc\Desktop\ybear.jpg")

# Size of the image in pixels (size of original image)
# (This is not mandatory)
width, height = im.size

# Setting the points for cropped image
left = 4
top = height / 5
right = 154
bottom = 3 * height / 5

# Cropped image of above dimension
# (It will not change original image)
im1 = im.crop((left, top, right, bottom))
newsize = (300, 300)
im1 = im1.resize(newsize)
# Shows the image in image viewer
im1.show()

I like the image.show()
N
Nips

My ugly example.

Function get file like: "pic[0-9a-z].[extension]", resize them to 120x120, moves section to center and save to "ico[0-9a-z].[extension]", works with portrait and landscape:

def imageResize(filepath):
    from PIL import Image
    file_dir=os.path.split(filepath)
    img = Image.open(filepath)

    if img.size[0] > img.size[1]:
        aspect = img.size[1]/120
        new_size = (img.size[0]/aspect, 120)
    else:
        aspect = img.size[0]/120
        new_size = (120, img.size[1]/aspect)
    img.resize(new_size).save(file_dir[0]+'/ico'+file_dir[1][3:])
    img = Image.open(file_dir[0]+'/ico'+file_dir[1][3:])

    if img.size[0] > img.size[1]:
        new_img = img.crop( (
            (((img.size[0])-120)/2),
            0,
            120+(((img.size[0])-120)/2),
            120
        ) )
    else:
        new_img = img.crop( (
            0,
            (((img.size[1])-120)/2),
            120,
            120+(((img.size[1])-120)/2)
        ) )

    new_img.save(file_dir[0]+'/ico'+file_dir[1][3:])

S
Sterling Archer

I resizeed the image in such a way and it's working very well

from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
import os, sys
from PIL import Image


def imageResize(image):
    outputIoStream = BytesIO()
    imageTemproaryResized = imageTemproary.resize( (1920,1080), Image.ANTIALIAS) 
    imageTemproaryResized.save(outputIoStream , format='PNG', quality='10') 
    outputIoStream.seek(0)
    uploadedImage = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" % image.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)

    ## For upload local folder
    fs = FileSystemStorage()
    filename = fs.save(uploadedImage.name, uploadedImage)

m
mustafa candan

The simplest way that worked for me

image = image.resize((image.width*2, image.height*2), Image.ANTIALIAS)

Example

from PIL import Image, ImageGrab
image = ImageGrab.grab(bbox=(0,0,400,600)) #take screenshot
image = image.resize((image.width*2, image.height*2), Image.ANTIALIAS)
image.save('Screen.png')

R
Riz.Khan

The following script creates nice thumbnails of all JPEG images preserving aspect ratios with 128x128 max resolution.

from PIL import Image
img = Image.open("D:\\Pictures\\John.jpg")
img.thumbnail((680,680))
img.save("D:\\Pictures\\John_resize.jpg")

2
24_saurabh sharma
######get resize coordinate after resize the image using this function#####
def scale_img_pixel(points,original_dim,resize_dim):
        multi_list = [points]
        new_point_list = []
        multi_list_point = []
        for point in multi_list:
            multi_list_point.append([point[0],point[1]])
            multi_list_point.append([point[2],point[3]])
        for lsingle_point in multi_list_point:
            x1 = int((lsingle_point[0] * (resize_dim[0] / original_dim[0])))
            y1 = int((lsingle_point[1] * (resize_dim[1] / original_dim[1])))
            new_point_list.append(x1)
            new_point_list.append(y1)
            
        return new_point_list
    
    
    points = [774,265,909,409]
    original_dim = (1237,1036)
    resize_dim = (640,480)
    result = scale_img_pixel(points,original_dim,resize_dim)
    print("result: ", result)  

m
max
import cv2
from skimage import data 
import matplotlib.pyplot as plt
from skimage.util import img_as_ubyte
from skimage import io
filename='abc.png'
image=plt.imread(filename)
im=cv2.imread('abc.png')
print(im.shape)
im.resize(300,300)
print(im.shape)
plt.imshow(image)

Unfortunately, this does not answer to the question, which explicitly addresses the library PIL -- and it does not keep the aspect ratio!. Further, you may provide a sort description of your approach to explain your thoughts