Rest Term

ANEで画像処理

Pocket

前回のエントリー、FlashDevelop + GCCでANE入門の続きになります。今回はANE(ActionScript Native Extensions)で画像処理を行う際のいくつかの注意点などをメモしていこうと思います。ANEの作成手順については前エントリーを参照してください。

ANEのC APIでは、ASのBitmapDataを扱う為の構造体 FREBitmapData というものが提供されています。

OpenCVでいう簡易IplImageのような構造です。学生時代にやったプログラミング演習とかでこんな構造体作って画像触ってた気がします。懐かしい。

で、あれ?座標のオリジンはどこ見ればわかるの?と思ったらなんとAIR 3.1から FREBitmapData2 という構造体が新しく追加されているようです。

oh... 本当に学生のプログラミング演習課題の再提出みたいな、そんな対応。

新しく追加された isInvertedY というメンバが座標のオリジンを示しているようですが、この行き当たりばったりな対応を見る限り、Adobe内でリソース配分(人材他)に苦労してる様子がわかります。

しかもシビアな画像処理をいくつか試していた中で気付いたのですが、FREBitmapData に画像データをバインドする際にそのデータが壊れている時があったので(原因不明、単一色で塗りつぶした画像を渡してネイティブ側で全ピクセル値をログに出してみると(A, R, G, B) = (0, 0, 0, 0)で入っている画素がたまに出現した)、FREByteArray に画像データを入れてwidthとheightを併せてネイティブ側に渡す方針に変更しました。AS側での BitmapData#getPixels/setPixels と、ネイティブ側でのバイトオーダー判定等のコストが余計にかかってしまいますが、いくらか安全です。

画像にどこまで触れるかを知りたいだけなので、ここではソフトフォーカス風にレタッチするフィルタをANEで作成してみます。前回はPure Cで書いたのですが今回はC++を使います。

* 画像データバッファ用クラス (bitmap.hpp)
C++0xの std::tuple を利用していますが、普通に構造体でRGB値を扱った方が無難かも。

デストラクタで FREReleaseByteArray() を呼び出して画像データをアンロックします。あとはデータのアドレス計算部分を隠蔽してアクセサを提供しているくらいの薄いラッパーになっています。

* ソフトフォーカスフィルタ (effect.cpp)
フィルタ処理の実体はPhotoshopでいうところの、元画像のレイヤーをコピーしてぼかしフィルタをかけ、それと元画像をスクリーンブレンドしたものと同じです。また、バイトオーダーがリトルエンディアンじゃなかったら何もしないという手抜き。。

C++0xの nullptr を使っていますが、もしC++0xを使わない場合は定数0を返す方法が良いです。NULLマクロはC++ではあまり推奨されていません。

また、この場合はコンパイラの最適化オプションを付けてコンパイルしないと、インライン展開などの最適化が行われずアセンブラコードが肥大化してパフォーマンスが低下してしまいます。なのでgccなら -O3 あたりを付けてコンパイルすることをオススメします。逆に最適化オプションを付けた場合、メモリアドレスを直接ガリガリ計算したコードと比較しても実行効率はほとんど変わらないことを確認しました。最近のGCCはすごいです。

AS側で特筆すべきところはありません。

左が元画像、右がフィルタ適用後 (カーネルサイズ 9×9, 輝度オフセット -50)
※ ブログ用にJPEG圧縮してるので、実際はもっとふわっと(?した効果が出ています。

下の画像はミッドタウンのガレリア1Fから。クリスマスモード全開ですね。

パフォーマンスについて

実行速度はカーネルサイズが3×3程度の小ささだとデータ転送のコストが高く付いてASオンリーの方が速かったですが、7×7よりも大きなカーネルならANEを使った方が速かったです。空間効率的には小さなバッファをずらしながら畳み込みとブレンド処理を同時に行えばBitmapData全体のコピーは不要なので効率は良くなります。

バッファ用クラスを用意せずにユーザーコード内で生ポインタのアドレスをガリガリ計算する従来の書き方でももちろんOKです。C++は恐いけどCなら大丈夫という方にとってはわかりやすいかもしれません。VMがメモリ上で画像データが置かれているチャンクを移動させないようにロックしてるはずですが、画素操作が終わった後に FREReleaseByteArray() でアンロックするのを忘れないように注意。また、前述のコードより実行効率が良さそうに見えますが(コードの見た目から判断する限り)、最適化オプションを付けてコンパイルすれば実行効率の差はほとんど消えてなくなるので個人的にはオススメしない書き方です。

* 従来のC Likeな書き方

さすがに原始的すぎるのでANE C APIだけを使ったライブラリを作るのはオススメしません。まともな画像処理をしたい場合は積極的に外部ライブラリを使っていきたいところです。例えばOpenCVの cv::Mat あるいは IplImage フォーマットに相互変換できる小さなアダプタ関数だけ用意して画像処理自体はOpenCVに任せた方がいいかと思います。

ANEを使ったAndroidアプリも作っていたのですが、インラインアセンブラでARM命令をぺたぺた書いたコードよりもC++で素朴に書いたコードの方が速い場合が多かったのでショックでした。「最適化はコンパイラに任せた方がいい」という意見を身に染みて実感。ただ、NEON命令(Advanced SIMD)を使えばさすがにもうちょっと速く動作すると思うのでそれも検証しておきたいです。

おまけ: FlashDevelopでのANE開発時の作業効率化

ANE開発で必要なファイル生成/配置などの作業はバッチで自動化させると楽ですが、圧縮ファイルの解凍は Lhaplus.exe を直接コマンドラインから叩けばできることを知ったのでこれをバッチに組み込んでいます。手動でaneを展開してextdirフォルダに置いたり、swcからlibrary.swfを取り出す作業も含めて自動化できます。

あわせて読む:

Pocket

 

Tags: , ,

Comments

No comments so far.

  • Leave a Reply
     
    Your gravatar
    Your Name