Como redimensiono uma imagem usando PIL e mantenho sua proporção?

435

Existe uma maneira óbvia de fazer isso que estou perdendo? Eu só estou tentando fazer miniaturas.

sábado
fonte
9
Como essa pergunta é bastante antiga, mas útil, e o travesseiro é bastante preferido, para um tutorial baseado em travesseiros, dê uma olhada no seguinte: pillow.readthedocs.org/en/latest/handbook/…
Wtower
3
Eu criei uma pequena biblioteca para redimensionar imagens, pode ser de alguma ajuda: github.com/charlesthk/python-resize-image
Charlesthk
A última versão do PIL foi em 2006. A embalagem de travesseiros é a substituição, tanto quanto eu sei. O último lançamento do Pillow foi em 2 de abril de 2020.
David Medinets

Respostas:

477

Defina um tamanho máximo. Em seguida, calcule uma taxa de redimensionamento usando min(maxwidth/width, maxheight/height).

O tamanho adequado é oldsize*ratio.

Obviamente, também existe um método de biblioteca para fazer isso: o método Image.thumbnail.
Abaixo está um exemplo (editado) da documentação do 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
fonte
4
Como se costuma dizer, o exemplo foi da documentação do pil e esse exemplo (ainda) não usa a bandeira antialias. Como provavelmente é o que a maioria das pessoas gostaria, eu adicionei.
precisa saber é o seguinte
3
PIL define a altura da nova imagem para o tamanho fornecido (128 aqui) e calcula a largura para manter a proporção. Existe uma maneira de corrigir a largura em vez da altura? talvez eu faça isso em outra pergunta.
Eugene
6
@ Eugene: tente algo como s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (isso é para divisão de ponto flutuante :)
gnud 13/12/12
48
Observe que ANTIALIASnão é mais preferido para os usuários do popular travesseiro de forquilha da PIL. pillow.readthedocs.org/pt/3.0.x/releasenotes/…
Joshmaker 23/12/15
8
A documentação do Python 3 para PIL diz que thumbnailsó funciona se a imagem resultante for menor que a original. Por isso, acho que usar resizeé o melhor caminho.
S
253

Esse script redimensionará uma imagem (somepic.jpg) usando PIL (Python Imaging Library) para uma largura de 300 pixels e uma altura proporcional à nova largura. Ele faz isso determinando qual a porcentagem de 300 pixels da largura original (img.size [0]) e multiplicando a altura original (img.size [1]) por essa porcentagem. Altere "largura da base" para qualquer outro número para alterar a largura padrão das suas imagens.

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
fonte
2
Se você estiver usando este script no Zope como um método externo, precisará da linha "from PIL import Image" para evitar conflitos de namespace com a "Image" do Zope.
tomvon
Esse código me fornece um arquivo de saída de 0 byte sompic.jpg. Por que isso acontece? Estou usando o Python 3.x
imrek 26/03
- Atualização: o mesmo acontece no Python 2.7.
imrek
Eu posso ter descoberto. Se você estiver salvando um .jpeg, use img.save('sompic.jpg', 'JPEG').
imrek
5
nit: não há PIL.Image.ANTIALIASopção para resize, realmente deve ser PIL.Image.LANCZOS, embora sejam ambos 1em valor, ver pillow.readthedocs.io/en/3.1.x/reference/...
Fred Wu
66

Também recomendo usar o método de miniatura do PIL, porque ele remove todos os aborrecimentos da proporção.

Uma dica importante, no entanto: Substitua

im.thumbnail(size)

com

im.thumbnail(size,Image.ANTIALIAS)

por padrão, o PIL usa o filtro Image.NEAREST para redimensionar o que resulta em bom desempenho, mas baixa qualidade.

Franz
fonte
2
Com isso, você só pode diminuir o tamanho de uma imagem. Não é possível aumentar o tamanho com Image.thumbnail.
burny
45

Baseado em @tomvon, terminei de usar o seguinte (escolha seu caso):

a) Redimensionando a altura ( conheço a nova largura, preciso da nova altura )

new_width  = 680
new_height = new_width * height / width 

b) Redimensionando a largura ( eu conheço a nova altura, então preciso da nova largura )

new_height = 680
new_width  = new_height * width / height

Então apenas:

img = img.resize((new_width, new_height), Image.ANTIALIAS)
muZk
fonte
6
Suas variáveis ​​estão todas misturadas. Sua postagem diz redimensionar a largura e redimensiona a altura. E na resizechamada, você está usando o new_widthpara altura e largura?
Zachafer
Sugeriu uma correção para isso @Zachafer
Mo Beigi
1
Melhor convertê-los em número inteiro
Black Thunder
18

O PIL já tem a opção de cortar uma imagem

img = ImageOps.fit(img, size, Image.ANTIALIAS)
Cícero Verneck Corrêa
fonte
20
Isso apenas corta a imagem, não mantém a proporção.
Radu
2
Isso não responde à pergunta de forma alguma.
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
fonte
8
Isso não mantém a proporção da imagem de origem. Força a imagem para 200x300 e resultará em uma imagem comprimida ou esticada.
burny
Isso não responde à pergunta de forma alguma.
AMC
12

Se você está tentando manter a mesma proporção, não redimensiona uma porcentagem do tamanho original?

Por exemplo, metade do tamanho original

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

fonte
13
Pode ser que as imagens eram de tamanhos variados e o resultado de redimensionamento foi obrigado a ser de tamanho uniforme
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))

Eu uso esta biblioteca:

pip install python-resize-image
guettli
fonte
7

Se você não deseja / não precisa abrir uma imagem com Pillow, use o seguinte:

from PIL import Image

new_img_arr = numpy.array(Image.fromarray(img_arr).resize((new_width, new_height), Image.ANTIALIAS))
hoohoo-b
fonte
4

Atualizando esta pergunta com um invólucro mais moderno. Esta biblioteca envolve o Pillow (um garfo do PIL) https://pypi.org/project/python-resize-image/

Permitindo que você faça algo assim: -

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

Amontoa mais exemplos no link acima.

Shanness
fonte
3
resize_contain parece realmente bastante útil!
Anytoe
4

Eu estava tentando redimensionar algumas imagens para um vídeo de apresentação de slides e, por isso, queria não apenas uma dimensão máxima, mas uma largura e uma altura máximas (o tamanho do quadro do vídeo).
E sempre havia a possibilidade de um vídeo em retrato ...
O Image.thumbnailmétodo era promissor, mas não consegui torná-lo melhor para uma imagem menor.

Então, depois que não consegui encontrar uma maneira óbvia de fazer isso aqui (ou em outros lugares), escrevi esta função e a coloquei aqui para as seguintes:

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
fonte
3

Um método simples para manter relações restritas e passar uma largura / altura máximas. Não é a mais bonita, mas realiza o trabalho e é fácil de entender:

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

Aqui está um script python que usa essa função para executar o redimensionamento de imagens em lote.

AlexG
fonte
3

Atualizou a resposta acima por "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
fonte
Trabalhe como um encanto! Então obrigado!
Denis Savenko
2

Meu exemplo feio.

A função obtém um arquivo como: "pic [0-9a-z]. [Extension]", redimensiona-o para 120x120, move a seção para o centro e salva em "ico [0-9a-z]. [Extension]", trabalha com retrato e paisagem:

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:])
Nips
fonte
2

Eu redimensiono a imagem dessa maneira e está funcionando muito bem

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
fonte
2

Também adicionarei uma versão do redimensionamento que mantém a proporção fixa. Nesse caso, ele ajustará a altura para corresponder à largura da nova imagem, com base na proporção inicial, asp_rat , que é flutuante (!). Mas, para ajustar a largura à altura, basta comentar uma linha e descomentar a outra no loop else . Você verá onde.

Você não precisa de ponto e vírgula (;), guardo-os apenas para me lembrar da sintaxe dos idiomas que utilizo com mais frequência.

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

E está feito. Tentei documentá-lo o máximo possível, para que fique claro.

Espero que possa ser útil para alguém lá fora!

RockOGOlic
fonte
0

Abra seu arquivo de imagem

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

Use o método PIL Image.resize (tamanho, resample = 0) , onde você substitui (largura, altura) da sua imagem pelo tamanho 2-tupla.

Isso exibirá sua imagem no tamanho original:

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

Isso exibirá sua imagem com metade do tamanho:

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

Isso exibirá sua imagem em 1/3 do tamanho:

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

Isso exibirá sua imagem em 1/4 do tamanho:

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

etc etc

user391339
fonte
O que é display()e onde está localizado?
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
fonte
-1

Você pode redimensionar a imagem pelo código abaixo:

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
fonte