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)?
6 回答
假设您的图像都是 .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()
下面的图像是使用上面的代码从一系列高清视频帧生成的。
我发现很难想象这里的内存是一个问题的情况,但是在(不太可能)你绝对不能创建我的原始答案所需的浮点数组的情况下,你可以使用 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 的重复应用过程中积累了小的舍入误差
我强烈推荐我原来的答案而不是这个替代方案。
也可以使用 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
我会考虑创建一个由 (0, 0, 0) 开始的 x x y 整数数组,然后为每个文件中的每个像素添加 RGB 值,将所有值除以图像数量,然后从那 - 你可能会发现 numpy 可以提供帮助。
如果有人对蓝图 numpy 解决方案感兴趣(我实际上正在寻找它),代码如下:
mean_frame = np.mean(([frame for frame in frames]), axis=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 位浮点数。您可以获得同样高的精度,但数量较少。结果似乎是相同的,但如果有人检查我的数学,我将不胜感激。