2016年8月31日水曜日

画像端処理

以前書いたCombMaskの画像端処理についての質問が来ていたので記事にして解説することにしました。

例えば縦方向のみで半径2の平均化フィルタを書くとします。
このとき各サンプルの配置は下図のようになりますが、
ここで問題になるのは画像の上端及び下端をどうするかです。
参照する値が五つ揃わなければ(sa+sb+sc+sd+se)/5は出来ないので、足りない分をどうにかして補う必要があります。

ここで考えられるのは以下のようなものとなります。

パターン1:画像端は処理しない。
面倒なことは避けるという極めて常識的な方法です。
上のような場合は画像の上下各2ラインはそのまま入力値をコピーし、3行目から下端-2行目までの計算を行います。
この方法は半径が小さい場合は特に悪くはないのですが、半径が大きくなるとフィルタがかからない部分も大きくなりますし、重ね掛けをすると未処理のままの画像端の値の影響が大きくなっていく欠点があります。

パターン2:足りないところは0として計算する。
「ないものはないんだから0でいいだろう」という身も蓋もない方法です。
たしかにないものはないのだから、ある意味正しいのかもしれませんが、これはパターン1以上に問題があります。
特に重ね掛けをすると画像端の劣化は激しく、RGBなら真っ黒、YUVなら緑色に染まっていくことになります。
なんでもOpenCVのガウシアンブラーには、まさにこれが起こるのがある(あった?)みたいですね。

パターン3:画像端の色でパディング
存在しないからと言って0として扱うのは問題があり過ぎなため、画像端も処理したい場合にはよくとられる方法です。
存在しないところは境界の色がそのまま続いていると考えるわけですね。
これならばとりあえず全体を処理することが可能ですし、真っ黒になったりもしません。
ただし、半径が大きくなればその分だけ画像端部が参照されることも多くなるため、そこら辺の考慮は必要になります。

パターン4:ミラーリング
パディングの欠点を補うため、存在しない部分は上下(または左右)が逆になった画像がくっついているものとして処理する方法です。
これであれば画像端も処理できますし、上端だけが何度も参照されるということもそこそこ避けることができます。
今回来た質問は
> const uint8_t* sc = srcp;
> const uint8_t* sb = sc + spitch;
> const uint8_t* sa = sb + spitch;
> const uint8_t* sd = sc + spitch;
> const uint8_t* se = sd + spitch;
この部分で、sbとsdは同じものになってしまうのではないでしょうか。
というものでしたが、これも一種のミラーリングを行っているのですね。
CombMaskはインタレ映像のコーミングを見つけるわけですが、画像上端では次のような状態になりますので、saとsbはそれぞれseとsdと同一の点を参照するようにしているのです。

と、ざっと画像端処理について書いてみました。
画像端処理の問題は、多くの空間軸処理において起こります。面倒ではありますがなにかしら対策しないわけにもいかないので、どれにするのかよく考えて行いましょう。

1 件のコメント:

  1. 解説画像まで作っていただき有難うございました。
    たいへん分かりやすかったです。

    返信削除