星景写真の周辺減光をプログラミングで補正

星景写真の周辺減光をPythonを使って補正する具体的な手順です。もう高価なカメラやレンズを追い求めるのではなく無料でオリジナルの写真を作っていきましょう。

以前に星景写真の周辺減光を補正する手段について説明しました。今回は具体的な方法を考えていきます。

まず元の写真です。

四隅にいくほど暗くなっています。これをLightroomなどで周辺減光を補正するとどうしても不自然になりました。

Pythonを使っても、単純に四隅いくほど平等に明るくするとLightroomと同様にどうしても不自然になります。そこで右上と左上にいくほど明るさを強く補正して右下と左下は弱く補正することにしました。全ての写真にこの方法を適応して自然に補正出来るということではありません。今回のように上が空、下が風景で風景はあまり補正したくないような場合には使えるかもしれません。

今回は、写真の中心からの距離(r)の2乗に比例して明るくさせました。周辺減光の原理を考えるとcos(r)の逆数に比例して明るくさせても良かったのですが、最初はシンプルの方がいいでしょう。

import numpy as np
import cv2
 
# cv2.imreadを使用してJPEG画像を読み込みピクセル値を0から1の範囲に正規化
img = cv2.imread(filename).astype(np.float32) / 255.0
 
h, w = img.shape[:2]
size = max([h, w])  # 幅、高の大きい方をsizeに定義
 
# 画像の各ピクセルが中心からどれだけ離れているかを示す距離画像 (r) を作成
x = np.linspace(-w/size, w/size, w)
y = np.linspace(-h/size, h/size, h)  # 長い方の辺が1になるように正規化
xx, yy = np.meshgrid(x, y)
r = np.sqrt(xx**2 + yy**2)
 
# 距離に基づいてゲイン値を計算してgainmap作成。ここでは距離の二乗に比例した補正を行う。
gain = 1.5*r**2 + 1
gainmap = np.dstack([gain, gain, gain])    # RGBは同じゲインに設定
 
# 上下左右の補正強度マップの作成
vertical_gradient = np.linspace(1.0, 0.5, h).reshape(h, 1)
horizontal_gradient = np.linspace(1.0, 0.5, w).reshape(1, w)
gradient_map = np.minimum(vertical_gradient, horizontal_gradient)
gradient_map = np.maximum(gradient_map, vertical_gradient[:, ::-1])
gradient_map = np.maximum(gradient_map, horizontal_gradient[::-1, :])

# 画像の下半分で左側 (i > h/2 and j < w/2) と右側 (i > h/2 and j > w/2) の補正を弱くするために強度マップを調整
for i in range(h):
    for j in range(w):
        if i > h/2 and j < w/2: # 左下
            gradient_map[i, j] = np.maximum(gradient_map[i, j], 0.3)
        elif i > h/2 and j > w/2: # 右下
            gradient_map[i, j] = np.maximum(gradient_map[i, j], 0.2)

#RGBに適用
#グラデーションマップ (gradient_map) をRGBチャンネルに適用してゲインマップ (gainmap) に乗じる。
gradient_map_rgb = np.dstack([gradient_map, gradient_map, gradient_map]) 

# ゲインマップに補正強度マップを適用
gainmap *= gradient_map_rgb

# ゲインマップを画像に乗じ、ピクセル値を0から1の範囲内にクリップする。
img = np.clip(img * gainmap, 0., 1.0)
 
# 最終的に、ピクセル値を0から255の範囲にスケーリングし、8ビットのJPEG画像として保存
cv2.imwrite("filename.jpeg",(img*255).astype(np.uint8))

再度、元の写真を上に、このプログラムで周辺減光を補正したものを下に表示させました。

どうでしょうか。割と自然に補正出来たのではないでしょうか。ただ、周辺減光があった方が写真として良い場合もあり、これはあくまで好みの問題と思います。

周辺減光をなくすために数十万円のカメラやレンズを買うのであれば、このプログラミングを使うことで数十万円を節約できるかもしれません。