涙目ブログ

アクセスカウンタ

zoom RSS AndroidでOpenCV 後編 ネイティブを使ってAR

<<   作成日時 : 2010/03/04 22:23   >>

ブログ気持玉 0 / トラックバック 2 / コメント 0

AndroidでOpenCVの後編です。
やってみましたが、OpenCVどころではありません。
そもそものAndroidのカメラ制御が悪すぎて手の施しようがありません(T_T)

端末:HTC-magic( Google Developer Day 2009で配られた試作品。 HTC-03Aと同型)
開発環境:Eclipse Version: 3.4.2 Build id: M20090211-1700 + android-sdk-windows-r04 + android-ndk-1.6_r1
開発OS:Windows Vista sp2

命題:
1.Androidのネイティブ環境を整えよ(前編の記事)
2.OpenCVの開発環境を整えよ(中編の記事)
3.ネイティブを使ってARに挑戦せよ(後編の記事)


ネイティブを使ってARに挑戦せよ
結果が見えているのですが、OpenCVどころではないです。
おそらくAndroidのカメラ制御は、写真を撮ることだけを考えて設計されたのだと
思います。
カメラを使用した画像はCamera.PreviewCallbackを使うのですが、このコールバックがされる前に
引数のバイト列の確保でMemoryErrorが発生→コールバックしないのコンボでまともに画像すら取得できません。
(上記はDDMSのLogCatで確認しました)

以降にやったことを記載しますが、androidでカメラを制御の続きと位置付けています。
そのため、上記の記事に目を通し、ある程度は自分でカメラ制御をしてみた人を対象にした記載となります。

やったことリスト
[Android側]
 1 surfaceChangedでint[width * heigth]の配列とBitmapを生成
 2 onPreviewFrameでネイティブに生成したintの配列と引数のバイト列を渡す
 3 intの配列をsetPixelsする。
 4 Bitmapを描画
[ネイティブ側]
 1 decodeYUV420SPを移植
 2 デコードされた値をintの配列に設定


[Android側]
1 surfaceChangedでint[width * heigth]の配列とBitmapを生成
 SurfaceHolderのsurfaceChangedに画面のサイズが渡されるので、そのサイズに合わせた
 intの配列とBitmapを生成します。
 intの配列はネイティブへアウトプット引数として渡し、表示するピクセル値を出力してもらいます。
 Bitmapは以下のように生成します。
 mBitmap = Bitmap.createBitmap( w, h, Bitmap.Config.ARGB_8888 );
 (mBitmapはBitmapをフィルードに宣言したものです)
 この時点でBitmapと配列を生成することで、プレビュー時にはアロケートを一切しないようにします。

2 onPreviewFrameでネイティブに生成したintの配列と引数のバイト列を渡す
 ネイティブのインターフェイスは以下のようにしました。
 public native void nativeTest( int[] work, byte[] data,int w, int h );
 第1引数には1で生成したintの配列、第2引数にはコールバックされた第1引数、
 第3と第4引数は画面の横と縦を設定します。
 これでバイト列と横と縦のサイズをインプットに画像のピクセル値を得ることができます。
 
 戻り値でintの配列を受け取ってもよいのですが、それだとネイティブ側でアロケートが必要となり、
 MemoryErrorで落ちます。

3 intの配列をsetPixelsする
 以下のようにします。
 mBitmap .setPixels( mPixels, 0, mWidth, 0, 0, mWidth, mHeight ); 
 mPixelsは2で生成したint配列、mWidthとmHeightは横と縦を保持したフィールドです)

4 Bitmapを描画
 以下のようにしました。
 Canvas canv = mHolder.lockCanvas();
 canv.drawBitmap( mBitmap, 0, 0, null );
 mHolder.unlockCanvasAndPost( canv );

 mHolderはSurfaceHolderをフィールドに宣言したものです。
 lockCanvasをするのでmHolder.setTypeでSurfaceHolder.SURFACE_TYPE_GPUを設定しました。

[ネイティブ側]
1 decodeYUV420SPを移植
 Issue 823で公開された変換処理をネイティブ側で行いました。
 Issue 823のコメントを見ていると、直すめどは全くないみたいですね。
 Javaから渡されたint配列はenv->GetIntArrayElementsでポインタを取得します。
 同様にバイト列はenv->GetByteArrayElementsでポインタを取得します。
 envとはネイティブの第1引数で、Java⇔C++をする機能を提供してくれます。

 decodeYUV420SPはfor文が2つあるだけで、C++なら
 引数の配列をポインタにすれば問題なく移植できます。
 
2 デコードされた値をintの配列に設定
 int配列はアウトプット引数なので、env->SetIntArrayRegionを使って設定します。
 本来は色々とOpenCVを使って処理をしようと思いましたが、いやな予感がずっとしていたので、
 とりあえずこのレベルでどのくらい動くか確かめたくなり、ここまでとなりました。

動作ですが、Javaのみよりは多少は動きます。
それでもすぐにメモリエラーとなり、コールバック自体がされなくなるので、
フリーズします。
リアルタイムでモニタをして、ARは無理ですね。

おそらく現在で最適な使い方はCamera#takePictureを使い、
静止画に対して1枚のAR画像を作成することですね。
時間は好きなだけかかってもよいので、OpenGLなんかも併用して
写真にすることで意義のあるARを模索した方がいいと思います。
例えば、心霊写真メーカとか。
ちなみに、静止画版で、OpenCVのcvPutTextができることは確認しました。
おそらく他の機能も使えると思います。

最後にTIPSです。
・ネイティブの関数名は最大で64bytes
 →ネイティブ関数名は「Java_パッケージ名_クラス名_関数名」となりますが
  全部で64bytesを超えると、例外が発生します。
・C++の場合は関数の宣言にextern "C"が必要
 →C++の関数名は引数なども自動でついてしまい、Javaから見ると異なる名前になります。
  そのため関数の先頭にextern "C"を追記し名前が変わらないようする必要があります。
・int列→IplImage、IplImage→byte列のサンプル
 →中編でダウンロードしたzipファイルには上記のndkも含まれています。
   サンプルにいかがでしょうか。
   Android→IplImage→Androidと画像がうまくリレーできれば、心霊写真メーカも夢ではありません。

と、ここまで書いて、カメラアプリはどうしているのか気になりました。
カメラアプリを起動すると、startPreviewした時っぽい画像に部品が浮いています。
画像に合成するのは無理でも、GUI部品を重ねることはできそうです。
ARは無理でもsekai cameraみたいなことはできそうですね。

テーマ

関連テーマ 一覧


月別リンク

ブログ気持玉

クリックして気持ちを伝えよう!
ログインしてクリックすれば、自分のブログへのリンクが付きます。
→ログインへ

トラックバック(2件)

タイトル (本文) ブログ名/日時
AndroidでOpenCV 中編 OpenCV開発環境設定
AndroidでOpenCVの続きです。 中編て変な響きですね。 序破急とかにすればよかったかも(T_T) ...続きを見る
涙目ブログ
2010/03/04 22:25
AndroidでOpenCV 前編 ネイティブ環境設定
だいぶ前に、Androidのカメラが使えない記事を書きましたが、 それはJavaのみで、ネイティブは使っていませんでした。 いつかネイティブを試そうと思うついでに、そのネイティブとやらを使えばOpenCVも動作するとのことで、 挑戦してみます。 バグもあるし、あのインターフェイスじゃ変わりようがないとは思いますが(T_T) ...続きを見る
涙目ブログ
2010/03/04 22:27

トラックバック用URL help


自分のブログにトラックバック記事作成(会員用) help

タイトル
本 文

コメント(0件)

内 容 ニックネーム/日時

コメントする help

ニックネーム
本 文
AndroidでOpenCV 後編 ネイティブを使ってAR 涙目ブログ/BIGLOBEウェブリブログ
文字サイズ:       閉じる