Python 如何使用PIL从100张图片中获取平均图片?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17291455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get an average picture from 100 pictures using PIL?
提问by Hanfei Sun
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)?
例如,我有100张分辨率相同的图片,我想将它们合并为一张图片。对于最终图片,每个像素的 RGB 值是该位置 100 张图片的平均值。我知道该getdata
函数可以在这种情况下工作,但是在 PIL(Python 图像库)中是否有更简单快捷的方法来执行此操作?
采纳答案by CnrL
Let's assume that your images are all .png files and they are all stored in the current working directory. The python code below will do what you want. As Ignacio suggests, using numpy along with PIL is the key here. You just need to be a little bit careful about switching between integer and float arrays when building your average pixel intensities.
让我们假设您的图像都是 .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()
The image below was generated from a sequence of HD video frames using the code above.
下面的图像是使用上面的代码从一系列高清视频帧生成的。
回答by Steve Barnes
I would consider creating an array of x by y integers all starting at (0, 0, 0) and then for each pixel in each file add the RGB value in, divide all the values by 100 and then create the image from that - you will probably find that numpy can help.
我会考虑创建一个 x 由 y 整数组成的数组,所有整数都从 (0, 0, 0) 开始,然后为每个文件中的每个像素添加 RGB 值,将所有值除以 100,然后从中创建图像 - 你可能会发现 numpy 可以提供帮助。
回答by Matt
I ran into MemoryErrors when trying the method in the accepted answer. I found a way to optimize that seems to produce the same result. Basically, you blend one image at a time, instead of adding them all up and dividing.
在接受的答案中尝试该方法时,我遇到了 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)
This does two things, you don't have to have two very dense copies of the image while you're turning the image into an array, and you don't have to use 64-bit floats at all. You get similarly high precision, with smaller numbers. The results APPEAR to be the same, though I'd appreciate if someone checked my math.
这有两件事,当您将图像转换为数组时,您不必拥有图像的两个非常密集的副本,并且根本不必使用 64 位浮点数。您可以使用较小的数字获得类似的高精度。结果似乎是一样的,但如果有人检查我的数学,我会很感激。
回答by CnrL
I find it difficult to imagine a situation where memory is an issue here, but in the (unlikely) event that you absolutely cannot afford to create the array of floats required for my original answer, you could use PIL's blend function, as suggestedby @mHurley as follows:
我发现很难想象内存在这里成为问题的情况,但是在(不太可能)您绝对负担不起创建我的原始答案所需的浮点数数组的情况下,您可以使用 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()
You could derive the correct sequence of alpha values, starting with the definition from PIL's blend function:
您可以从 PIL 的混合函数的定义开始推导出正确的 alpha 值序列:
out = image1 * (1.0 - alpha) + image2 * alpha
Think about applying that function recursively to a vector of numbers (rather than images) to get the mean of the vector. For a vector of length N, you would need N-1 blending operations, with N-1 different values of alpha.
考虑将该函数递归地应用于数字向量(而不是图像)以获得向量的均值。对于长度为 N 的向量,您需要 N-1 次混合操作,以及 N-1 个不同的 alpha 值。
However, it's probably easier to think intuitively about the operations. At each step you want the avg image to contain equal proportions of the source images from earlier steps. When blending the first and second source images, alpha should be 1/2 to ensure equal proportions. When blending the third with the the average of the first two, you would like the new image to be made up of 1/3 of the third image, with the remainder made up of the average of the previous images (current value of avg), and so on.
但是,直观地考虑操作可能更容易。在每个步骤中,您都希望平均图像包含来自先前步骤的源图像的相等比例。混合第一个和第二个源图像时,alpha 应为 1/2 以确保相等的比例。将第三张与前两张的平均值混合时,您希望新图像由第三张图像的 1/3 组成,其余图像由前一张图像的平均值(avg 的当前值)组成, 等等。
In principle this new answer, based on blending, should be fine. However I don't know exactly how the blend function works. This makes me worry about how the pixel values are rounded after each iteration.
原则上,这个基于混合的新答案应该没问题。但是我并不确切知道混合功能是如何工作的。这让我担心每次迭代后像素值是如何舍入的。
The image below was generated from 288 source images using the code from my original answer:
下面的图像是使用我原来的答案中的代码从 288 个源图像生成的:
On the other hand, this image was generated by repeatedly applying PIL's blend function to the same 288 images:
另一方面,该图像是通过将 PIL 的混合函数重复应用于相同的 288 个图像生成的:
I hope you can see that the outputs from the two algorithms are noticeably different. I expect this is because of accumulation of small rounding errors during repeated application of Image.blend
我希望你能看到两种算法的输出明显不同。我预计这是因为在 Image.blend 的重复应用过程中积累了小的舍入误差
I strongly recommend my original answerover this alternative.
我强烈推荐我对这个替代方案的原始答案。
回答by Katerina
One can also use numpy mean function for averaging. The code looks better and works faster.
还可以使用 numpy mean 函数进行平均。代码看起来更好,运行速度更快。
Here the comparison of timing and results for 700 noisy grayscale images of faces:
这里是 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, the results of averaging are quite different. I think the first method lose information during averaging. And the second one has some artifacts.
顺便说一句,平均的结果是完全不同的。我认为第一种方法在平均过程中会丢失信息。第二个有一些文物。
average_img_1
average_img_1
average_img_2
average_img_2
average_img_3
average_img_3
回答by Fábio
in case anybody is interested in a blueprint numpy solution (I was actually looking for it), here's the code:
如果有人对蓝图 numpy 解决方案感兴趣(我实际上是在寻找它),这里是代码:
mean_frame = np.mean(([frame for frame in frames]), axis=0)