PNaClでOpenCVを使う方法

2014年の初めぐらいにOpenCVPNaCl(つまりPPAPIを使うLLVM IR)用にコンパイルできるようになったという話題がありました。たしかにOpenCV公式サイトの1/9のニュースが存在しているのだけど、実際に動かしてみたという例は2ヶ月ほど経った今でも見かけていません。そこで、本当に使えるのかな?という検証も兼ねてキャリブレーション用のちょっとしたツール(Webcam Calibrator)を書いてみた話をします。

PNaCl

PNaClが何であるかについては、http://d.hatena.ne.jp/hdk_embedded/20131128/1385669904とかを読むといいと思います。基本的にはブラウザ内で安全にネイティブコードを実行するための技術です(まだChromeにしか実装されてませんが)。何が嬉しいかというと、

  1. 実行速度が速い
  2. 既存資産の活用

があると思いますが、1についてはjavascriptと数倍しか変わらないわけで、わざわざサポートが薄いPNaClを使うほどの理由になることは少ないと思います。むしろ2のC/C++の資産活用のほうが重要で、画像処理・音声処理・暗号・ゲームエンジンみたいな、別の言語で再実装したくないし、重い/低いレイテンシ要求のある処理をクライアント側でやらせたいと言う理由が大きいと思います。

それで、今回はOpenCVで画像処理をやってみます。

NaCl SDK / NaCl Ports

とりあえずDownload the Native Client SDKあたりを見つつNaCl SDKを入れてチュートリアルは動いたものの、OpenCVをどうやったらPNaCl向けにコンパイルできるのか一向にわかりません。実はCMakeのオプションなどがあるのではなくNaCl Portsというところから持ってこないといけないのでした。

一見すると謎クライアント(gclient)が必要っぽいですが、実はひっそりとgitのミラーがあるのでそこから適当なところにcloneして、あとはREADMEとかに従ってmakeすれば自動的にNaCl SDKの中にライブラリが生成されます。*1

PNaCl使う上での注意点

まず、PNaClは現時点では例外をサポートしていなくて*2、例外が出ると"NaCl module crashed"とかそういう感じです。OpenCVの場合stderrにもエラーを吐いてくれるので、これを見ると良いです。PNaClモジュールからstdout/stderrに吐かれたメッセージは、デフォルトではブラウザ自体のstdout/stderrに出るようです(つまりブラウザをターミナルで開くとそこに出る)。

あと、build方法に癖があります。基本的にはpnacl-clang++で.cpp→.bcして、pnacl-finalizeで.bc→.pexeというフローなんですが、例えばpnacl-clang++で各.cppを.oにコンパイルした後、pnacl-clang++で.oを.bcにリンクするとバイナリは出来るのですが、なぜか実行時にクラッシュしたりします。それならばということで、チュートリアルMakefileを真似して.cppから.bcに直接するコマンドを生成しようとしたりするもののなぜかundefined referenceが出たり*3するので、今のところはMakefileをそのまま使うほうが無難そうです。

例: Webcam Calibrator

ただblurしたりedge検出するぐらいだとそれjsfeatでできるよ!とか言われそうなので、チェッカーボードの検出とキャリブレーションというわりと実装が面倒そうな関数を使う例を作ってみました。それにキャリブレーションはCVとかARでは基本になる操作なので、実用性もそこそこあると思います。

まだ解像度が320x240しか選べないのでパラメーターの精度は低いですが、一応(Chromeでは)動くはずです。

*1:ここが動かないとどうするのかは謎

*2:そもそもGoogleでは歴史的事情によりC++の例外は使っていないらしい。とはいえPNaClでは「すごく近い将来に」サポートする予定とのこと https://developers.google.com/native-client/dev/reference/pnacl-c-cpp-language-support?hl=ja#exception-handling

*3:どうもpnacl-clang++はpythonで書かれたwrapperで、テンポラリファイルをいくつも使ってstripとかしているようなので、そのあたりがSConsと相性が悪い(かもしれない)?