深層学習を使って 単一画像で反射を除去する

AI

GMOグローバルサイン・ホールディングスCTO室の@zulfazlihussinです。
私はhakaru.aiの開発チームのAI開発を担当しております。この記事では、単一画像で反射を除去する方法について調べてみましたので、共有したいと思っております。

画像の反射の問題

ガラス越しの撮影をしたときに、ガラスの反対側の背景が映り込み、ガラスの中にある物体が見えなくなることはないでしょうか?

画像認識のときも同じですが、実用化には画像での反射の対策が不可欠です。画像に反射した背景が映り込んだときに、実際に画像認識で識別したい物体がの輪郭が分からなくなり、物体の判別が難しくなります。次の画像の例を見てみましょう。


Fig. 1 反射ありの画像(左)と画像処理でのエッジ検出の結果(右)

Fig. 1 では、背景の反射で ”予告” の文字が見えづらくなってしまいました。実際にエッジ検出の結果でも分かるかと思いますが、”予告” の文字の輪郭を完璧に検出することができなくなってしまいました。エッジ検出の処理は次のコードのように実施します。

import numpy as np
import cv2
import matplotlib.pyplot as plot

ori_img = cv2.imread(file)
img_rgb = cv2.cvtColor(ori_img, cv2.COLOR_BGR2RGB)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
img_gray_blur = cv2.GaussianBlur(img_gray, (9, 9), 0)

kernel = np.ones((5,5),np.uint8)
img_gray_blur = cv2.morphologyEx(img_gray_blur, cv2.MORPH_CLOSE, kernel)

img_gray_sobel_x = cv2.Sobel(img_gray_blur, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # ksize=-1 if use Scharr
img_gray_sobel_y = cv2.Sobel(img_gray_blur, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=-1)

img_gray_sobel_x = cv2.convertScaleAbs(img_gray_sobel_x)
img_gray_sobel_y = cv2.convertScaleAbs(img_gray_sobel_y)
img_gray_sobel_xycombined = cv2.addWeighted(img_gray_sobel_x, 0.5, img_gray_sobel_y, 0.5, 0)

fig, ax = plot.subplots(1,2)
ax[0].imshow(img_rgb)
ax[1].imshow(img_gray_sobel_xycombined)
plot.show()

どうやって反射を除去するのか

単一画像で反射を除去するために、Chao Li et al.[1] が提案された手法を試してみました。この手法では反射のノイズが含まれている画像を 反射なしの画像(Transmission)と反射した背景の画像(Reflection)、2枚の画像に分離し、反射を除去します。

これは、反射した背景は焦点が合ってないと仮定し、映り込む画像のエッジの強度が弱いという性質があります[2]。これを利用して、輝度や色味など変化させることで、反射した背景の”強さ”を最大化し、分離しやすい状態を作り上げることができれば画像の反射を除去することができるようになります。

次の写真を見てみましょう。この写真は、Chao Li et al.で使った学習の時のイメージです。一番右の画像TRはそれぞれ反射なしの教師データの画像と反射した背景の教師データの画像です。

最初のステップ T0 では入力した画像のそのままです。R0 では、学習が進んでいないため、黒色の背景の画像になっています。そして、T1 へ学習を進んだとき、教師データ T に同じになるように学習します(反射した背景の特徴量の重みを最小化する)。一方、R1 では T1 と逆で、反射した背景の特徴量の重みを最大化し、反射した背景の教師データ、R に近づけます。これを繰り返すことで、反射なしの画像と反射した背景の画像を分離することができます。


Fig. 2 Chao Li et al.で使った学習の時のイメージ

実装

実際に入力画像から反射した背景を除去してみます。今回はChao Li et al.が公開したソースコード[3]を使います。今回は、ソースコードの中に既に作って頂いたモデルがありましたので、学習を実施せずに実行します。まずは、レポジトリをダウンロードします。

git clone https://github.com/JHL-HUST/IBCLN.git

今回はFig. 1 の画像をテストデータとして使います。テストデータはテスト用フォルダーへ移動します。

cd IBCLN
cp ~/img1.png datasets/reflection/testB/

テスト用の実行スクリプトを実行してみましょう。今回は、GPUを使っていないので、gpu_idsは-1で設定します。

python test.py --dataroot datasets/reflection --name IBCLN --model IBCLN --dataset_mode resize_natural_3 --preprocess "" --no_flip --epoch final --gpu_ids -1

結果

これは反射の除去処理を行った結果です。先ほどの2枚のテスト画像、Fig. 1 に含んでいる反射した光が除去されたことが分かりますでしょうか。

反射した光が除去したことで、Fig. 1 の画像の文字 ”予告” が処理前より見えやすくなっています。

Fig. 3 反射を除去する流れ:Transmissionの画像(上)、Reflectionの画像(下)

反射を除去した画像を使って、エッジ検出処理を実施してみました。Fig. 1の文字 ”予告” のエッジが問題なく検出することができました。

終わりに

今回の記事は以上です。
最後までお読みいただきましてありがとうございました。

参考

[1] C. Li, Y. Yang, K. He, S. Lin and J. Hopcroft, “Single Image Reflection Removal Through Cascaded Refinement,” in 2020 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), Seattle, WA, USA, pp. 3562-3571, 2020

[2] Y. Li and M. S. Brown, “Exploiting reflection change for automatic reflection removal,” in Proc.
IEEE Int. Conf. Comput. Vis. (ICCV), pp. 2432-2439, 2013.

[2] https://github.com/JHL-HUST/IBCLN