Bagaimana cara mengubah ukuran gambar menggunakan PIL dan mempertahankan rasio aspeknya?


437

Apakah ada cara yang jelas untuk melakukan ini yang saya lewatkan? Saya hanya mencoba membuat thumbnail.


9
Karena pertanyaan ini sudah cukup lama tetapi bermanfaat, dan bantal lebih disukai, untuk tutorial berbasis bantal lihat ini: pillow.readthedocs.org/en/latest/handbook/…
Wtower

3
Saya telah membuat perpustakaan kecil untuk mengubah ukuran gambar, dapat membantu: github.com/charlesthk/python-resize-image
Charlesthk

Rilis terakhir PIL adalah pada tahun 2006. Paket bantal adalah pengganti sejauh yang saya tahu. Rilis terbaru Pillow adalah 2 April 2020.
David Medinets

Jawaban:


477

Tentukan ukuran maksimum. Kemudian, hitung rasio ukuran dengan mengambil min(maxwidth/width, maxheight/height).

Ukuran yang tepat adalah oldsize*ratio.

Tentu saja ada juga metode perpustakaan untuk melakukan ini: metode Image.thumbnail.
Di bawah ini adalah contoh (diedit) dari dokumentasi 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
Seperti katanya, contohnya adalah dari dokumentasi pil, dan contoh itu (masih) tidak menggunakan bendera antialias. Karena mungkin itulah yang diinginkan kebanyakan orang, saya menambahkannya.
gnud

3
PIL mengatur ketinggian gambar baru ke ukuran yang diberikan (128 di sini) dan menghitung lebar untuk menjaga rasio aspek. Apakah ada cara untuk memperbaiki lebar daripada tinggi? mungkin saya akan menanyakan ini dalam pertanyaan terpisah.
eugene

6
@Eugene: coba sesuatu seperti s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (Itu untuk pembagian floating point :)
gnud

48
Perhatikan bahwa ANTIALIAStidak lagi disukai bagi pengguna garpu Bantal PIL yang populer. pillow.readthedocs.org/en/3.0.x/releasenotes/…
Joshmaker

8
Dokumentasi Python 3 untuk PIL mengatakan bahwa thumbnailhanya berfungsi jika gambar yang dihasilkan lebih kecil dari yang asli. Karena itu saya akan menebak bahwa menggunakan resizeadalah cara yang lebih baik.
Jadi S

253

Script ini akan mengubah ukuran gambar (somepic.jpg) menggunakan PIL (Python Imaging Library) dengan lebar 300 piksel dan tinggi sebanding dengan lebar baru. Ini dilakukan dengan menentukan berapa persentase 300 piksel dari lebar asli (img.size [0]) dan kemudian mengalikan tinggi asli (img.size [1]) dengan persentase itu. Ubah "basewidth" ke nomor lain untuk mengubah lebar default gambar Anda.

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
Jika Anda menggunakan skrip ini di Zope sebagai metode eksternal, Anda akan memerlukan baris "dari PIL import Image" untuk menghindari perselisihan namespace dengan "Image" Zope.
Tomovon

Kode ini memberi saya file output 0 byte sompic.jpg. Mengapa ini terjadi? Saya menggunakan Python 3.x
imrek

- Pembaruan: hal yang sama terjadi pada Python 2.7.
imrek

Saya mungkin sudah tahu. Jika Anda menyimpan .jpeg, gunakan img.save('sompic.jpg', 'JPEG').
imrek

5
nit: tidak ada PIL.Image.ANTIALIASpilihan untuk resize, sebenarnya PIL.Image.LANCZOS, walaupun keduanya 1bernilai, lihat pillow.readthedocs.io/en/3.1.x/reference/…
Fred Wu

66

Saya juga merekomendasikan menggunakan metode thumbnail PIL, karena menghapus semua kerepotan rasio dari Anda.

Namun ada satu petunjuk penting: Ganti

im.thumbnail(size)

dengan

im.thumbnail(size,Image.ANTIALIAS)

secara default, PIL menggunakan filter Image.NEAREST untuk mengubah ukuran yang menghasilkan kinerja yang baik, tetapi kualitasnya buruk.


2
Dengan ini, Anda hanya dapat mengurangi ukuran gambar. Tidak dimungkinkan untuk menambah ukuran Image.thumbnail.
burny

45

Berbasis di @tomvon, saya selesai menggunakan yang berikut (pilih kasing Anda):

a) Mengubah ukuran ketinggian ( Saya tahu lebar baru, jadi saya perlu ketinggian baru )

new_width  = 680
new_height = new_width * height / width 

b) Mengubah ukuran lebar ( Saya tahu ketinggian baru, jadi saya perlu lebar baru )

new_height = 680
new_width  = new_height * width / height

Maka cukup:

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

6
Semua variabel Anda tercampur. Posting Anda mengatakan ukuran lebar, dan kemudian mengubah ukuran ketinggian. Dan dalam resizepanggilan, Anda menggunakan new_widthuntuk tinggi dan lebar?
Zachafer

Menyarankan perbaikan untuk @Zachafer
Mo Beigi

1
Lebih baik mengonversikannya ke integer
Black Thunder

18

PIL sudah memiliki opsi untuk memotong gambar

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

20
Itu hanya memotong gambar, itu tidak mempertahankan aspek rasio.
Radu

2
Ini tidak menjawab pertanyaan dengan cara apa pun.
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
Ini tidak menjaga rasio aspek gambar sumber. Ini memaksa gambar ke 200x300 dan akan menghasilkan gambar yang diperas atau diregangkan.
burny

Ini tidak menjawab pertanyaan dengan cara apa pun.
AMC

13

Jika Anda mencoba mempertahankan rasio aspek yang sama, bukankah Anda akan mengubah ukuran berdasarkan persentase dari ukuran aslinya?

Misalnya, setengah dari ukuran aslinya

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

13
Bisa jadi gambar-gambar itu memiliki ukuran yang berbeda-beda dan hasil pengubahan ukurannya harus berukuran seragam
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))

Saya menggunakan perpustakaan ini:

pip install python-resize-image

7

Jika Anda tidak ingin / tidak perlu membuka gambar dengan Bantal, gunakan ini:

from PIL import Image

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

4

Hanya memperbarui pertanyaan ini dengan pembungkus yang lebih modern Perpustakaan ini membungkus Bantal (garpu PIL) https://pypi.org/project/python-resize-image/

Mengizinkan Anda melakukan sesuatu seperti ini: -

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

Tumpukan lebih banyak contoh di tautan di atas.


3
resize_contain terlihat sangat berguna!
Anytoe

4

Saya mencoba mengubah ukuran beberapa gambar untuk video tayangan slide dan karena itu, saya ingin tidak hanya satu dimensi maks, tetapi lebar maks dan tinggi maks (ukuran bingkai video).
Dan selalu ada kemungkinan video potret ...
TheImage.thumbnail Metode menjanjikan, tapi aku tidak bisa membuatnya kelas atas gambar yang lebih kecil.

Jadi setelah saya tidak dapat menemukan cara yang jelas untuk melakukan itu di sini (atau di beberapa tempat lain), saya menulis fungsi ini dan meletakkannya di sini untuk yang akan datang:

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

Metode sederhana untuk menjaga rasio terbatas dan memberikan lebar / tinggi maks. Bukan yang tercantik tetapi menyelesaikan pekerjaan dan mudah dimengerti:

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

Berikut ini adalah skrip python yang menggunakan fungsi ini untuk menjalankan mengubah ukuran gambar batch.


3

Telah memperbarui jawaban di atas oleh "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')

Bekerja seperti pesona! Jadi terima kasih
Denis Savenko

2

Contoh jelek saya.

Fungsi mendapatkan file seperti: "pic [0-9a-z]. [Ekstensi]", ubah ukurannya menjadi 120x120, pindahkan bagian ke tengah dan simpan ke "ico [0-9a-z]. [Ekstensi]", bekerja dengan potret dan lansekap:

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

Saya mengganti gambarnya sedemikian rupa dan itu bekerja dengan sangat baik

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

Saya juga akan menambahkan versi ukuran yang menjaga rasio aspek tetap. Dalam hal ini, itu akan menyesuaikan ketinggian agar sesuai dengan lebar gambar baru, berdasarkan rasio aspek awal, asp_rat , yang float (!). Tetapi, untuk menyesuaikan lebar dengan tinggi, Anda hanya perlu mengomentari satu baris dan menghapus komentar yang lain di loop lain . Anda akan lihat, di mana.

Anda tidak memerlukan titik koma (;), saya menyimpannya hanya untuk mengingatkan diri saya pada sintaks bahasa yang saya gunakan lebih sering.

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

Dan, itu selesai. Saya mencoba mendokumentasikannya sebanyak mungkin, jadi jelas.

Saya harap ini bisa membantu seseorang di luar sana!


0

Buka file gambar Anda

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

Gunakan metode PIL Image.resize (size, resample = 0) , di mana Anda mengganti (lebar, tinggi) gambar Anda dengan ukuran 2-tuple.

Ini akan menampilkan gambar Anda pada ukuran asli:

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

Ini akan menampilkan gambar Anda dengan ukuran 1/2:

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

Ini akan menampilkan gambar Anda dengan ukuran 1/3:

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

Ini akan menampilkan gambar Anda dengan ukuran 1/4:

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

dll


Apa itu display()dan di mana lokasinya?
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))

-1

Anda dapat mengubah ukuran gambar dengan kode di bawah ini:

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')
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.