web-dev-qa-db-fra.com

Quel est le moyen le plus rapide de générer des vignettes d’image en Python?

Je construis une galerie de photos en Python et je veux pouvoir générer rapidement des vignettes pour les images haute résolution.

Quel est le moyen le plus rapide de générer des vignettes de haute qualité pour diverses sources d’image?

Devrais-je utiliser une bibliothèque externe comme imagemagick ou existe-t-il un moyen interne efficace de le faire?

Les dimensions des images redimensionnées seront (taille maximale):

120x120
720x720
1600x1600

La qualité est un problème, car je souhaite préserver autant de couleurs originales que possible et minimiser les artefacts de compression.

Merci.

21
ensnare

Vous voulez PIL il le fait avec facilité

from PIL import Image
sizes = [(120,120), (720,720), (1600,1600)]
files = ['a.jpg','b.jpg','c.jpg']

for image in files:
    for size in sizes:
        im = Image.open(image)
        im.thumbnail(size)
        im.save("thumbnail_%s_%s" % (image, "_".join(size)))

Si vous avez désespérément besoin de vitesse. Ensuite, enfilez-le, multi-traitez ou obtenez une autre langue.

23
Jakob Bowyer

Un peu tard pour la question (seulement un an!), Mais je vais revenir à la partie "multitraitement" de la réponse de @ JakobBowyer.

C’est un bon exemple de problème avec { parallèle embarrassant }, car le bit de code principal ne mute en aucun état externe à lui-même. Il lit simplement une entrée, effectue son calcul et enregistre le résultat.

Python est en fait assez bon pour ce genre de problèmes grâce à la fonction map fournie par multiprocessing.Pool.

from PIL import Image
from multiprocessing import Pool 

def thumbnail(image_details): 
    size, filename = image_details
    try:
        im = Image.open(filename)
        im.thumbnail(size)
        im.save("thumbnail_%s" % filename)
        return 'OK'
    except Exception as e: 
        return e 

sizes = [(120,120), (720,720), (1600,1600)]
files = ['a.jpg','b.jpg','c.jpg']

pool = Pool(number_of_cores_to_use)
results = pool.map(thumbnail, Zip(sizes, files))

Le noyau du code est exactement le même que @JakobBowyer, mais au lieu de l'exécuter en boucle dans un seul thread, nous l'avons intégré dans une fonction répartie sur plusieurs cœurs via la fonction de mappage multitraitement.

12
Audionautics

J'avais envie de m'amuser et j'ai donc fait une analyse comparative des différentes méthodes suggérées ci-dessus et de quelques idées personnelles.

J'ai rassemblé 1000 images haute résolution pour iPhone 6s 12MP, de 4032x3024 pixels chacune, et utilise un iMac à 8 cœurs.

Voici les techniques et les résultats - chacun dans sa propre section.


Méthode 1 - Image séquentielle ImageMagick

C'est un code simpliste et non optimisé. Chaque image est lue et une vignette est produite. Ensuite, il est relu et une miniature de taille différente est produite.

#!/bin/bash

start=$SECONDS
# Loop over all files
for f in image*.jpg; do
   # Loop over all sizes
   for s in 1600 720 120; do
      echo Reducing $f to ${s}x${s}
      convert "$f" -resize ${s}x${s} t-$f-$s.jpg
   done
done
echo Time: $((SECONDS-start))

Résultat: 170 secondes


Méthode 2 - ImageMagick séquentielle avec chargement unique et redimensionnement successif

C'est toujours séquentiel mais légèrement plus intelligent. Chaque image n'est lue qu'une fois et l'image chargée est ensuite redimensionnée à trois dimensions et sauvegardée à trois résolutions. L’amélioration réside dans le fait que chaque image est lue une seule fois, pas 3 fois.

#!/bin/bash

start=$SECONDS
# Loop over all files
N=1
for f in image*.jpg; do
   echo Resizing $f
   # Load once and successively scale down
   convert "$f"                              \
      -resize 1600x1600 -write t-$N-1600.jpg \
      -resize 720x720   -write t-$N-720.jpg  \
      -resize 120x120          t-$N-120.jpg
   ((N=N+1))
done
echo Time: $((SECONDS-start))

Résultat: 76 secondes


Méthode 3 - GNU Parallel + ImageMagick

Ceci s'appuie sur la méthode précédente en utilisant GNU Parallel pour traiter les images N en parallèle, où N est le nombre de cœurs de processeur de votre ordinateur.

#!/bin/bash

start=$SECONDS

doit() {
   file=$1
   index=$2
   convert "$file"                               \
      -resize 1600x1600 -write t-$index-1600.jpg \
      -resize 720x720   -write t-$index-720.jpg  \
      -resize 120x120          t-$index-120.jpg
}

# Export doit() to subshells for GNU Parallel   
export -f doit

# Use GNU Parallel to do them all in parallel
parallel doit {} {#} ::: *.jpg

echo Time: $((SECONDS-start))

Résultat: 18 secondes


Méthode 4 - GNU Parallel + vips

Ceci est identique à la méthode précédente, mais utilise vips sur la ligne de commande au lieu de ImageMagick .

#!/bin/bash

start=$SECONDS

doit() {
   file=$1
   index=$2
   r0=t-$index-1600.jpg
   r1=t-$index-720.jpg
   r2=t-$index-120.jpg
   vipsthumbnail "$file"  -s 1600 -o "$r0"
   vipsthumbnail "$r0"    -s 720  -o "$r1"
   vipsthumbnail "$r1"    -s 120  -o "$r2"
}

# Export doit() to subshells for GNU Parallel   
export -f doit

# Use GNU Parallel to do them all in parallel
parallel doit {} {#} ::: *.jpg

echo Time: $((SECONDS-start))

Résultat: 8 secondes


Méthode 5 - PIL séquentielle

Ceci est censé correspondre à la réponse de Jakob.

#!/usr/local/bin/python3

import glob
from PIL import Image

sizes = [(120,120), (720,720), (1600,1600)]
files = glob.glob('image*.jpg')

N=0
for image in files:
    for size in sizes:
      im=Image.open(image)
      im.thumbnail(size)
      im.save("t-%d-%s.jpg" % (N,size[0]))
    N=N+1

Résultat: 38 secondes


Méthode 6 - PIL séquentiel à chargement unique et redimensionnement successif

Ceci est conçu comme une amélioration de la réponse de Jakob, dans laquelle l'image est chargée une seule fois, puis redimensionnée trois fois au lieu d'être rechargée à chaque fois pour produire chaque nouvelle résolution.

#!/usr/local/bin/python3

import glob
from PIL import Image

sizes = [(120,120), (720,720), (1600,1600)]
files = glob.glob('image*.jpg')

N=0
for image in files:
   # Load just once, then successively scale down
   im=Image.open(image)
   im.thumbnail((1600,1600))
   im.save("t-%d-1600.jpg" % (N))
   im.thumbnail((720,720))
   im.save("t-%d-720.jpg"  % (N))
   im.thumbnail((120,120))
   im.save("t-%d-120.jpg"  % (N))
   N=N+1

Résultat: 27 secondes


Méthode 7 - PIL parallèle

Ceci est destiné à correspondre à la réponse d'Audionautics, dans la mesure où elle utilise le multitraitement de Python. Cela évite également de recharger l'image pour chaque taille de vignette.

#!/usr/local/bin/python3

import glob
from PIL import Image
from multiprocessing import Pool 

def thumbnail(params): 
    filename, N = params
    try:
        # Load just once, then successively scale down
        im=Image.open(filename)
        im.thumbnail((1600,1600))
        im.save("t-%d-1600.jpg" % (N))
        im.thumbnail((720,720))
        im.save("t-%d-720.jpg"  % (N))
        im.thumbnail((120,120))
        im.save("t-%d-120.jpg"  % (N))
        return 'OK'
    except Exception as e: 
        return e 


files = glob.glob('image*.jpg')
pool = Pool(8)
results = pool.map(thumbnail, Zip(files,range((len(files)))))

Résultat: 6 secondes


Méthode 8 - Parallel OpenCV

Ceci est censé être une amélioration de la réponse de bcattle, dans la mesure où il utilise OpenCV, mais évite également le besoin de recharger l'image pour générer chaque nouvelle sortie de résolution.

#!/usr/local/bin/python3

import cv2
import glob
from multiprocessing import Pool 

def thumbnail(params): 
    filename, N = params
    try:
        # Load just once, then successively scale down
        im = cv2.imread(filename)
        im = cv2.resize(im, (1600,1600))
        cv2.imwrite("t-%d-1600.jpg" % N, im) 
        im = cv2.resize(im, (720,720))
        cv2.imwrite("t-%d-720.jpg" % N, im) 
        im = cv2.resize(im, (120,120))
        cv2.imwrite("t-%d-120.jpg" % N, im) 
        return 'OK'
    except Exception as e: 
        return e 


files = glob.glob('image*.jpg')
pool = Pool(8)
results = pool.map(thumbnail, Zip(files,range((len(files)))))

Résultat: 5 secondes

6
Mark Setchell

Une autre option consiste à utiliser les liaisons python à OpenCV . Cela peut être plus rapide que PIL ou Imagemagick.

import cv2

sizes = [(120, 120), (720, 720), (1600, 1600)]
image = cv2.imread("input.jpg")
for size in sizes:
    resized_image = cv2.resize(image, size)
    cv2.imwrite("thumbnail_%d.jpg" % size[0], resized_image) 

Il existe une solution plus complète ici .

Si vous voulez l'exécuter en parallèle, utilisez concurrent.futures sur Py3 ou le futures package sur Py2.7:

import concurrent.futures
import cv2

def resize(input_filename, size):
    image = cv2.imread(input_filename)
    resized_image = cv2.resize(image, size)
    cv2.imwrite("thumbnail_%s%d.jpg" % (input_filename.split('.')[0], size[0]), resized_image)

executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
sizes = [(120, 120), (720, 720), (1600, 1600)]
for size in sizes:
    executor.submit(resize, "input.jpg", size)
4
bcattle

Si vous connaissez déjà imagemagick, pourquoi ne pas vous en tenir aux liaisons python?

PythonMagick

3
Don Question

Python 2.7, Windows, utilisateurs x64

En plus de @JobobBowyer & @Audionautics , PIL est assez ancien et vous pouvez trouver le dépannage et rechercher la bonne version ... à la place, utilisez Pillow dans ici ( source )

l'extrait mis à jour ressemblera à ceci:

im = Image.open(full_path)
im.thumbnail(thumbnail_size)
im.save(new_path, "JPEG")

script d'énumération complet pour la création de vignettes:

import os
from PIL import Image

output_dir = '.\\output'
thumbnail_size = (200,200)

if not os.path.exists(output_dir):
    os.makedirs(output_dir)

for dirpath, dnames, fnames in os.walk(".\\input"):
    for f in fnames:
        full_path = os.path.join(dirpath, f)
        if f.endswith(".jpg"):
            filename = 'thubmnail_{0}'.format(f) 
            new_path = os.path.join(output_dir, filename)

            if os.path.exists(new_path):
                os.remove(new_path)

            im = Image.open(full_path)
            im.thumbnail(thumbnail_size)
            im.save(new_path, "JPEG")
2
Jossef Harush

Je suis tombé sur ceci en essayant de trouver quelle bibliothèque je devrais utiliser:

Il semble que OpenCV est clairement plus rapide que PIL .

Cela dit, je travaille avec des tableurs et il s’avère que le module que j’utilisais openpyxl nécessite déjà l’importation de PIL pour insérer des images .

0
virtualxtc