python - segmentacion - ¿Cómo obtener una imagen promedio de 100 imágenes usando PIL?
segmentacion imagen python (6)
Por ejemplo, tengo 100 imágenes cuya resolución es la misma, y quiero unirlas en una sola imagen. Para la imagen final, el valor RGB de cada píxel es el promedio de las 100 imágenes en esa posición. Sé que la función getdata
puede funcionar en esta situación, pero ¿hay una forma más simple y rápida de hacerlo en PIL (Biblioteca de imágenes de Python)?
Consideraría crear una matriz de enteros x por y todos comenzando en (0, 0, 0) y luego para cada píxel en cada archivo agregue el valor RGB, divida todos los valores por 100 y luego cree la imagen a partir de eso. Es probable que encuentre que Numpy puede ayudar.
En caso de que alguien esté interesado en una solución de número de planos (en realidad la estaba buscando), aquí está el código:
mean_frame = np.mean(([frame for frame in frames]), axis=0)
Me encontré con MemoryErrors al intentar el método en la respuesta aceptada. Encontré una forma de optimizar que parece producir el mismo resultado. Básicamente, combina una imagen a la vez, en lugar de sumarlas todas y dividirlas.
N=len(images_to_blend)
avg = Image.open(images_to_blend[0])
for im in images_to_blend: #assuming your list is filenames, not images
img = Image.open(im)
avg = Image.blend(avg, img, 1/N)
avg.save(blah)
Esto hace dos cosas, no tiene que tener dos copias muy densas de la imagen mientras está convirtiendo la imagen en una matriz, y no tiene que usar flotadores de 64 bits en absoluto. Obtienes una alta precisión similar, con números más pequeños. Los resultados parecen ser los mismos, aunque apreciaría si alguien verificara mis cálculos.
Me resulta difícil imaginar una situación en la que la memoria sea un problema aquí, pero en el caso (improbable) de que no pueda permitirse el lujo de crear el conjunto de flotadores necesarios para mi respuesta original , podría usar la función de mezcla de PIL, como sugiere @ mHurley como sigue:
# Alternative method using PIL blend function
avg=Image.open(imlist[0])
for i in xrange(1,N):
img=Image.open(imlist[i])
avg=Image.blend(avg,img,1.0/float(i+1))
avg.save("Blend.png")
avg.show()
Podría derivar la secuencia correcta de valores alfa, comenzando con la definición de la función de mezcla de PIL:
out = image1 * (1.0 - alpha) + image2 * alpha
Piense en aplicar esa función recursivamente a un vector de números (en lugar de imágenes) para obtener la media del vector. Para un vector de longitud N, necesitaría operaciones de fusión N-1, con N-1 valores diferentes de alfa.
Sin embargo, es probable que sea más fácil pensar intuitivamente sobre las operaciones. En cada paso, desea que la imagen promedio contenga proporciones iguales de las imágenes de origen de los pasos anteriores. Cuando se combinan la primera y la segunda fuente de imágenes, alfa debe ser 1/2 para asegurar proporciones iguales. Al combinar la tercera con el promedio de las dos primeras, le gustaría que la nueva imagen se componga de 1/3 de la tercera imagen, mientras que el resto se componga de la media de las imágenes anteriores (valor actual de avg) , y así.
En principio, esta nueva respuesta, basada en la mezcla, debería estar bien. Sin embargo, no sé exactamente cómo funciona la función de mezcla. Esto me hace preocuparme por cómo se redondean los valores de los píxeles después de cada iteración.
La imagen a continuación se generó a partir de 288 imágenes de origen utilizando el código de mi respuesta original:
Por otro lado, esta imagen se generó aplicando repetidamente la función de mezcla de PIL a las mismas 288 imágenes:
Espero que puedan ver que los resultados de los dos algoritmos son notablemente diferentes. Espero que esto se deba a la acumulación de pequeños errores de redondeo durante la aplicación repetida de Image.blend
Recomiendo encarecidamente mi respuesta original sobre esta alternativa.
Supongamos que sus imágenes son todos archivos .png y que están almacenados en el directorio de trabajo actual. El siguiente código de python hará lo que quieras. Como sugiere Ignacio, usar la clave junto con PIL es la clave aquí. Solo debe tener un poco de cuidado al cambiar entre matrices de enteros y flotantes al construir sus intensidades de píxeles promedio.
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlist=[filename for filename in allfiles if filename[-4:] in [".png",".PNG"]]
# Assuming all images are the same size, get dimensions of first image
w,h=Image.open(imlist[0]).size
N=len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr=numpy.zeros((h,w,3),numpy.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr=numpy.array(Image.open(im),dtype=numpy.float)
arr=arr+imarr/N
# Round values in array and cast as 8-bit integer
arr=numpy.array(numpy.round(arr),dtype=numpy.uint8)
# Generate, save and preview final image
out=Image.fromarray(arr,mode="RGB")
out.save("Average.png")
out.show()
La imagen de abajo se generó a partir de una secuencia de cuadros de video HD usando el código anterior.
También se puede usar la función media numpy para promediar. El código se ve mejor y funciona más rápido.
Aquí la comparación de la sincronización y los resultados para 700 imágenes de caras ruidosas en escala de grises:
def average_img_1(imlist):
# Assuming all images are the same size, get dimensions of first image
w,h=Image.open(imlist[0]).size
N=len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr=np.zeros((h,w),np.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr=np.array(Image.open(im),dtype=np.float)
arr=arr+imarr/N
out = Image.fromarray(arr)
return out
def average_img_2(imlist):
# Alternative method using PIL blend function
N = len(imlist)
avg=Image.open(imlist[0])
for i in xrange(1,N):
img=Image.open(imlist[i])
avg=Image.blend(avg,img,1.0/float(i+1))
return avg
def average_img_3(imlist):
# Alternative method using numpy mean function
images = np.array([np.array(Image.open(fname)) for fname in imlist])
arr = np.array(np.mean(images, axis=(0)), dtype=np.uint8)
out = Image.fromarray(arr)
return out
average_img_1()
100 loops, best of 3: 362 ms per loop
average_img_2()
100 loops, best of 3: 340 ms per loop
average_img_3()
100 loops, best of 3: 311 ms per loop
Por cierto, los resultados del promedio son bastante diferentes. Creo que el primer método pierde información durante el promedio. Y el segundo tiene algunos artefactos.
average_img_1
average_img_2
average_img_3