この記事は筑波大学の金森教授(https://kanamori.cs.tsukuba.ac.jp/index-ja.html )にポートフォリオとしての使用許可を頂き執筆しております。
はじめに
今回は筑波大学の研究である、【光の遮蔽を考慮した人物全身画像の逆レンダリング 】のソースコードが公開されていたので、実際に動かしてみることにしました。また、ただ動かすだけではすぐに終わってしまうので光源位置をマウスで操作したり、光源の色を変えられるようなGUIを実装してみます。
目次
再照明ってなによ?
再照明とは、ある照明環境下で撮影された物体を、他の照明環境下に持っていった場合に、どのような陰影がつくかシミュレーションする技術のことです。relightingとも言います。この技術は主にコンポジット(合成)に対して役に立つことが期待されています。
従来、人物に対する再照明は顔のみを対象とすることが多かったのですが、筑波大学の研究では球面調和関数に可視関数を導入することで全身画像に対しても精度の高い再照明を実現しています。細かい説明については以下のサイトでされていますので興味がある方は読んでみてください。
実際に動かしてみる。
まず、論文のプロジェクトページに飛んで光源データとPytorch版の訓練済みモデルとソースコードをダウンロードします。(モデルは右クリックして「名前を付けてリンク先を保存」で保存できるよ)
次にtest_with_photos.pyで光伝達マップや光源データなどを出力する必要があります。photo_inputsフォルダに再照明したい画像とそのマスク画像を入れれば、好きな画像で再照明を行うこともできます。以下は、元から入っていた画像で推論を行った結果です。
この結果をもとにrelight.pyで再照明結果をmp4出力します。出力結果がこちらです。
ちなみに、モデルをトレーニングしたいときはtrain.pyでトレーニングできます。トレーニング用とテスト用のライトデータはプロジェクトページからダウンロードできます。
光源色をいじれるようにしてみる
再照明や光源データの仕組みを学ぶついでに、リアルタイムで光源色を変えたり光源位置を変えたりできるようにしたいと思います。まずは光源色変化です。
光源データを覗いてみたところ、3列のnumpy配列でRGBを表現しているようでした。そこで各配列に係数をかけて正規化し、色をコントロールしてみます。
coeffs[:, 0] = sh_rot.sh_rot(R, light[:, 0]*blue)
coeffs[:, 1] = sh_rot.sh_rot(R, light[:, 1]*green)
coeffs[:, 2] = sh_rot.sh_rot(R, light[:, 2]*red)
ここでsh_rot関数は球面調和関数の回転を行っているようです。回転行列Rと光源配列を引数として球面調和関数の係数を返します。
次にUIにはtkinterのcolorchooserを使用します。ユーザーが選択した色から先ほどの各配列にかける係数を決定します。
def change_color():
color = colorchooser.askcolor()
R, G, B = color[0][0], color[0][1], color[0][2] * 1.5
files_path = create_frame(n_rotation_div, B, G, R, max_val)
return files_path
button1 = ttk.Button(
frame,
text='Change Color',
command=lambda: change_color())
button1.grid(row=0, column=1, padx=5, sticky=(E))
光源の位置を動かせるようにしてみる
光源1周分の再照明結果をあらかじめ読み込んで、tkinterのscaleと呼ばれるスライダーUIで光源角度を調整できるようにします。
def create_frame(n_rotation_div, blue, green, red, max_val):
tmp_renderings = []
for j in range(n_rotation_div):
deg = (360. / n_rotation_div) * j
R = sh_rot.calc_y_rot(deg / 180. * np.pi)
coeffs = np.empty_like(light)
print(coeffs)
coeffs[:, 0] = sh_rot.sh_rot(R, light[:, 0]*blue)
coeffs[:, 1] = sh_rot.sh_rot(R, light[:, 1]*green)
coeffs[:, 2] = sh_rot.sh_rot(R, light[:, 2]*red)
coeffs = torch.from_numpy(coeffs.astype(np.float32)).clone()
if gpu > -1:
coeffs = coeffs.to("cuda")
shading = torch.matmul(transport, coeffs)
if not args.shading:
rendering = albedo * shading
else:
rendering = shading
tmp_renderings.append(rendering)
max_val = max((max_val, torch.max(rendering)))
for j in range(n_rotation_div):
rendering = 255 * tmp_renderings[j] / max_val
if gpu > -1:
rendering = rendering.to("cpu")
rendering = rendering.to('cpu').detach().numpy().copy()
結果は以下のようになりました。
いい感じにできたと思います。再照明についても、論文を読みながらの実装だったので少し理解できました。
余談ですが山や谷などの地形画像に対しての再照明を卒論テーマにしようと思っており、今回の実装を参考に色々試行錯誤しています。今回はデータセット作成などについては取り組めなかったので、また機会があったら触れてみたいですね。
参考文献
謝辞
本記事の参考元となる論文の著者である筑波大学の金森教授にポートフォリオとしての掲載許可をいただきました。厚く御礼申し上げます。