Làm cách nào để thay đổi kích thước hình ảnh bằng PIL và duy trì tỷ lệ khung hình của nó?


435

Có một cách rõ ràng để làm điều này mà tôi đang thiếu? Tôi chỉ đang cố gắng để tạo hình thu nhỏ.


9
Kể từ khi câu hỏi này là khá cũ nhưng hữu ích, và gối được khá ưa thích, cho một cái gối dựa trên hướng dẫn hãy xem này: pillow.readthedocs.org/en/latest/handbook/...
Wtower

3
Tôi đã tạo ra một thư viện nhỏ cho thay đổi kích thước hình ảnh, nó có thể là của bất kỳ sự giúp đỡ: github.com/charlesthk/python-resize-image
Charlesthk

Bản phát hành cuối cùng của PIL là vào năm 2006. Gói gối là sự thay thế theo như tôi biết. Phát hành mới nhất của Gối là ngày 2 tháng 4 năm 2020.
David Medinets

Câu trả lời:


477

Xác định kích thước tối đa. Sau đó, tính tỷ lệ thay đổi kích thước bằng cách lấy min(maxwidth/width, maxheight/height).

Kích thước phù hợp là oldsize*ratio.

Tất nhiên cũng có một phương thức thư viện để làm điều này: phương thức Image.thumbnail.
Dưới đây là một ví dụ (được chỉnh sửa) từ tài liệu 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
Giống như nó nói, ví dụ này là từ tài liệu pil, và ví dụ đó (vẫn) không sử dụng cờ antialias. Vì có lẽ đó là điều mà hầu hết mọi người sẽ muốn, mặc dù vậy, tôi đã thêm nó.
gnud

3
PIL đặt chiều cao của hình ảnh mới theo kích thước đã cho (128 ở đây) và tính chiều rộng để giữ tỷ lệ khung hình. Có cách nào để sửa chiều rộng thay vì chiều cao không? có lẽ tôi sẽ hỏi điều này trong câu hỏi riêng biệt.
eugene

6
@Eugene: thử cái gì như thế s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)nào? (đó là để phân chia điểm nổi :)
gnud

48
Lưu ý rằng ANTIALIASkhông còn được ưa thích cho người dùng PIL Gối phổ biến của PIL. gối.readthedocs.org/en/3.0.x/releasenotes/ từ
Joshmaker

8
Tài liệu Python 3 cho PIL nói rằng thumbnailchỉ hoạt động nếu hình ảnh thu được nhỏ hơn ảnh gốc. Do đó tôi sẽ đoán rằng sử dụng resizelà cách tốt hơn.
Vì vậy,

253

Tập lệnh này sẽ thay đổi kích thước hình ảnh (somepic.jpg) bằng PIL (Thư viện hình ảnh Python) thành chiều rộng 300 pixel và chiều cao tỷ lệ thuận với chiều rộng mới. Nó thực hiện điều này bằng cách xác định phần trăm 300 pixel có chiều rộng ban đầu (img.size [0]) và sau đó nhân chiều cao ban đầu (img.size [1]) với tỷ lệ phần trăm đó. Thay đổi "basewidth" thành bất kỳ số nào khác để thay đổi độ rộng mặc định của hình ảnh của bạn.

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
Nếu bạn đang sử dụng tập lệnh này trong Zope làm phương thức Bên ngoài, bạn sẽ cần dòng "từ hình ảnh nhập PIL" để tránh xung đột không gian tên với "Hình ảnh" của Zope.
tomvon

Mã này cho tôi một tệp đầu ra 0 byte sompic.jpg. Lý do tại sao điều này xảy ra? Tôi đang sử dụng Python 3.x
imrek

- Cập nhật: điều tương tự xảy ra trong Python 2.7.
imrek

Tôi có thể đã tìm ra. Nếu bạn đang tiết kiệm a .jpeg, hãy sử dụng img.save('sompic.jpg', 'JPEG').
imrek

5
nit: thực sự không có PIL.Image.ANTIALIAStùy chọn nào , mặc dù cả hai đều có giá trị, xem gối.readthedocs.io/en/3.1.x/reference/ KẻresizePIL.Image.LANCZOS1
Fred Wu

66

Tôi cũng khuyên bạn nên sử dụng phương pháp thu nhỏ của PIL, bởi vì nó loại bỏ tất cả các rắc rối về tỷ lệ khỏi bạn.

Một gợi ý quan trọng, mặc dù: Thay thế

im.thumbnail(size)

với

im.thumbnail(size,Image.ANTIALIAS)

theo mặc định, PIL sử dụng bộ lọc Image.NEAREST để thay đổi kích thước dẫn đến hiệu suất tốt, nhưng chất lượng kém.


2
Với điều này, bạn chỉ có thể giảm kích thước của một hình ảnh. Không thể tăng kích thước với Image.thumbnail.
cháy bỏng

45

Dựa trên @tomvon, tôi đã hoàn thành việc sử dụng như sau (chọn trường hợp của bạn):

a) Thay đổi kích thước chiều cao ( Tôi biết chiều rộng mới, vì vậy tôi cần chiều cao mới )

new_width  = 680
new_height = new_width * height / width 

b) Thay đổi kích thước chiều rộng ( Tôi biết chiều cao mới, vì vậy tôi cần chiều rộng mới )

new_height = 680
new_width  = new_height * width / height

Sau đó, chỉ cần:

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

6
Các biến của bạn được trộn lẫn. Bài viết của bạn cho biết thay đổi kích thước chiều rộng, và sau đó thay đổi kích thước chiều cao. Và trong resizecuộc gọi, bạn đang sử dụng new_widthcho cả chiều cao và chiều rộng?
Zachafer

Đề xuất cách khắc phục cho điều đó @Zachafer
Mo Beigi

1
Tốt hơn nên chuyển đổi chúng thành số nguyên
Black Thunder

18

PIL đã có tùy chọn cắt ảnh

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

20
Điều đó chỉ cắt hình ảnh, nó không duy trì tỷ lệ khung hình.
Radu

2
Điều này không trả lời câu hỏi theo bất kỳ cách nào.
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
Điều này không giữ tỷ lệ khung hình của hình ảnh nguồn. Nó buộc hình ảnh thành 200x300 và sẽ dẫn đến hình ảnh bị nén hoặc kéo dài.
cháy bỏng

Điều này không trả lời câu hỏi theo bất kỳ cách nào.
AMC

12

Nếu bạn đang cố gắng duy trì tỷ lệ khung hình tương tự, thì bạn sẽ không thay đổi kích thước theo một số phần trăm kích thước ban đầu chứ?

Ví dụ: một nửa kích thước ban đầu

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

13
Có thể là các hình ảnh có kích thước khác nhau và kết quả thay đổi kích thước được yêu cầu phải có kích thước đồng đều
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))

Tôi sử dụng thư viện này:

pip install python-resize-image

7

Nếu bạn không muốn / không có nhu cầu mở hình ảnh bằng Gối, hãy sử dụng:

from PIL import Image

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

4

Chỉ cần cập nhật câu hỏi này với trình bao bọc hiện đại hơn Thư viện này kết hợp Gối (một nhánh của PIL) https://pypi.org/project/python-resize-image/

Cho phép bạn làm một cái gì đó như thế này: -

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 nhiều ví dụ trong liên kết trên.


3
resize_contain trông thực sự khá hữu ích!
Anytoe

4

Tôi đã cố gắng thay đổi kích thước một số hình ảnh cho video trình chiếu và vì thế, tôi muốn không chỉ một chiều tối đa, mà cả chiều rộng tối đa chiều cao tối đa (kích thước của khung video).
Và luôn luôn có được khả năng xảy ra một video chân dung ...
CácImage.thumbnail phương pháp đã được hứa hẹn, nhưng tôi không thể làm cho nó cao cấp hình ảnh nhỏ hơn.

Vì vậy, sau khi tôi không thể tìm thấy một cách rõ ràng để làm điều đó ở đây (hoặc tại một số nơi khác), tôi đã viết chức năng này và đặt nó ở đây cho những người đến:

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

Một phương pháp đơn giản để giữ tỷ lệ bị ràng buộc và vượt qua chiều rộng / chiều cao tối đa. Không phải là đẹp nhất nhưng hoàn thành công việc và rất dễ hiểu:

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

Đây là một kịch bản python sử dụng chức năng này để chạy thay đổi kích thước hình ảnh hàng loạt.


3

Đã cập nhật câu trả lời ở trên bởi "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')

Làm việc như một cơ duyên! Cảm ơn!
Denis Savenko

2

Ví dụ xấu xí của tôi.

Chức năng nhận tệp như: "pic [0-9a-z]. [Tiện ích mở rộng]", thay đổi kích thước của chúng thành 120x120, di chuyển phần vào giữa và lưu vào "ico [0-9a-z]. [Tiện ích mở rộng]", hoạt động với chân dung và phong cảnh:

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

Tôi lấy lại hình ảnh theo cách như vậy và nó hoạt động rất tốt

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

Tôi cũng sẽ thêm một phiên bản thay đổi kích thước để giữ tỷ lệ khung hình cố định. Trong trường hợp này, nó sẽ điều chỉnh chiều cao cho phù hợp với chiều rộng của hình ảnh mới, dựa trên tỷ lệ khung hình ban đầu, asp_rat , là float (!). Nhưng, để điều chỉnh chiều rộng theo chiều cao, thay vào đó, bạn chỉ cần bình luận một dòng và bỏ ghi chú khác trong vòng lặp khác . Bạn sẽ thấy, ở đâu.

Bạn không cần dấu chấm phẩy (;), tôi giữ chúng chỉ để nhắc nhở bản thân về cú pháp ngôn ngữ tôi sử dụng thường xuyên hơn.

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

Và, nó đã được thực hiện. Tôi đã cố gắng ghi lại nó nhiều nhất có thể, vì vậy nó là rõ ràng.

Tôi hy vọng nó có thể hữu ích cho ai đó ngoài kia!


0

Mở tập tin hình ảnh của bạn

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

Sử dụng phương pháp PIL Image.resize (size, resample = 0) , trong đó bạn thay thế (chiều rộng, chiều cao) của hình ảnh cho kích thước 2-tuple.

Điều này sẽ hiển thị hình ảnh của bạn ở kích thước ban đầu:

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

Điều này sẽ hiển thị hình ảnh của bạn ở kích thước 1/2:

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

Điều này sẽ hiển thị hình ảnh của bạn ở kích thước 1/3:

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

Điều này sẽ hiển thị hình ảnh của bạn ở kích thước 1/4:

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

Vân vân


Nó nằm display()ở đâu và nằm ở đâu?
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

Bạn có thể thay đổi kích thước hình ảnh bằng mã bên dưới:

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')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.