Comment redimensionner une image à l'aide de PIL et conserver son format d'image?

438

Existe-t-il un moyen évident de faire cela qui me manque? J'essaie juste de faire des miniatures.

samedi
la source
9
Étant donné que cette question est assez ancienne mais utile et que l'oreiller est plutôt préféré, pour un tutoriel basé sur l'oreiller, jetez un œil à ceci: pillow.readthedocs.org/en/latest/handbook/…
Wtower
3
J'ai créé une petite bibliothèque pour redimensionner les images, elle peut être de toute aide: github.com/charlesthk/python-resize-image
Charlesthk
La dernière version de PIL date de 2006. Le pack d'oreillers est le remplacement que je sache. La dernière version de Pillow était le 2 avril 2020.
David Medinets

Réponses:

477

Définissez une taille maximale. Ensuite, calculez un rapport de redimensionnement en prenant min(maxwidth/width, maxheight/height).

La bonne taille est oldsize*ratio.

Il y a bien sûr aussi une méthode de bibliothèque pour ce faire: la méthode Image.thumbnail.
Voici un exemple (modifié) de la documentation 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
la source
4
Comme il est dit, l'exemple provient de la documentation de pil, et cet exemple n'utilise pas (encore) l'indicateur d'antialias. Comme c'est probablement ce que la plupart des gens voudraient, je l'ai ajouté.
gnud
3
PIL définit la hauteur de la nouvelle image à la taille indiquée (128 ici) et calcule la largeur pour conserver le rapport hauteur / largeur. Existe-t-il un moyen de fixer la largeur au lieu de la hauteur? je vais peut-être poser cette question dans une question distincte.
eugene
6
@Eugene: essayez quelque chose comme s= img.size(); ratio = MAXWIDTH/s[0]; newimg = img.resize((s[0]*ratio, s[1]*ratio), Image.ANTIALIAS)? (c'est pour la division en virgule flottante cependant :)
gnud
48
Notez que ce ANTIALIASn'est plus préféré pour les utilisateurs de la populaire fourchette Pillow de PIL. pillow.readthedocs.org/en/3.0.x/releasenotes/…
Joshmaker
8
La documentation Python 3 pour PIL indique que cela thumbnailne fonctionne que si l'image résultante est plus petite que l'image d'origine. Pour cette raison, je suppose que l'utilisation resizeest la meilleure façon.
So S
254

Ce script redimensionnera une image (somepic.jpg) à l'aide de PIL (Python Imaging Library) à une largeur de 300 pixels et une hauteur proportionnelle à la nouvelle largeur. Pour ce faire, il détermine le pourcentage de 300 pixels de la largeur d'origine (img.size [0]), puis multiplie la hauteur d'origine (img.size [1]) par ce pourcentage. Remplacez «basewidth» par tout autre nombre pour modifier la largeur par défaut de vos images.

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
la source
2
Si vous utilisez ce script dans Zope comme méthode externe, vous aurez besoin de la ligne "from PIL import Image" pour éviter les conflits d'espace de noms avec "Image" de Zope.
Tomvon
Ce code me donne un fichier de sortie de 0 octet sompic.jpg. Pourquoi cela arrive-t-il? J'utilise Python 3.x
imrek
- Mise à jour: la même chose se produit dans Python 2.7.
imrek
J'ai peut-être compris. Si vous enregistrez un .jpeg, utilisez img.save('sompic.jpg', 'JPEG').
imrek
5
nit: il n'y a pas d' PIL.Image.ANTIALIASoption pour resize, devrait en fait être PIL.Image.LANCZOS, bien qu'ils soient tous les deux 1en valeur, voir pillow.readthedocs.io/en/3.1.x/reference/…
Fred Wu
66

Je recommande également d'utiliser la méthode des miniatures de PIL, car elle vous supprime tous les problèmes de rapport.

Un indice important, cependant: remplacer

im.thumbnail(size)

avec

im.thumbnail(size,Image.ANTIALIAS)

par défaut, PIL utilise le filtre Image.NEAREST pour le redimensionnement, ce qui se traduit par de bonnes performances mais une mauvaise qualité.

Franz
la source
2
Avec cela, vous ne pouvez que réduire la taille d'une image. Il n'est pas possible d'augmenter la taille avec Image.thumbnail.
burny
45

Basé à @tomvon, j'ai fini d'utiliser les éléments suivants (choisissez votre cas):

a) Hauteur de redimensionnement ( je connais la nouvelle largeur, j'ai donc besoin de la nouvelle hauteur )

new_width  = 680
new_height = new_width * height / width 

b) Largeur de redimensionnement ( je connais la nouvelle hauteur, j'ai donc besoin de la nouvelle largeur )

new_height = 680
new_width  = new_height * width / height

Alors juste:

img = img.resize((new_width, new_height), Image.ANTIALIAS)
muZk
la source
6
Vos variables sont toutes mélangées. Votre message indique le redimensionnement de la largeur, puis redimensionne la hauteur. Et dans l' resizeappel, vous utilisez à la new_widthfois la hauteur et la largeur?
Zachafer
Suggéré un correctif pour cela @Zachafer
Mo Beigi
1
Mieux les convertir en nombres entiers
Black Thunder
18

PIL a déjà la possibilité de recadrer une image

img = ImageOps.fit(img, size, Image.ANTIALIAS)
Cícero Verneck Corrêa
la source
20
Cela ne fait que recadrer l'image, il ne conserve pas de rapport hauteur / largeur.
Radu
2
Cela ne répond en rien à la question.
AMC
16
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
la source
8
Cela ne conserve pas le rapport hauteur / largeur de l'image source. Cela force l'image à 200x300 et entraînera une image comprimée ou étirée.
burny
Cela ne répond en rien à la question.
AMC
13

Si vous essayez de conserver le même rapport hauteur / largeur, ne redimensionneriez-vous pas un certain pourcentage de la taille d'origine?

Par exemple, la moitié de la taille d'origine

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

la source
13
Il se pourrait que les images soient de tailles variables et que le résultat du redimensionnement devait être de taille 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))

J'utilise cette bibliothèque:

pip install python-resize-image
guettli
la source
7

Si vous ne voulez pas / n'avez pas besoin d'ouvrir l'image avec Pillow, utilisez ceci:

from PIL import Image

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

Juste mettre à jour cette question avec un wrapper plus moderne Cette bibliothèque enveloppe Pillow (une fourchette de PIL) https://pypi.org/project/python-resize-image/

Vous permettant de faire quelque chose comme ça: -

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

Des tas d'autres exemples dans le lien ci-dessus.

Shanness
la source
3
resize_contain semble en fait assez utile!
Anytoe
4

J'essayais de redimensionner certaines images pour un diaporama vidéo et à cause de cela, je ne voulais pas seulement une dimension maximale, mais une largeur maximale et une hauteur maximale (la taille de l'image vidéo).
Et il y avait toujours la possibilité d'une vidéo portrait ...
La Image.thumbnailméthode était prometteuse, mais je ne pouvais pas la faire monter en gamme une image plus petite.

Donc, après que je n'ai pas trouvé de moyen évident de le faire ici (ou à d'autres endroits), j'ai écrit cette fonction et l'ai mise ici pour celles à venir:

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
la source
3

Une méthode simple pour garder des rapports contraints et passer une largeur / hauteur maximale. Pas le plus joli mais fait le travail et est facile à comprendre:

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

Voici un script python qui utilise cette fonction pour exécuter le redimensionnement d'image par lots.

AlexG
la source
3

Ont mis à jour la réponse ci-dessus par "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
la source
Travaillez comme un charme! Alors merci!
Denis Savenko
2

Mon vilain exemple.

La fonction récupère le fichier comme: "pic [0-9a-z]. [Extension]", les redimensionne à 120x120, déplace la section au centre et l'enregistre dans "ico [0-9a-z]. [Extension]", fonctionne avec portrait et paysage:

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
la source
2

J'ai redimensionné l'image de cette manière et ça fonctionne très 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)
Siddhartha Mukherjee
la source
2

J'ajouterai également une version du redimensionnement qui maintient le rapport d'aspect fixe. Dans ce cas, il ajustera la hauteur pour correspondre à la largeur de la nouvelle image, en fonction du rapport hauteur / largeur initial, asp_rat , qui est float (!). Mais, pour ajuster la largeur à la hauteur, à la place, il vous suffit de commenter une ligne et de décommenter l'autre dans l' autre boucle . Vous verrez où.

Vous n'avez pas besoin des points-virgules (;), je les garde juste pour me rappeler la syntaxe des langages que j'utilise le plus souvent.

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

Et c'est fait. J'ai essayé de le documenter autant que possible, donc c'est clair.

J'espère que cela pourrait être utile à quelqu'un là-bas!

RockOGOlic
la source
0

Ouvrez votre fichier image

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

Utilisez la méthode PIL Image.resize (size, resample = 0) , où vous remplacez (largeur, hauteur) de votre image par la taille 2-tuple.

Cela affichera votre image à sa taille d'origine:

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

Cela affichera votre image à la moitié de la taille:

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

Cela affichera votre image au 1/3 de la taille:

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

Cela affichera votre image à 1/4 de la taille:

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

etc

user391339
la source
Qu'est-ce que c'est display()et où est-il situé?
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
la source
-1

Vous pouvez redimensionner l'image par le code ci-dessous:

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
la source