Як змінити розмір зображення за допомогою PIL та підтримувати його співвідношення сторін?


437

Чи є очевидний спосіб зробити це, що я пропускаю? Я просто намагаюся зробити ескізи.


9
Оскільки це запитання досить старе, але корисне, і подушка є скоріше бажаною, ознайомтесь із посібником на основі подушок: pillow.readthedocs.org/en/latest/handbook/…
Wtower

3
Я створив невелику бібліотеку для зміни розмірів зображень, вона може допомогти: github.com/charlesthk/python-resize-image
Charlesthk,

Останній випуск PIL був у 2006 році. Настільки, наскільки я знаю, накладка для подушок є заміною. Останній реліз Подушки був 2 квітня 2020 р.
Девід Медінець

Відповіді:


477

Визначте максимальний розмір. Потім обчисліть коефіцієнт зміни розміру, взявши 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

4
Як говориться, приклад був із документації на pil, і цей приклад (досі) не використовує прапор antialias. Оскільки це, мабуть, те, чого хотіли б більшість людей, я додав це.
gnud

3
PIL встановлює висоту нового зображення на заданий розмір (тут 128) та обчислює ширину, щоб зберегти співвідношення сторін. Чи є спосіб зафіксувати ширину замість висоти? можливо, я запитаю це окремим запитанням.
Євген

6
@Eugene: спробувати щось на кшталт s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (це для поділу з плаваючою комою, хоча :)
gnud

48
Зауважте, що ANTIALIASбільше не є бажаним для користувачів популярної вилки Pillow Pillow. pillow.readthedocs.org/en/3.0.x/releasenotes/…
Joshmaker

8
Документація Python 3 для PIL говорить, що thumbnailпрацює лише в тому випадку, якщо отримане зображення менше оригінального. Через це я б здогадався, що використання resize- це кращий спосіб.
Так S

253

Цей сценарій дозволить змінити розмір зображення (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('sompic.jpg') 

2
Якщо ви використовуєте цей скрипт у Zope як зовнішній метод, вам знадобиться рядок "з зображення імпорту PIL", щоб уникнути сутичок простору імен із "Зображенням" Zope.
tomvon

Цей код отримує мені вихідний файл 0 байтів sompic.jpg. Чому це відбувається? Я використовую Python 3.x
imrek

- Оновлення: те саме відбувається в Python 2.7.
imrek

Я, можливо, зрозумів. Якщо ви зберігаєте .jpeg, використовуйте img.save('sompic.jpg', 'JPEG').
imrek

5
nit: немає PIL.Image.ANTIALIASваріанту для resize, насправді має бути PIL.Image.LANCZOS, хоча вони обоє 1мають значення, див. pillow.readthedocs.io/en/3.1.x/reference/…
Фред Ву

66

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

Хоча один важливий натяк: Замінити

im.thumbnail(size)

з

im.thumbnail(size,Image.ANTIALIAS)

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


2
За допомогою цього ви можете лише зменшити розмір зображення. Збільшити розмір за допомогою неможливо Image.thumbnail.
Бурхливий

45

Базуючись у @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)

6
Усі ваші змінні змішані. У вашій публікації вказано розмір ширини, а потім змінено висоту. А у resizeдзвінку ви використовуєте як new_widthдля висоти, так і для ширини?
Захафер

Запропонував виправлення для цього @Zachafer
Mo Beigi

1
Краще перетворіть їх на цілі числа
Black Thunder

18

У PIL вже є можливість обрізати зображення

img = ImageOps.fit(img, size, Image.ANTIALIAS)

20
Що просто обробляє зображення, воно не підтримує співвідношення сторін.
Раду

2
Це жодним чином не відповідає на питання.
AMC

15
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

8
Це не підтримує співвідношення сторін вихідного зображення. Це примушує зображення до 200x300 і призведе до стисненого або розтягнутого зображення.
Бурхливий

Це жодним чином не відповідає на питання.
AMC

12

Якщо ви намагаєтеся підтримувати однакове співвідношення сторін, то чи не змінили б ви якийсь відсоток від початкового розміру?

Наприклад, половина початкового розміру

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

13
Можливо, зображення були різного розміру, а результат зміни розміру повинен був бути однакового розміру
Steen

7
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

7

Якщо ви не хочете / не маєте потреби відкривати зображення за допомогою Подушки, скористайтеся цим:

from PIL import Image

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

4

Просто оновивши це питання сучаснішою обгорткою Ця бібліотека обгортає Подушку (вилка 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()

Наведемо більше прикладів у вищенаведеному посиланні.


3
resize_contain виглядає насправді досить корисно!
Anytoe

4

Я намагався змінити розмір деяких зображень для слайд-шоу, тому я хотів не один розмір, а максимальну ширину та максимальну висоту (розмір відеокадру).
І завжди була можливість портретного відео ...
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)

3

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

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, який використовує цю функцію для виконання розміру пакетного зображення.


3

Оновили відповідь вище "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')

Працюй як шарм! Тож дякую!
Денис Савенко

2

Мій потворний приклад.

Функція отримати файл типу: "pic [0-9a-z]. [Розширення]", змініть їх розмір до 120x120, перемістить розділ до центру та збережіть у "ico [0-9a-z]. [Розширення]", працює з портретом і пейзаж:

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

2

Я змінив розмір зображення таким чином, і він працює дуже добре

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)

2

Я також додам версію розміру, яка зберігає фіксовану пропорцію. У цьому випадку він регулює висоту відповідно до ширини нового зображення, виходячи з початкового співвідношення сторін, 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");

І, це робиться. Я намагався документувати це якомога більше, щоб це було зрозуміло.

Я сподіваюся, що це може бути корисним комусь там!


0

Відкрийте файл зображення

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

тощо


Що таке display()і де він знаходиться?
Антоній

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

-1

Ви можете змінити розмір зображення за кодом нижче:

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')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.