使用Python叠加天文图像

20

一开始我以为这会很容易,但过了一会儿我最终还是放弃了,至少要休息几个小时...

我想复制这个星轨图像,它是从时间序列照片集中生成的。受到这个启发: Inspiration

原作者使用了用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: 图像模式错误

有任何想法吗?

(我还尝试通过在与im.point(lambda i: i * 2)相结合之前对其进行乘法来使图像“变暗”,但结果仍然很糟糕)


1
你的图片权重不平等。例如,你的第一张图片的不透明度为 1 / (1 + 1) = 0.5,而你的第九张图片的不透明度为 0.1 - Blender
1
Blender,我认为它是等权的。当i = 0时,1 /(i + 1)= 1,因此对于第一次迭代,该图像的权重为0.5,因为另外0.5由第二个图像(file [1])占据,然后当i = 2时,file [2]的权重为0.33,前两个文件的总和为0.66..即每个文件为0.33。因此第9个图像确实具有0.1 alpha,但这意味着前8个图像的组合不透明度为0.9,即每个图像为0.1(考虑file [0])。 - JunCTionS
3
不,@Blender是正确的,你需要为每张图片分配一个恒定的权重,意味着是1/len(files)。如果你总共有2张图片,每张图片的权重为0.5。如果你总共有10张图片,每张图片的权重为0.1。 - mathematical.coffee
我确定这是它正在获取的内容(相等的权重)。使用混合函数,每次调用时结果会被归一化。例如,如果我对前10张图像的第一个贡献进行了0.1(针对黑色背景),当下一轮迭代到来时,这个0.9黑色和0.1图像的比例将成为下一个混合的贡献的0.9部分。第一张图片将贡献0.09,第二张图片将贡献0.1。最终,第十张图片将贡献0.1,而第一张图片将贡献0.039(即0.1*(0.9)^10)。混合=image1 *(1.0-alpha)+ image2 * alpha - JunCTionS
1个回答

24

问题在于您正在对每个像素的亮度进行平均。这可能看起来很合理,但实际上根本不是您想要的——明亮的星星会因为它们在图像上移动而被“平均掉”。请看以下四个帧:

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")

这是我得到的:

Low res image set stacked

编辑:我看了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')

这里是一张图片,你可以将它与前面的星轨图像组合在一起。 将低分辨率的图像相加


2
太好了!非常感谢。 这是最终结果,包括原始的全尺寸图像(虽然被imgur的大小限制降级了一些)。 - JunCTionS
@JunCTionS 不用担心,看起来非常不错。我实际上读了Reddit的评论,他对于星轨做了和我建议的一样的事情,但是对于地球,类似于你最初的平均方法。我会快速尝试并将其作为编辑添加。 - simonb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接