Например, у меня есть 100 изображений, разрешение которых одинаковое, и я хочу объединить их в одну картинку. Для окончательного изображения значение RGB для каждого пикселя составляет в среднем 100 изображений в этой позиции. Я знаю, что функция getdata
может работать в этой ситуации, но есть ли более простой и быстрый способ сделать это в PIL (Библиотека изображений Python)?
Как получить среднее изображение из 100 изображений с помощью PIL?
Ответ 1
Предположим, что ваши изображения - это все .png файлы, и все они хранятся в текущем рабочем каталоге. Код python ниже сделает то, что вы хотите. Как предполагает Игнасио, использование numpy вместе с PIL является ключевым здесь. Вам просто нужно быть немного осторожным при переключении между массивами integer и float при построении средних интенсивностей пикселей.
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()
Изображение, приведенное ниже, было создано из последовательности видеокадров HD-видео, используя приведенный выше код.
Ответ 2
Мне трудно представить ситуацию, в которой проблема памяти здесь, но в (маловероятном) событии вы абсолютно не можете позволить себе создать массив поплавков, необходимых для моего оригинала answer, вы можете использовать функцию смешивания PIL, как предложено @mHurley следующим образом:
# 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()
Вы можете получить правильную последовательность альфа-значений, начиная с определения из функции смешивания PIL:
out = image1 * (1.0 - alpha) + image2 * alpha
Подумайте о том, чтобы рекурсивно применять эту функцию к вектору чисел (а не к изображениям), чтобы получить среднее значение вектора. Для вектора длины N вам понадобятся операции смешивания N-1 с N-1 различными значениями альфа.
Однако, возможно, легче интуитивно думать об операциях. На каждом шаге вы хотите, чтобы avg-образ содержал равные пропорции исходных изображений с более ранних шагов. При смешивании первого и второго исходных изображений альфа должна составлять 1/2 для обеспечения равных пропорций. При смешивании третьего со средним значением первых двух вы хотели бы, чтобы новое изображение состояло из 1/3 третьего изображения, а остаток составлял среднее из предыдущих изображений (текущее значение avg), и т.д.
В принципе, этот новый ответ, основанный на смешении, должен быть прекрасным. Однако я точно не знаю, как работает функция blend. Это заставляет меня беспокоиться о том, как значения пикселей округляются после каждой итерации.
Изображение ниже было создано из 288 исходных изображений, используя код из моего исходного ответа:
С другой стороны, это изображение было создано путем многократного применения функции смешивания PIL к тем же 288 изображениям:
Надеюсь, вы увидите, что выходы из двух алгоритмов заметно отличаются. Я ожидаю, что это связано с накоплением небольших ошибок округления при повторном применении Image.blend
Я настоятельно рекомендую мой оригинальный ответ по этой альтернативе.
Ответ 3
Я хотел бы создать массив x от y целых чисел, начиная с (0, 0, 0), а затем для каждого пикселя в каждом файле добавьте значение RGB, разделите все значения на 100 и затем создайте изображение из что - вы, вероятно, обнаружите, что numpy может помочь.
Ответ 4
Можно также использовать среднюю функцию numpy для усреднения. Код выглядит лучше и работает быстрее.
Здесь сравнение времени и результатов для 700 шумовых изображений в градациях серого:
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
BTW, результаты усреднения совершенно разные. Я думаю, что первый метод потерял информацию во время усреднения. А вторая имеет некоторые артефакты.
average_img_1
average_img_2
average_img_3
Ответ 5
Я столкнулся с MemoryErrors при попытке метода в принятом ответе. Я нашел способ оптимизации, который, похоже, дает тот же результат. В основном, вы смешиваете одно изображение за раз, вместо того, чтобы добавлять их все и делиться.
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)
Это делает две вещи: вам не нужно иметь две очень плотные копии изображения, пока вы поворачиваете изображение в массив, и вам не нужно использовать 64-битные поплавки. Вы получаете такую же высокую точность, с меньшими числами. Результаты APPEAR будут одинаковыми, хотя я был бы признателен, если бы кто-то проверил мою математику.