こしあん
2019-07-20

画像のダウンサンプリングとPSNRの測定

Pocket
LINEで送る


U-Netでどこまでの深さが効いているのかを考えるために、画像をダウンサンプリングし、アップサンプリングするという処理を行いPSNRを見ていきます。その結果、PSNRが15~20程度だと、U-Netの深い層が効かないかもしれないという状況を確認することができました。

きかっけ・考え方

U-Netの実装を考えるとき、DecoderにおいてSkip-Connection側の入力が効いているのか、深層側の入力が効いているのかよくわからないことがあります。そこで、どっちが効くかの指標として「画像をある層での解像度にダウンサンプリングしたときに、PSNRで測ってどれぐらいになるか?」を調べるのが有効ではないかと思ったのです。

どういうことかというと、次のような方式です。

  • 画像(例:256×256)をある層の解像度にあうように、ダウンサンプリング(例えば32×32)とする。具体的にはAveragePooling。
  • ダウンサンプリングした画像を元の解像度にアップサンプリング(Nearest Neighbor法)する
  • 当然ダウンサンプリング→アップサンプリングした画像は画質が明らかに悪くなっているので、その画像と元の画像とのPSNRを取る
  • このPSNRを解像度別(32×32, 16×16など)取って比較する

これがなぜU-Netにおいて意味あるかというと、

  • ある解像度において求められたPSNRを$p_i$とする
  • ネットワークの出力でのPSNRを同じく見る
  • 出力のPSNRが$p_i$より低いか同程度なら、$i$に対応する層でより深い層の出力ではなく、Skip-Connection側が効いていると考えられる。なぜなら、より深い層を頑張って訓練するより、Skip-Connectionの入力をコピペしたほうが楽だから。
  • 出力のPSNRが$p_i$より高ければ、$i$に対応する層でより深い層の入力が効いていると考えられる。なぜなら、SkipConnectionの入力をコピペしただけでは、その画質は再現できないから。

したがって、訓練を通さないにしろ、$p_i$がU-Netにおいて深い層が効くか効かないかのある種の目安として扱うことができます。これはSkip-Connection側が効くか、深層側が効くか結果を見てみないとわからないU-Netにおいて結構有効ではないでしょうか。

実験

前処理

KaggleのMyWaifuListデータセットを使います。前処理としてカラー画像のみを抽出しました。カラー画像の抽出は、チャンネル単位でのピクセルの相関係数を取り、その相関行列の平均が0.995より低いものをカラー画像としました。もともと別の訓練の過程で作ったデータなので、その中から2048枚をテスト画像として除外しています。訓練画像12443枚についてPSNRの計算をします。

これらの画像について、256×256にダウンサンプリングします。LANCZOS法によってリサイズします。この画像を真の画像(img_true)とします。

比較実験

真の画像img_trueを以下の解像度にAvg Pooling→Nearest Neighborのアップサンプリングをします。これをPU変換と呼ぶことにします。

  • 128×128 (2倍)
  • 64×64 (4倍)
  • 32×32 (8倍)
  • 16×16 (16倍)
  • 8×8 (32倍)

もとの解像度で割った値をスケール倍率とします。スケール倍率が高いほど画質は落ちます。PU変換後の画質ばスケール倍率が高いほど落ちます。PU変換後の解像度はimg_trueの解像度256×256と一致します。PU変換後の画像に対して$p_i$を計算します。

この説明では、「真の画像img_trueとimg_trueをPU変換した画像のPSNR」を求めましたが、U-Netにおいては入力≠img_trueであることが普通なので、PU変換をする画像に別の前処理を施します。

  1. 真の画像img_trueをグレースケール化する(ただし3チャンネルのまま)。これをimg_grayとする。img_grayに対してPU変換をし、img_trueとのPSNRを取る。これは白黒画像のカラー化でのタスクを想定。
  2. 真の画像img_trueにモザイクをかける。モザイクの掛け方は後述。これをimg_mosaicとする。img_mosaicに対してPU変換をし、img_trueとのPSNRを取る。これはU-Netによるモザイク除去のタスクを想定。

また、モザイクをかける際は次のようにします。

  • img_trueに対して、16倍のスケール倍率でPU変換
  • 半径4ピクセルのガウシアンぼかしを使う

これにより生成されるのがimg_mosaicです。

すべての画像において、PSNRを計算するときは比較対象はimg_trueを対象にします。したがって、img_trueのPU変換よりも、img_gray, img_mosaicのほうがPSNRの値は低くなります。

可視化

img_true、img_gray、img_mosaicのPU変換をスケール倍率ごとに可視化したものがこちらです。上から、「本物、スケール倍率2倍、4倍……」です。

img_true

img_gray

img_mosaic

おおよそイメージがつかめたのではないでしょうか。スケール倍率を上げれば上げるほどモザイクがかかったような出力になります。

結果

画像別の結果を示します。

スケール倍率 img_real img_gray img_mosaic
1 19.85 15.58
2 24.61 18.65 15.58
4 20.77 17.39 15.55
8 18.32 16.19 15.41
16 16.31 14.97 15.17
32 14.57 13.74 14.22

img_realの場合は、8×8解像度まで落とすとPSNRは15を切ることがわかりました。32×32ならPSNRは20を越えないぐらい。訓練の進むとして評価関数にPSNRを使うことはよくあるので、これはわかりやすいです。

img_grayの場合は、img_realのカラー情報を落としているため、PSNRはより低くなります。しかし、低解像度領域においてはそこまでPSNRの差は出ません。スケール倍率が8より大きいと、PSNRでの差は2よりも小さくなります。これは驚きでした。カラー情報がPSNRの決定的な差になるのはスケール倍率が低い、つまり高解像度領域となります。

img_mosaicの場合は、そもそも前処理でPU変換をしているため、PSNRは頭打ち傾向にあります。もちろん、前処理のPU変換が16×16解像度まで落としているので、スケール倍率が16までの差はほぼ誤差のようなものです。32まで倍率を上げると、ガクッとPSNRが落ちます。

考察

U-Netを使った画像変換において、PSNRが15~25ぐらいというのは結構あるケースなのですが、それはもしかするとこれで見たように低解像度のSkip Connectionによるコピーで再現できるかもしれません。したがって、深い層の訓練が進まない可能性は考えられます。どこまでの深さが訓練されるか、あるいは意味あるのかを見るのは、こういった考察をするのが簡単かつ有効かもしれません。

可視化の部分で確認したように、PU変換というのはやっていることはモザイクの付与であるため、モザイクについて考えることがU-Netの挙動を考える際に有効となるやもしれません。一見ふざけているようにみえるモザイクというのは、画像処理の本質的な部分を突いている、と言えるのではないでしょうか。

Related Posts

Google ColabのTPU環境でmodel.fitのhistoryが消える現象... Google ColabのTPU環境でmodel.fitしたときに、通常の環境で得られるhistory(誤差や精度のログ)が消えていることがあります。その対応法を示します。 原因はTPU用のモデルに変換したから まず結論からいうとこの現象はCPU/GPU環境では再発しません。TPU環境特有の現...
PyTorchで行列(テンソル)積としてConv2dを使う... PyTorchではmatmulの挙動が特殊なので、思った通りにテンソル積が取れないことがあります。この記事では、基本的な畳み込み演算である「Conv2D」を使い、Numpyのドット積相当の演算を行うという方法を解説します。 はじめに PyTorchの変態コーディング技術です。多分。 画像のテ...
Pillowでグレースケール化するときに3チャンネルで出力するテクニック... カラー画像をグレースケール化すると1チャンネルの出力になりますが、カラー画像と同時に扱うと整合性からグレースケールでも3チャンネルで欲しいことがあります。Numpyのブロードキャストを使わずに簡単に3チャンネルで出力する方法を書きます。 グレースケール化してカラー化すればOK これでOK i...
OpenCVのアフィン変換でAssertion failed OpenCVのアフィン変換のgetAffineTransformで、起点と終点の行列をちゃんと正しいshapeで指定しているのにもかかわらず「(-215:Assertion failed)」とエラーになってしまいました。かなり難解なエラーだったので、原因を探ってみました。 このコード アフィン...
Kerasでメモリ使用量を減らしたかったらmax_queue_sizeを調整しよう... Kerasで大きめの画像を使ったモデルを訓練していると、メモリが足りなくなるということがよくあります。途中処理の変数のデータ型(np.uint8)を変えるのだけではなく、max_queue_sizeの調整をする必要があることがあります。それを見ていきます。 メモリサイズの目安 ニューラルネット...
Pocket
Delicious にシェア

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です