用Python叠加天文图像
我本以为这会很简单,但过了一段时间后,我终于决定放弃,至少暂时先休息几个小时……
我想要制作一张星轨的图片,灵感来自于一组延时摄影的照片。看这个:

原作者使用了低分辨率的视频帧,利用VirtualDub和imageJ结合在一起。我想我可以用Python更省内存的方法来轻松复现这个过程,这样我就可以使用原始的高分辨率图片,得到更好的效果。
我的算法思路很简单,就是一次合并两张图片,然后不断地将合并后的图片与下一张图片再合并。这一过程重复几百次,并且要合理加权,确保每张图片对最终结果的贡献是一样的。
我对Python还比较陌生(而且我也不是专业程序员,这一点很明显),但我发现Python Imaging Library是个很常用的库,所以我决定用它(如果你觉得有更好的选择,请告诉我)。
这是我目前的进展:
#program to blend many images into one
import os,Image
files = os.listdir("./")
finalimage=Image.open("./"+files[0]) #add the first image
for i in range(1,len(files)): #note that this will skip files[0] but go all the way to the last file
currentimage=Image.open("./"+files[i])
finalimage=Image.blend(finalimage,currentimage,1/float(i+1))#alpha is 1/i+1 so when the image is a combination of i images any adition only contributes 1/i+1.
print "\r" + str(i+1) + "/" + str(len(files)) #lousy progress indicator
finalimage.save("allblended.jpg","JPEG")
这个代码是按预期工作的,但生成的图片比较暗,如果我试着增强一下,就会明显发现因为像素值缺乏深度而丢失了信息。(我不太确定这里的专业术语是什么,可能是色彩深度、色彩精度、像素大小)。这是使用低分辨率图片得到的最终结果:
或者是我尝试用另一组照片制作的4k x 2k高分辨率的结果:
所以,我尝试通过设置图片模式来修复这个问题:
firstimage=Image.open("./"+files[0])
size = firstimage.size
finalimage=Image.new("I",size)
但显然Image.blend不接受这种图片模式。
ValueError: image has wrong mode
有没有什么好的建议?
(我也尝试过在合并之前通过im.point(lambda i: i * 2)让图片“变得不那么暗”,但结果还是一样糟糕)
1 个回答
这里的问题是,你在对每个像素的亮度进行平均。虽然这看起来很合理,但实际上这并不是你想要的效果——因为亮星在图像中移动,它们会被“平均掉”。看看以下四帧:
1000 0000 0000 0000
0000 0100 0000 0000
0000 0000 0010 0000
0000 0000 0000 0001
如果你对这些帧进行平均,你会得到:
0.25 0 0 0
0 0.25 0 0
0 0 0.25 0
0 0 0 0.25
而你想要的是:
1000
0100
0010
0001
与其混合这些图像,不如尝试对每个像素取所有图像中看到的最大值。如果你有PIL库,可以试试ImageChops中的lighter函数。
from PIL import ImageChops
import os, Image
files = os.listdir("./")
finalimage=Image.open("./"+files[0])
for i in range(1,len(files)):
currentimage=Image.open("./"+files[i])
finalimage=ImageChops.lighter(finalimage, currentimage)
finalimage.save("allblended.jpg","JPEG")
这是我得到的结果:
编辑:我看了Reddit的帖子,发现他实际上结合了两种方法——一种用于星轨,另一种用于地球。这是你尝试的平均方法的更好实现,使用了适当的权重。我使用了numpy数组来进行中间存储,而不是uint8图像数组。
import os, Image
import numpy as np
files = os.listdir("./")
image=Image.open("./"+files[0])
im=np.array(image,dtype=np.float32)
for i in range(1,len(files)):
currentimage=Image.open("./"+files[i])
im += np.array(currentimage, dtype=np.float32)
im /= len(files) * 0.25 # lowered brightness, with magic factor
# clip, convert back to uint8:
final_image = Image.fromarray(np.uint8(im.clip(0,255)))
final_image.save('all_averaged.jpg', 'JPEG')
这是图像,你可以将它与之前的星轨结合起来。