¿Cómo cambio el tamaño de una imagen usando PIL y mantengo su relación de aspecto?


437

¿Hay una manera obvia de hacer esto que me estoy perdiendo? Solo intento hacer miniaturas.


99
Como esta pregunta es bastante antigua pero útil, y se prefiere la almohada, para un tutorial basado en almohadas, eche un vistazo a esto: pillow.readthedocs.org/en/latest/handbook/…
Wtower

3
He creado una pequeña biblioteca para cambiar el tamaño de las imágenes, puede ser de ayuda: github.com/charlesthk/python-resize-image
Charlesthk

El último lanzamiento de PIL fue en 2006. Hasta donde yo sé, el paquete de almohadas es el reemplazo. El último lanzamiento de Pillow fue el 2 de abril de 2020.
David Medinets

Respuestas:


477

Definir un tamaño máximo. Luego, calcule una relación de cambio de tamaño tomando min(maxwidth/width, maxheight/height).

El tamaño adecuado es oldsize*ratio.

Por supuesto, también hay un método de biblioteca para hacer esto: el método Image.thumbnail.
A continuación se muestra un ejemplo (editado) de la documentación 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

44
Como dice, el ejemplo fue de la documentación de pil, y ese ejemplo (todavía) no usa la bandera antialias. Sin embargo, dado que es probablemente lo que la mayoría de la gente querría, lo agregué.
gnud

3
PIL establece la altura de la nueva imagen al tamaño dado (128 aquí) y calcula el ancho para mantener la relación de aspecto. ¿Hay alguna manera de arreglar el ancho en lugar de la altura? tal vez haré esto en una pregunta separada.
eugene

66
@ Eugene: ¿intentas algo así s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (eso es para la división de coma flotante :))
gnud

48
Tenga en cuenta que ANTIALIASya no se prefiere para los usuarios de la popular horquilla Pillow de PIL. pillow.readthedocs.org/en/3.0.x/releasenotes/…
Joshmaker

8
La documentación de Python 3 para PIL dice que thumbnailsolo funciona si la imagen resultante es más pequeña que la original. Por eso supongo que usar resizees la mejor manera.
Entonces S

254

Este script redimensionará una imagen (somepic.jpg) usando PIL (Python Imaging Library) a un ancho de 300 píxeles y una altura proporcional al nuevo ancho. Para ello, determina qué porcentaje de 300 píxeles es del ancho original (tamaño img. [0]) y luego multiplica la altura original (tamaño img. [1]) por ese porcentaje. Cambie "ancho de base" a cualquier otro número para cambiar el ancho predeterminado de sus imágenes.

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
Si está utilizando esta secuencia de comandos en Zope como método externo, necesitará la línea "from PIL import Image" para evitar conflictos de espacio de nombres con la "Imagen" de Zope.
tomvon

Este código me da un archivo de salida de 0 bytes sompic.jpg. ¿Por qué pasó esto? Estoy usando Python 3.x
imrek

- Actualización: lo mismo ocurre en Python 2.7.
imrek

Puede que me haya dado cuenta. Si está guardando un .jpeg, use img.save('sompic.jpg', 'JPEG').
imrek

55
nit: no hay ninguna PIL.Image.ANTIALIASopción para resize, debería serlo PIL.Image.LANCZOS, aunque ambos tienen 1valor, vea pillow.readthedocs.io/en/3.1.x/reference/…
Fred Wu

66

También recomiendo usar el método de miniaturas de PIL, porque elimina todas las molestias de la relación.

Sin embargo, una pista importante: reemplazar

im.thumbnail(size)

con

im.thumbnail(size,Image.ANTIALIAS)

de forma predeterminada, PIL utiliza el filtro Image.NEAREST para cambiar el tamaño, lo que resulta en un buen rendimiento, pero de baja calidad.


2
Con esto, solo puede disminuir el tamaño de una imagen. No es posible aumentar el tamaño con Image.thumbnail.
burny 01 de

45

Con base en @tomvon, terminé de usar lo siguiente (elija su caso):

a) Cambiar el tamaño de la altura ( conozco el nuevo ancho, así que necesito la nueva altura )

new_width  = 680
new_height = new_width * height / width 

b) Cambiar el tamaño del ancho ( conozco la nueva altura, así que necesito el nuevo ancho )

new_height = 680
new_width  = new_height * width / height

Entonces solo:

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

66
Sus variables están todas mezcladas. Su publicación dice cambiar el tamaño del ancho y luego cambia el tamaño de la altura. ¿Y en la resizellamada, está utilizando new_widthtanto para altura como para ancho?
Zachafer

Sugirió una solución para eso @Zachafer
Mo Beigi

1
Mejor conviértalos a entero
Black Thunder

18

PIL ya tiene la opción de recortar una imagen

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

20
Eso solo recorta la imagen, no mantiene la relación de aspecto.
Radu

2
Esto no responde a la pregunta de ninguna manera.
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
Esto no mantiene la relación de aspecto de la imagen de origen. Obliga a la imagen a 200x300 y dará como resultado una imagen comprimida o estirada.
burny 01 de

Esto no responde a la pregunta de ninguna manera.
AMC

13

Si está tratando de mantener la misma relación de aspecto, ¿no cambiaría el tamaño en algún porcentaje del tamaño original?

Por ejemplo, la mitad del tamaño original

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

13
Podría ser que las imágenes fueran de diferentes tamaños y se requiriera que el resultado del cambio de tamaño fuera de tamaño 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))

Yo uso esta biblioteca:

pip install python-resize-image

7

Si no desea / no necesita abrir la imagen con Pillow, use esto:

from PIL import Image

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

4

Simplemente actualizando esta pregunta con un contenedor más moderno Esta biblioteca envuelve Pillow (un tenedor de PIL) https://pypi.org/project/python-resize-image/

Permitiéndole hacer algo como esto: -

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

Montones de más ejemplos en el enlace de arriba.


3
¡resize_contain parece realmente bastante útil!
Anytoe

4

Estaba tratando de cambiar el tamaño de algunas imágenes para un video de presentación de diapositivas y debido a eso, no solo quería una dimensión máxima, sino un ancho máximo y una altura máxima (el tamaño del cuadro de video).
Y siempre existía la posibilidad de un video de retrato ...
ElImage.thumbnail método era prometedor, pero no podía hacer que fuera una imagen más pequeña.

Entonces, después de que no pude encontrar una manera obvia de hacer eso aquí (o en otros lugares), escribí esta función y la puse aquí para las siguientes:

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

Un método simple para mantener relaciones restringidas y pasar un ancho / alto máximo. No es el más bonito, pero hace el trabajo y es 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

Aquí hay un script de Python que usa esta función para ejecutar el cambio de tamaño de la imagen por lotes.


3

Han actualizado la respuesta anterior 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')

¡Trabajar como un encanto! ¡Así que gracias!
Denis Savenko

2

Mi feo ejemplo.

Función obtener archivo como: "pic [0-9a-z]. [Extensión]", cambiar su tamaño a 120x120, mueve la sección al centro y guardar en "ico [0-9a-z]. [Extensión]", funciona con retrato y paisaje:

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

Cambié el tamaño de la imagen de tal manera y está funcionando muy bien

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

También agregaré una versión del cambio de tamaño que mantiene la relación de aspecto fija. En este caso, ajustará la altura para que coincida con el ancho de la nueva imagen, en función de la relación de aspecto inicial, asp_rat , que es flotante (!). Pero, para ajustar el ancho a la altura, solo necesita comentar una línea y descomentar la otra en el bucle else . Verás dónde.

No necesita los puntos y comas (;), los guardo solo para recordarme la sintaxis de los idiomas que uso con más frecuencia.

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

Y ya está hecho. Traté de documentarlo tanto como pude, por lo que está claro.

¡Espero que pueda ser útil para alguien por ahí!


0

Abre tu archivo de imagen

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

Utilice el método PIL Image.resize (tamaño, resample = 0) , donde sustituye (ancho, alto) de su imagen por el tamaño de 2 tuplas.

Esto mostrará su imagen en tamaño original:

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

Esto mostrará su imagen a la mitad del tamaño:

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

Esto mostrará su imagen a 1/3 del tamaño:

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

Esto mostrará su imagen a 1/4 del tamaño:

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

etcétera etcétera


¿Qué es display()y dónde está ubicado?
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

Puede cambiar el tamaño de la imagen por el siguiente código:

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')
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.