OpenCV for Androidでキャプチャしたカメラ画像にフレームを合成

卒業研究も無事に終わり、中途半端に終わってしまったソースコードの手直しとかしてました。

卒業研究周りはこの辺がもう少し落ち着いたらまた手を付けて行きたいなぁと。

その時に解決できた事柄を1個メモ。

 

OpenCV for Android 2.4.6のサンプル OpenCV Tutorial 1 -Camera Previewを使って枠付きの写真を撮影する作ってみます。

 

自分がメモしたいと思ったところのみに絞るのでプロジェクトの追加方法は割愛します(閲覧している方は0に近いと思いますが、もし気になる方いらっしゃればご一報を。追記します)

 

Tutorial1Activity.javaの中にOnCameraFrameメソッドがあります。

この中に記述を追記することで、表示するカメラの映像に編集を加えます。

 

   public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

 

        return inputFrame.rgba();

    }

 

 元々これだけの記述しかありませんが、これを色々変えていじります。

 

かぶせる枠はresフォルダ-drawableフォルダの中へ。

 

f:id:eight-seven:20140225053601p:plain

 

こんな具合です。sunset.pngが枠の画像です。

以下のコードを記述することでsunset.pngの画像ファイル(PNG)をビットマップとして取り込むことができます。(要import android.graphics.BitmapFactory;記述)

 

      Resources r = getResources(); 

      Bitmap bmp_frame = BitmapFactory.decodeResource(r, R.drawable.sunset);

 

 

例えば、今回のsunset.pngというファイル名であれば、

R.drawable.sunset

という名前のidの変数をdecodeResource関数のid部分に記述します。

 

後で画像を合成する際に、サイズを合わせる必要があるため

Bitmap型の時点でサイズの変換を行います。今回取り込むカメラ画像は640x480なのでそれに合わせます。 

 

     bmp_frame = Bitmap.createScaledBitmap(bmp_frame, 640, 480, false);

 

これでPNGの画像はBitmap型になります。 

しかしBitmap型のままでは合成ができないので、bitmapToMat関数を使ってMat型に変換します。(要import org.opencv.android.Util;記述?<筆者はimport.org.opencv.android.*;で対処>)

 

 

     Mat mat = new Mat(3,3,CvType.CV_32F);

     Utils.bitmapToMat(bmp_frame, mat);

 

 

変換した後の置き場として変数matを作りました。

ちなみにブランクのMatを作ろうとしてずっとnullでやってエラー吐いてました...

ちゃんとコンストラクタで定義しましょう。(恥ずかしい)

 

 

     Mat mat2 = new Mat(3,3,CvType.CV_32F); 

     mat2 = inputFrame.rgba();

 

 

カメラ画像用のMatも用意しておきます。このサンプルコードだと、

 

   inputFrame.rgba();

 

でカメラ画像のMatを取得することができます。

 

そして、Coreクラスのaddメソッドで2つのMatを合成します。

 

  Core.add(mat2, mat, mat);

 

ここまでの流れをざっと示すと以下のソースコードになるはずです。

 

   public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

    

     Resources r = getResources(); 

     Bitmap bmp_frame = BitmapFactory.decodeResource(r, R.drawable.sunset);

     bmp_frame = Bitmap.createScaledBitmap(bmp_frame, 640, 480, false);

 

     Mat mat = new Mat(3,3,CvType.CV_32F);

     Utils.bitmapToMat(bmp_frame, mat);

 

     Mat mat2 = new Mat(3,3,CvType.CV_32F); 

     mat2 = inputFrame.rgba();

     

   Core.add(mat2, mat, mat);

                  

        return mat;

    }

 

これを記述して起動するとこんな感じになります。枠の画像は某フォロワさん作。

 

f:id:eight-seven:20140225060341p:plain

 

んー、このコードだと半透明になってしまうのが...元の画像にアルファ値含まれているんだろうか。

アルファ値削除か、マスク画像使う手法を試していないので後でやってみます。 

 

 

============================

 【ここから反省会】

Matを合成する部分?でエラーを吐いていたのですがその原因が分からず滞っていました。

こんなの。

02-22 04:02:22.634: E/cv::error()(18106): OpenCV Error: Assertion failed (src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols) in void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean), file /home/reports/ci/slave_desktop/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp, line 97

02-22 04:02:22.634: E/org.opencv.android.Utils(18106): nMatToBitmap catched cv::Exception: /home/reports/ci/slave_desktop/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp:97: error: (-215) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean)

02-22 04:02:22.634: E/CameraBridge(18106): Mat type: Mat [ 825*675*CV_8UC4, isCont=true, isSubmat=false, nativeObj=0xab5160, dataAddr=0x5d916010 ]

02-22 04:02:22.634: E/CameraBridge(18106): Bitmap type: 640*480

02-22 04:02:22.634: E/CameraBridge(18106): Utils.matToBitmap() throws an exception: /home/reports/ci/slave_desktop/50-SDK/opencv/modules/java/generator/src/cpp/utils.cpp:97: error: (-215) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function void Java_org_opencv_android_Utils_nMatToBitmap2(JNIEnv*, jclass, jlong, jobject, jboolean)

 

これが、2つのMatファイルのサイズの相違を示していたなんて...

 

02-22 04:02:22.634: E/CameraBridge(18106): Mat type: Mat [ 825*675*CV_8UC4, isCont=true, isSubmat=false, nativeObj=0xab5160, dataAddr=0x5d916010 ]

 これが枠の画像(825*675)で

 

02-22 04:02:22.634: E/CameraBridge(18106): Bitmap type: 640*480

こっちが取り込んだカメラ画像(640*480)と。
 
今回は

 

     bmp_frame = Bitmap.createScaledBitmap(bmp_frame, 640, 480, false);

 
の記述を追加して、枠の画像をカメラ画像に合わせて対処しました。
もっとOpenCVしっかり勉強しないとなぁ。