如何使用PIL从100张图片中获得平均图片?

时间:2013-06-25 07:23:38

标签: python image python-imaging-library

例如,我有100张分辨率相同的图片,我想将它们合并为一张图片。对于最终图片,每个像素的RGB值是该位置处的100张图片的平均值。我知道getdata函数可以在这种情况下工作,但在PIL(Python图像库)中有更简单,更快捷的方法吗?

6 个答案:

答案 0 :(得分:37)

假设您的图像都是.png文件,它们都存储在当前工作目录中。下面的python代码将执行您想要的操作。正如Ignacio所说,使用numpy和PIL是关键。在构建平均像素强度时,您只需要在整数和浮点数组之间切换时要小心。

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()

以下图片是使用上述代码从一系列高清视频帧生成的。

Average of HD video frames

答案 1 :(得分:11)

我发现很难想象内存在这里是一个问题,但在(不太可能)的事件中,你绝对无法负担创建original answer所需的浮点数组,你可以使用PIL&# 39; s @mHurley的混合函数as suggested如下:

# 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的混合函数定义开始派生正确的alpha值序列:

out = image1 * (1.0 - alpha) + image2 * alpha

考虑将该函数递归地应用于数字向量(而不是图像)以获得向量的均值。对于长度为N的向量,您需要N-1个混合操作,其中N-1个不同的alpha值。

但是,直观地思考这些操作可能更容易。在每个步骤中,您希望avg图像包含来自早期步骤的相同比例的源图像。在混合第一和第二源图像时,alpha应为1/2以确保相等的比例。当将第三个与前两个的平均值混合时,您希望新图像由第三个图像的1/3组成,其余部分由前一个图像的平均值组成(当前平均值)等等。

原则上这个基于混合的新答案应该没问题。但是,我并不确切知道混合功能是如何工作的。这让我担心每次迭代后像素值如何舍入。

下面的图片是使用我原来答案中的代码从288个源图像生成的:

Averaged, original answer

另一方面,该图像是通过将PIL的混合功能重复应用于相同的288张图像而生成的:

Blended, using Image.blend

我希望你能看到两种算法的输出明显不同。我希望这是因为在重复应用Image.blend

期间累积了小的舍入误差

我强烈推荐我的original answer替代此选项。

答案 2 :(得分:4)

也可以使用numpy mean函数进行平均。代码看起来更好,工作更快。

这里比较了面部的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
顺便说一句,平均的结果是完全不同的。我认为第一种方法在平均期间会丢失信息。而第二个有一些文物。

average_img_1

enter image description here

average_img_2

enter image description here

average_img_3

enter image description here

答案 3 :(得分:2)

我会考虑从(0,0,0)开始创建一个x乘y整数的数组,然后为每个文件中的每个像素添加RGB值,将所有值除以100,然后从中创建图像那 - 你可能会发现numpy可以提供帮助。

答案 4 :(得分:1)

如果有人对蓝图numpy解决方案感兴趣(我实际上是在寻找它),这里是代码:

mean_frame = np.mean(([frame for frame in frames]), axis=0)

答案 5 :(得分:0)

在接受的答案中尝试方法时,我遇到了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位浮在水面上。您可以获得类似的高精度,数量更少。结果显示相同,但​​如果有人检查了我的数学,我会很感激。