写真を加工する 5.6 高度なフィルタの実装と高速化(備忘録)

#wp7dev_jp

これまで、様々な画像フィルタを作ってきて、それを組み合わせてフィルタ効果を実装した。

Photochrome (13)

例えば上記の効果を作るために、フィルタ処理を重ねて実装している。ここでは、レベル補正と→単色乗算→トーンカーブをかけている。

 int[] tmp;
uint color = 0xfff6ddad;

float[] tc = ToneCurve.getcurve(0, 39, 75, 70, 167, 203, 255, 255);

tmp = Level.effect(src, w, h, 0, 0, 128);

tmp = SolidColorMix.effect(tmp, color, w, h);

tmp = ToneCurve.effect(tmp, w, h, tc, null, null, null);

return tmp;

ま、Photoshop の処理をプログラムで実現しているわけで、パラメータを変化させればいろいろな効果を生み出せる。

が、気が付いた。実装にはこんなことをやる必要はない。だって、Photoshop を作るわけではないんだから。

高速化

上のフィルタは、所詮完成品。アプリの中でパラメーターを変えることはしない。だったらもっとシンプルになりそう。

色の入力は所詮 RとGとBの3色で、それぞれ 0~255の256段階しかない。 フィルタをかければ、その色が変わっていくだけ。

例 R=30 → レベル補正 → 142 → 乗算 → 96 → トーンカーブ → 80

間はいろいろあるが、結局まとめると、こうなるだけだ。このパターンを 256 色分x3色分用意すればいい。

例 R=30 → 処理 → 80

こうすれば、あとは変換1回で処理が終わる。 ということで上記の処理はこうなってしまった。

 int[] tmp;
int[] TR = new int[] { 39, 39, 39, 39, 39, 39, 39, 39, ... 251, 252, 252 };

int[] TG = new int[] { 39, 39, 39, 39, 39, 39, 39, 39, ... 242, 243, 243 };

int[] TB = new int[] { 82, 82, 82, 83, 83, 85, 85, 85, ...  208, 208, 209 };

tmp = Convert.effect(src, TR, TG, TB);

return tmp;

画像処理の高速化のコツ

これはそんなに難しくない。

  • 計算を減らす
  • 浮動小数点計算よりも整数型の計算にする
  • 掛け算よりもビット処理

例えば、30%の濃さにする場合、RGBx0.3 でどうしても浮動小数点演算になりそうになる。

→ 計算時に 1000倍くらいして、整数にして、最後にまた1/1000にする。これで結構十分。

更に、その掛け算割り算もビット処理で行う。

→ 1000倍ではなく1024倍にする。そして終わったら1024で割る。

→ 1024 の割り算掛け算は 10ビットの移動処理でできるので、高速化が見込める。

  • (int)((float)a x 0.3) → ((int)a x 307) >> 10 ;

 

どのくらい高速化するのか?

これまで、全店に対して、フィルタ1回ずつ、場合によっては浮動小数点演算を行ってきた。このフィルタの場合、約0.3-0.4秒かかっていた。

これが高速化処理によって、整数型の変換テーブルで変換するだけなので、結果として処理時間は 約 0.02秒となった。約1/20秒の短縮化である。

デメリットはある。

  • あとから処理を微調整できない
  • そもそもフィルタ処理から変換テーブルを作るのが面倒

そんなわけで、フィルタ処理から変換テーブルを作るための、別のアプリを作らないとだめそう。ま、仕方がない。

全てにおいて有効か?

単純にこれでできないものもある。1:1の関係にならないもの。

  • 例えば、白黒化は RGBの3色を必要とするので、テーブルにすると 256x256x256 のサイズになる。これは計算のほうが早い。
  • そしてぼかし処理。これは、1点を決めるのに周りの点の情報が必要となるので、どうにもならない。

特にぼかし処理は時間がかかる(2,3秒)ので、読み込み時に作っちゃいます。ボタンを押してから2,3秒は待てないけどファイル読み込み時のどさくさに紛れて2,3秒は結構許されるんですよね。

関連リンク