Jak zmienić rozmiar obrazu za pomocą PIL i zachować jego proporcje?

437

Czy istnieje oczywisty sposób na zrobienie tego, za czym tęsknię? Próbuję tylko tworzyć miniatury.

sobota
źródło
9
Ponieważ to pytanie jest dość stare, ale przydatne, a poduszka jest raczej preferowana, w samouczku opartym na poduszkach spójrz na to: pillow.readthedocs.org/en/latest/handbook/…
Wtower
3
Stworzyłem małą bibliotekę do zmiany rozmiaru obrazów, może być pomocna: github.com/charlesthk/python-resize-image
Charlesthk
Ostatnie wydanie PIL miało miejsce w 2006 roku. O ile mi wiadomo, poduszka jest zamiennikiem. Najnowsza wersja Pillow miała miejsce 2 kwietnia 2020 r.
David Medinets

Odpowiedzi:

477

Określ maksymalny rozmiar. Następnie oblicz współczynnik zmiany rozmiaru, biorąc min(maxwidth/width, maxheight/height).

Właściwy rozmiar to oldsize*ratio.

Istnieje oczywiście również metoda biblioteczna: metoda Image.thumbnail.
Poniżej znajduje się (zredagowany) przykład z dokumentacji 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
gnud
źródło
4
Jak to mówi, przykład pochodzi z dokumentacji pil, a ten przykład (nadal) nie używa flagi antyialiasowej. Ponieważ prawdopodobnie tego chciałby większość ludzi, dodałem to.
gnud,
3
PIL ustawia wysokość nowego obrazu na podany rozmiar (tutaj 128) i oblicz szerokość, aby zachować proporcje. Czy istnieje sposób, aby ustalić szerokość zamiast wysokości? może zadam to w osobnym pytaniu.
eugene
6
@Eugene: spróbuj czegoś takiego s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (dotyczy to jednak podziału zmiennoprzecinkowego :)
gnud,
48
Należy pamiętać, że ANTIALIASnie jest już preferowany dla użytkowników popularnego widelca Pillow PIL. pillow.readthedocs.org/en/3.0.x/releasenotes/…
Joshmaker
8
Dokumentacja Pythona 3 dla PIL mówi, że thumbnaildziała tylko wtedy, gdy obraz wynikowy jest mniejszy niż oryginalny. Z tego powodu zgaduję, że używanie resizejest lepszym sposobem.
So S
253

Skrypt zmieni rozmiar obrazu (somepic.jpg) przy użyciu PIL (Python Imaging Library) do szerokości 300 pikseli i wysokości proporcjonalnej do nowej szerokości. Dokonuje tego poprzez określenie, jaki procent 300 pikseli ma oryginalną szerokość (img.size [0]), a następnie pomnożenie pierwotnej wysokości (img.size [1]) przez ten procent. Zmień „szerokość pasma” na dowolną inną liczbę, aby zmienić domyślną szerokość zdjęć.

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') 
tomvon
źródło
2
Jeśli używasz tego skryptu w Zope jako metody zewnętrznej, potrzebujesz linii „z obrazu importu PIL”, aby uniknąć kolizji przestrzeni nazw z „obrazem” Zope.
tomvon
Ten kod daje mi 0 bajtowy plik wyjściowy sompic.jpg. Dlaczego to się dzieje? Używam Python 3.x
imrek
- Aktualizacja: to samo dzieje się w Pythonie 2.7.
imrek
Mogłem się domyślić. Jeśli zapisujesz a .jpeg, użyj img.save('sompic.jpg', 'JPEG').
imrek
5
nit: nie ma PIL.Image.ANTIALIASopcji resize, powinien być PIL.Image.LANCZOS, chociaż oba są 1wartościowe, zobacz pillow.readthedocs.io/en/3.1.x/reference/…
Fred Wu
66

Polecam również użycie metody miniatury PIL, ponieważ usuwa ona wszystkie problemy związane z proporcjami.

Jedna ważna wskazówka: zamień

im.thumbnail(size)

z

im.thumbnail(size,Image.ANTIALIAS)

domyślnie PIL używa filtra Image.NEAREST do zmiany rozmiaru, co powoduje dobrą wydajność, ale słabą jakość.

Franz
źródło
2
Dzięki temu możesz jedynie zmniejszyć rozmiar obrazu. Nie można zwiększyć rozmiaru za pomocą Image.thumbnail.
napalony
45

Na podstawie @tomvon, skończyłem używać następujących (wybierz swoją skrzynkę):

a) Zmiana rozmiaru wysokości ( znam nową szerokość, więc potrzebuję nowej wysokości )

new_width  = 680
new_height = new_width * height / width 

b) Zmiana rozmiaru szerokości ( znam nową wysokość, więc potrzebuję nowej szerokości )

new_height = 680
new_width  = new_height * width / height

Więc po prostu:

img = img.resize((new_width, new_height), Image.ANTIALIAS)
muZk
źródło
6
Twoje zmienne są pomieszane. Twój post mówi o zmianie rozmiaru, a następnie zmianie rozmiaru. A w resizerozmowie używasz new_widthzarówno wysokości, jak i szerokości?
Zachafer,
Sugerowana naprawa tego @Zachafer
Mo Beigi
1
Lepiej przekonwertuj je na liczby całkowite
Black Thunder
18

PIL ma już opcję przycięcia obrazu

img = ImageOps.fit(img, size, Image.ANTIALIAS)
Cícero Verneck Corrêa
źródło
20
To po prostu przycina obraz, nie utrzymuje proporcji.
Radu,
2
To w żaden sposób nie odpowiada na pytanie.
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
Mohideen bin Mohammed
źródło
8
Nie zachowuje to proporcji obrazu źródłowego. Zmusza obraz do 200 x 300 i spowoduje ściśnięcie lub rozciągnięcie obrazu.
napalony
To w żaden sposób nie odpowiada na pytanie.
AMC
12

Jeśli próbujesz zachować ten sam współczynnik kształtu, to czy nie zmieniłbyś rozmiaru o pewien procent oryginalnego rozmiaru?

Na przykład połowa oryginalnego rozmiaru

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

źródło
13
Możliwe, że obrazy były różnych rozmiarów, a wynik zmiany rozmiaru musiał być jednolity
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))

Korzystam z tej biblioteki:

pip install python-resize-image
guettli
źródło
7

Jeśli nie chcesz / nie musisz otwierać obrazu w Pillow, użyj tego:

from PIL import Image

new_img_arr = numpy.array(Image.fromarray(img_arr).resize((new_width, new_height), Image.ANTIALIAS))
hoohoo-b
źródło
4

Właśnie aktualizuję to pytanie o nowocześniejsze opakowanie Ta biblioteka otacza Pillow (rozwidlenie PIL) https://pypi.org/project/python-resize-image/

Umożliwiając zrobienie czegoś takiego: -

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

Więcej przykładów w powyższym linku.

Shanness
źródło
3
resize_contain wygląda właściwie całkiem przydatny!
Anytoe
4

Próbowałem zmienić rozmiar niektórych zdjęć do pokazu slajdów i dlatego chciałem nie tylko jednego maksymalnego wymiaru, ale maksymalnej szerokości i maksymalnej wysokości (rozmiaru klatki wideo).
I zawsze istniała możliwość wideo portret ...
Image.thumbnail metoda została obiecujące, ale nie mogłem zrobić to ekskluzywny mniejszy obraz.

Więc po tym, jak nie mogłem znaleźć oczywistego sposobu, aby to zrobić tutaj (lub w niektórych innych miejscach), napisałem tę funkcję i umieściłem ją tutaj dla tych, które mają przyjść:

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)
noEmbryo
źródło
3

Prosta metoda utrzymania ograniczonych proporcji i przekroczenia maksymalnej szerokości / wysokości. Nie najładniejsza, ale wykonuje pracę i jest łatwa do zrozumienia:

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

Oto skrypt Pythona, który używa tej funkcji do uruchamiania zmiany wielkości obrazu wsadowego.

AlexG
źródło
3

Zaktualizowałem powyższą odpowiedź przez „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')
Kanish Mathew
źródło
Pracuj jak urok! Więc dziękuję!
Denis Savenko
2

Mój brzydki przykład.

Funkcja pobiera plik w rodzaju: „pic [0-9a-z]. [Rozszerzenie]”, zmienia rozmiar na 120x120, przenosi sekcję do środka i zapisuje w „ico [0-9a-z]. [Rozszerzenie]”, działa z portretem i krajobraz:

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:])
Czapki
źródło
2

Zmieniłem rozmiar obrazu w taki sposób i działa bardzo dobrze

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)
Siddhartha Mukherjee
źródło
2

Dodam również wersję zmiany rozmiaru, która utrzymuje stały współczynnik kształtu. W takim przypadku dostosuje wysokość, aby dopasować szerokość nowego obrazu, na podstawie początkowego współczynnika kształtu asp_rat , który jest zmiennoprzecinkowy (!). Aby jednak dopasować szerokość do wysokości, wystarczy skomentować jedną linię i odkomentować drugą w pętli else . Zobaczysz gdzie.

Nie potrzebujesz średników (;), zachowuję je tylko po to, aby przypomnieć sobie składnię języków, których używam częściej.

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

I gotowe. Próbowałem to udokumentować, jak tylko potrafię, więc jest jasne.

Mam nadzieję, że może się przydać komuś!

RockOGOlic
źródło
0

Otwórz plik obrazu

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

Użyj metody PIL Image.resize (size, resample = 0) , w której zamieniasz (szerokość, wysokość) obrazu na rozmiar 2-krotny.

Spowoduje to wyświetlenie obrazu w oryginalnym rozmiarze:

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

Spowoduje to wyświetlenie obrazu w rozmiarze 1/2:

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

Spowoduje to wyświetlenie obrazu w rozmiarze 1/3:

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

Spowoduje to wyświetlenie obrazu w rozmiarze 1/4:

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

itd itd

użytkownik391339
źródło
Co to jest display()i gdzie się znajduje?
Anthony
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))
hamid_reza hobab
źródło
-1

Możesz zmienić rozmiar obrazu za pomocą poniższego kodu:

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')
Smit Parmar
źródło