例如,我有100张分辨率相同的图片,我想将它们合并为一张图片。对于最终图片,每个像素的RGB值是该位置处的100张图片的平均值。我知道getdata
函数可以在这种情况下工作,但在PIL(Python图像库)中有更简单,更快捷的方法吗?
答案 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()
以下图片是使用上述代码从一系列高清视频帧生成的。
答案 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个源图像生成的:
另一方面,该图像是通过将PIL的混合功能重复应用于相同的288张图像而生成的:
我希望你能看到两种算法的输出明显不同。我希望这是因为在重复应用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
average_img_2
average_img_3
答案 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位浮在水面上。您可以获得类似的高精度,数量更少。结果显示相同,但如果有人检查了我的数学,我会很感激。