Как изменить размер изображения с помощью PIL и сохранить его соотношение сторон?

Есть ли очевидный способ сделать это, который мне не хватает? Я просто пытаюсь сделать миниатюры.


person saturdayplace    schedule 07.11.2008    source источник
comment
Поскольку этот вопрос довольно старый, но полезный, а подушка предпочтительнее, для учебника на основе подушек взгляните на это: Pillow.readthedocs.org/en/latest/handbook/   -  person Wtower    schedule 26.03.2015
comment
Я создал небольшую библиотеку для изменения размера изображений, она может помочь: github.com/charlesthk / python-resize-image   -  person Charlesthk    schedule 28.04.2015
comment
Последний выпуск PIL был в 2006 году. Насколько я знаю, подушка является заменой. Последний выпуск Pillow состоялся 2 апреля 2020 года.   -  person David Medinets    schedule 17.04.2020


Ответы (21)


Определите максимальный размер. Затем вычислите коэффициент изменения размера, взяв min(maxwidth/width, maxheight/height).

Правильный размер oldsize*ratio.

Конечно, существует также библиотечный метод для этого: метод Image.thumbnail.
Ниже приведен (отредактированный) пример из документация PIL.

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
person gnud    schedule 07.11.2008
comment
Как говорится, пример взят из документации pil, и в этом примере (все еще) не используется флаг сглаживания. Но поскольку это, вероятно, то, что захотело бы большинство людей, я добавил это. - person gnud; 08.12.2011
comment
PIL устанавливает высоту нового изображения равной заданному размеру (здесь 128) и вычисляет ширину, чтобы сохранить соотношение сторон. Есть ли способ исправить ширину вместо высоты? возможно я задам это отдельным вопросом. - person eugene; 13.12.2012
comment
@ Евгений: попробуй что-нибудь вроде s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (хотя это для деления с плавающей запятой :) - person gnud; 13.12.2012
comment
Гарантирует ли использование вышеизложенного, что окончательное сохраненное изображение будет размером 128x128? - person mt88; 08.08.2015
comment
Обратите внимание, что ANTIALIAS больше не является предпочтительным для пользователей популярной вилки Pillow PIL. Pillow.readthedocs.org/ ru / 3.0.x / releasenotes / - person Joshmaker; 23.12.2015
comment
Для этого не требуется ANTIALIAS начиная с версии Pillow 2.5 и выше. - person Mark; 26.05.2016
comment
В документации для PIL Python 3 говорится, что thumbnail работает, только если итоговое изображение меньше исходного. Из-за этого я бы предположил, что использование resize - лучший способ. - person So S; 29.01.2017
comment
Решение, предоставленное @tomvon, сработало для меня лучше, потому что оно включало, как рассчитать соотношение - person Jose Luis de la Rosa; 02.07.2017
comment
По умолчанию метод PIL save() имеет низкое качество, вы можете использовать image.save(file_path, quality=quality_value), чтобы изменить качество. - person codezjx; 21.10.2017
comment
Можем ли мы таким же образом обрезать изображение? - person Arpit Singh; 10.05.2018
comment
@ArpitSingh для обрезки используйте метод Image.crop - Pillow.readthedocs.io/en/3.1.x/reference/ - person gnud; 10.05.2018
comment
в документах говорится: In Pillow 2.5 the default filter for thumbnail() was changed from NEAREST to ANTIALIAS - person pnd; 28.06.2018

Этот скрипт изменит размер изображения (somepic.jpg) с помощью PIL (Python Imaging Library) до ширины 300 пикселей и высоты, пропорциональной новой ширине. Для этого он определяет, какой процент от исходной ширины составляет 300 пикселей (img.size [0]), а затем умножает исходную высоту (img.size [1]) на этот процент. Измените базовую ширину на любое другое число, чтобы изменить ширину изображений по умолчанию.

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')
person tomvon    schedule 16.01.2009
comment
Если вы используете этот скрипт в Zope в качестве внешнего метода, вам понадобится строка из PIL import Image, чтобы избежать конфликтов пространства имен с Zope Image. - person tomvon; 16.01.2009
comment
Этот код дает мне 0-байтовый выходной файл sompic.jpg. Почему это происходит? Я использую Python 3.x - person imrek; 26.03.2017
comment
- Обновление: то же самое происходит в Python 2.7. - person imrek; 26.03.2017
comment
Возможно, я догадался. Если вы сохраняете .jpeg, используйте img.save('sompic.jpg', 'JPEG'). - person imrek; 26.03.2017
comment
nit: нет опции PIL.Image.ANTIALIAS для resize, на самом деле должно быть PIL.Image.LANCZOS, хотя они оба 1 по значению, см. Pillow.readthedocs.io/en/3.1.x/reference/ - person Fred Wu; 02.05.2017
comment
Различия между миниатюрой и изменением размера? - person Daniel Möller; 11.08.2017
comment
@FredWu PIL.Image.ANTIALIAS находится в PIL версии 1.1.7. Это идет с Anaconda3 v5.x - person J'e; 07.06.2018
comment
протестировано и РАБОТАЕТ с python 2.7.15, а также работает с файлами PNG без необходимости редактирования - person Marc Maxmeister; 20.02.2019
comment
Это повернет некоторые изображения (дополнительные сведения и решение см. Здесь stackoverflow.com/questions/4228530/) - person Ronen Ness; 25.03.2020

Я также рекомендую использовать метод эскизов PIL, потому что он избавляет вас от всех проблем с соотношением сторон.

Однако один важный совет: замените

im.thumbnail(size)

с участием

im.thumbnail(size,Image.ANTIALIAS)

по умолчанию PIL использует фильтр Image.NEAREST для изменения размера, что приводит к хорошей производительности, но низкому качеству.

person Franz    schedule 02.06.2009
comment
При этом вы можете только уменьшить размер изображения. Невозможно увеличить размер с помощью Image.thumbnail. - person burny; 01.10.2019

Основываясь на @tomvon, я закончил использовать следующее (выберите свой случай):

а) Изменение высоты (Я знаю новую ширину, поэтому мне нужна новая высота)

new_width  = 680
new_height = new_width * height / width 

б) Изменение ширины (Я знаю новую высоту, поэтому мне нужна новая ширина)

new_height = 680
new_width  = new_height * width / height

Тогда просто:

img = img.resize((new_width, new_height), Image.ANTIALIAS)
person muZk    schedule 14.05.2015
comment
Все ваши переменные перепутаны. В вашем сообщении говорится об изменении ширины, а затем об изменении высоты. И в вызове resize вы используете new_width как для высоты, так и для ширины? - person Zachafer; 20.01.2016
comment
Предложил исправление для этого @Zachafer - person Mo Beigi; 09.08.2016
comment
Лучше преобразовать их в целое число - person Black Thunder; 10.11.2019

Если вы пытаетесь сохранить такое же соотношение сторон, разве вы не измените размер на какой-то процент от исходного размера?

Например, половина оригинального размера

half = 0.5
out = im.resize( [int(half * s) for s in im.size] )
person Community    schedule 13.12.2008
comment
Возможно, изображения были разных размеров, и результат изменения размера должен был быть одинакового размера. - person Steen; 03.03.2010
comment
Для меня это было очень простое и элегантное решение. - person JoeTheShmoe; 23.09.2020
comment
Очень сексуально. В этом примере используется понимание списка. Также можно использовать генератор (заключенный в круглые скобки): out = im.resize( (int(half * s) for s in im.size) ) - person howdoicode; 07.11.2020

PIL уже имеет возможность обрезать изображение

img = ImageOps.fit(img, size, Image.ANTIALIAS)
person Cícero Verneck Corrêa    schedule 03.10.2012
comment
Это просто обрезает изображение, оно не поддерживает соотношение сторон. - person Radu; 22.12.2013
comment
Это никак не отвечает на вопрос. - person AMC; 27.02.2020

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))

Я использую эту библиотеку:

pip install python-resize-image
person guettli    schedule 27.09.2015
comment
Можно было просто использовать .thumbnail из PIL, ваше решение не работает с увеличением размера, а только с его уменьшением. - person Boz; 17.04.2021

Если вы не хотите / вам не нужно открывать изображение с помощью Pillow, используйте это:

from PIL import Image

new_img_arr = numpy.array(Image.fromarray(img_arr).resize((new_width, new_height), Image.ANTIALIAS))
person hoohoo-b    schedule 14.01.2019

Просто обновите этот вопрос с помощью более современной оболочки Эта библиотека обертывает Pillow (ответвление PIL) https://pypi.org/project/python-resize-image/

Позволить вам сделать что-то вроде этого: -

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()

Кучи больше примеров в приведенной выше ссылке.

person Shanness    schedule 15.05.2018
comment
resize_contain выглядит весьма полезным! - person Anytoe; 14.07.2019

Я пытался изменить размер некоторых изображений для видео слайд-шоу, и из-за этого мне нужен был не только один максимальный размер, но и максимальная ширина и максимальная высота (размер видеокадра).
И всегда была возможность сделать портретное видео ...
Метод Image.thumbnail был многообещающим, но я не мог увеличить масштаб изображения меньшего размера.

Поэтому после того, как я не смог найти очевидного способа сделать это здесь (или в некоторых других местах), я написал эту функцию и поместил ее сюда для тех, кто придет:

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)
person noEmbryo    schedule 22.01.2019

Откройте ваш файл изображения

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

Используйте PIL Image.resize (size, resample = 0), в котором вы заменяете кортеж размером 2 (ширина, высота) вашего изображения.

Это отобразит ваше изображение в исходном размере:

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

Это отобразит ваше изображение в 1/2 размера:

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

Это отобразит ваше изображение в размере 1/3:

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

Это отобразит ваше изображение в 1/4 размера:

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

и т. д. и т. д.

person user391339    schedule 18.02.2020
comment
Что такое display() и где он находится? - person Anthony; 11.05.2020
comment
@Anthony, display() - это функция iPython, которую можно использовать в Jupyter Notebook для отображения изображений. - person Qinjie; 09.07.2020

Я также добавлю версию изменения размера, которая сохраняет фиксированное соотношение сторон. В этом случае он отрегулирует высоту в соответствии с шириной нового изображения на основе исходного соотношения сторон, asp_rat, которое равно float (!). Но вместо этого, чтобы настроить ширину на высоту, вам просто нужно прокомментировать одну строку и раскомментировать другую в цикле else. Вы увидите, где.

Вам не нужны точки с запятой (;), я оставлю их, чтобы напомнить себе о синтаксисе языков, которые я использую чаще.

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");

И это сделано. Я старался задокументировать это как можно больше, чтобы было понятно.

Надеюсь, это может быть кому-то полезно!

person RockOGOlic    schedule 02.05.2020

Простой метод сохранения ограниченных соотношений и передачи максимальной ширины / высоты. Не самый красивый, но выполняет свою работу и его легко понять:

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

Вот скрипт Python, который использует эту функцию для запуска пакетного изменения размера изображения.

person AlexG    schedule 31.05.2018

Обновили ответ "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')
person Kanish Mathew    schedule 18.09.2019

Вы можете комбинировать Image.thumbnail PIL с sys.maxsize, если ограничение на изменение размера задано только для одного измерения (ширины или высоты).

Например, если вы хотите изменить размер изображения так, чтобы его высота была не более 100 пикселей, сохраняя при этом соотношение сторон, вы можете сделать что-то вроде этого:

import sys
from PIL import Image

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

Имейте в виду, что Image.thumbnail изменит размер изображения на месте, в отличие от Image.resize, который вместо этого возвращает изображение с измененным размером без изменения исходного.

person Vito Gentile    schedule 26.01.2021

Мой некрасивый пример.

Функция получает файл типа: «pic [0-9a-z]. [Extension]», изменяет их размер до 120x120, перемещает раздел в центр и сохраняет в «ico [0-9a-z]. [Extension]», работает с портретом и пейзаж:

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:])
person Nips    schedule 22.05.2013

Я изменил размер изображения таким образом, и оно работает очень хорошо

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)
person Siddhartha Mukherjee    schedule 08.04.2019

Следующий скрипт создает красивые эскизы всех изображений JPEG с сохранением соотношений сторон с максимальным разрешением 128x128.

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

Вы можете изменить размер изображения с помощью кода ниже:

From PIL import Image
img=Image.open('Filename.jpg') # paste image in python folder
print(img.size())
new_img=img.resize((400,400))
new_img.save('new_filename.jpg')
person Smit Parmar    schedule 09.04.2020

person    schedule
comment
При этом соотношение сторон исходного изображения не сохраняется. При этом размер изображения увеличивается до 200x300, что приводит к сжатию или растяжению изображения. - person burny; 01.10.2019
comment
Это никоим образом не отвечает на вопрос. - person AMC; 27.02.2020

person    schedule
comment
К сожалению, это не отвечает на вопрос, который явно касается библиотеки PIL - и не сохраняет соотношение сторон !. Кроме того, вы можете дать некое описание своего подхода, чтобы объяснить свои мысли. - person max; 09.08.2020