recorrer - manipulacion de imagenes en python
¿Cuál es la forma más rápida de generar miniaturas de imágenes en Python? (7)
Python 2.7, Windows, x64 usuarios
Además de @JakobBowyer & @Audionautics , PIL
es bastante antiguo y puedes encontrarte con la solución de problemas y buscar la versión correcta ... en su lugar, usa Pillow
desde here ( source )
El fragmento actualizado se verá así:
im = Image.open(full_path)
im.thumbnail(thumbnail_size)
im.save(new_path, "JPEG")
Script de enumeración completo para la creación de miniaturas:
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")
Estoy construyendo una galería de fotos en Python y quiero poder generar rápidamente miniaturas para las imágenes de alta resolución.
¿Cuál es la forma más rápida de generar miniaturas de alta calidad para una variedad de fuentes de imagen?
¿Debo utilizar una biblioteca externa como imagemagick o hay una forma interna eficiente de hacerlo?
Las dimensiones de las imágenes redimensionadas serán (tamaño máximo):
120x120
720x720
1600x1600
La calidad es un problema, ya que deseo preservar la mayor cantidad de colores originales posible y minimizar los artefactos de compresión.
Gracias.
Me gustó algo de diversión, así que hice una evaluación comparativa de los diversos métodos sugeridos anteriormente y algunas ideas propias.
Recolecté 1000 imágenes de alta resolución de 12MP para iPhone 6s, cada una de 4032x3024 píxeles y uso un iMac de 8 núcleos.
Aquí están las técnicas y los resultados, cada uno en su propia sección.
Método 1 - ImageMagick secuencial
Este es un código simplista, no optimizado. Cada imagen se lee y se produce una miniatura. Luego se lee de nuevo y se produce una miniatura de diferente tamaño.
#!/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))
Resultado: 170 segundos.
Método 2: ImageMagick secuencial con carga única y cambio de tamaño sucesivo
Esto sigue siendo secuencial pero un poco más inteligente. Cada imagen solo se lee una vez y la imagen cargada se redimensiona a tres y se guarda en tres resoluciones. La mejora es que cada imagen se lee solo una vez, no 3 veces.
#!/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))
Resultado: 76 segundos.
Método 3 - GNU Parallel + ImageMagick
Esto se basa en el método anterior, al usar GNU Parallel para procesar N
imágenes en paralelo, donde N
es el número de núcleos de CPU en su máquina.
#!/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))
Resultado: 18 segundos.
Método 4 - GNU paralelo + vips
Este es el mismo que el método anterior, pero usa vips
en la línea de comandos en lugar 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))
Resultado: 8 segundos
Método 5 - PIL secuencial
Esto está destinado a corresponder a la respuesta 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
Resultado: 38 segundos.
Método 6: PIL secuencial con carga única y cambio de tamaño sucesivo
Esto pretende ser una mejora de la respuesta de Jakob, en donde la imagen se carga solo una vez y luego se redimensiona tres veces en lugar de volver a cargarla cada vez para producir cada nueva resolución.
#!/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
Resultado: 27 segundos.
Método 7 - PIL paralelo
Se pretende que esto corresponda con la respuesta de Audionautics, en la medida en que utiliza el multiprocesamiento de Python. También evita la necesidad de volver a cargar la imagen para cada tamaño de miniatura.
#!/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)))))
Resultado: 6 segundos.
Método 8 - OpenCV paralelo
Se pretende que sea una mejora en la respuesta de bcattle, en la medida en que utiliza OpenCV, pero también evita la necesidad de volver a cargar la imagen para generar cada salida de resolución nueva.
#!/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)))))
Resultado: 5 segundos
Me topé con this al intentar averiguar qué biblioteca debería usar:
Parece que OpenCV es claramente más rápido que PIL .
Dicho esto, estoy trabajando con hojas de cálculo y resulta que el módulo que estaba usando openpyxl ya me obliga a importar PIL para insertar imágenes .
Otra opción es usar los enlaces de python para OpenCV . Esto puede ser más rápido que PIL o 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)
Hay un tutorial más completo here .
Si desea ejecutarlo en paralelo, use concurrent.futures
en Py3 o el paquete de futures
en 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)
Quieres PIL lo hace con facilidad.
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 necesitas desesperadamente velocidad. Luego pásalo, multiprocesalo u obtén otro idioma.
Si ya está familiarizado con imagemagick, ¿por qué no atenerse a los enlaces de python?
Un poco tarde para la pregunta (¡solo un año!), Pero voy a ir con la parte de "multiprocesamiento" de la respuesta de @ JakobBowyer.
Este es un buen ejemplo de un problema vergonzosamente paralelo , ya que el bit principal del código no muta ningún estado externo a sí mismo. Simplemente lee una entrada, realiza su cálculo y guarda el resultado.
Python es bastante bueno en este tipo de problemas gracias a la función de mapa proporcionada por 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))
El núcleo del código es exactamente el mismo que @JakobBowyer, pero en lugar de ejecutarlo en un bucle en un solo hilo, lo envolvimos en una función distribuida en varios núcleos a través de la función de mapa de multiprocesamiento.