25

For example, I have 100 pictures whose resolution is the same, and I want to merge them into one picture. For the final picture, the RGB value of each pixel is the average of the 100 pictures' at that position. I know the getdata function can work in this situation, but is there a simpler and faster way to do this in PIL(Python Image Library)?

4

6 回答 6

48

假设您的图像都是 .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()

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

高清视频帧的平均值

于 2013-06-29T18:49:58.370 回答
14

我发现很难想象这里的内存是一个问题的情况,但是在(不太可能)你绝对不能创建我的原始答案所需的浮点数组的情况下,你可以使用 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 的 blend 函数的定义开始推导出正确的 alpha 值序列:

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

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

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

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

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

平均,原始答案

另一方面,这张图片是通过对相同的 288 张图片重复应用 PIL 的混合功能生成的:

混合,使用 Image.blend

我希望你能看到两种算法的输出明显不同。我预计这是因为在 Image.blend 的重复应用过程中积累了小的舍入误差

我强烈推荐我原来的答案而不是这个替代方案。

于 2015-06-16T20:43:11.790 回答
7

也可以使用 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

在此处输入图像描述

average_img_2

在此处输入图像描述

average_img_3

在此处输入图像描述

于 2017-03-23T09:36:38.753 回答
3

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

于 2013-06-25T07:42:10.903 回答
3

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

mean_frame = np.mean(([frame for frame in frames]), axis=0)
于 2018-05-07T20:49:44.680 回答
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 位浮点数。您可以获得同样高的精度,但数量较少。结果似乎是相同的,但如果有人检查我的数学,我将不胜感激。

于 2015-06-16T04:31:41.450 回答