Подтвердить что ты не робот

Как получить среднее изображение из 100 изображений с помощью PIL?

Например, у меня есть 100 изображений, разрешение которых одинаковое, и я хочу объединить их в одну картинку. Для окончательного изображения значение RGB для каждого пикселя составляет в среднем 100 изображений в этой позиции. Я знаю, что функция getdata может работать в этой ситуации, но есть ли более простой и быстрый способ сделать это в PIL (Библиотека изображений Python)?

4b9b3361

Ответ 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-видео, используя приведенный выше код.

Average of HD video frames

Ответ 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 исходных изображений, используя код из моего исходного ответа:

Averaged, original answer

С другой стороны, это изображение было создано путем многократного применения функции смешивания PIL к тем же 288 изображениям:

Blended, using Image.blend

Надеюсь, вы увидите, что выходы из двух алгоритмов заметно отличаются. Я ожидаю, что это связано с накоплением небольших ошибок округления при повторном применении 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 будут одинаковыми, хотя я был бы признателен, если бы кто-то проверил мою математику.