OpenCVを使ってスマホのカメラプレビューで取得する画像にリアルタイムでエッジ検出処理をやってみました。
こんな感じです。
なんかロトスコープアニメーション見たいになります。
エッジ検出は、OpenCVを利用しCannyエッジ検出しています。それで得られた画像を白黒反転させて表示しています。
ざっくりやり方を説明すると、
・AndroidStadioプロジェクトにOpenCV SDKをインポート
・カメラプレビュー画像を取得(Camera.PreviewCallback)
・取得した画像(NV21フォーマット)をグレースケールMat形式に変換
・Canny変換
・変換した画像を表示
といった手順です。
カメラはAPI21以降はCamera2が推奨されていますが、今回は使用していません。
エッジ画像はカメラプレビューを表示するSurfaceView上に別Viewをオーバーラップして表示させています。
本当はSurfaceViewにそのまま描画したかったのですが…やり方が分からず断念しました。
※SurfaceTextureを使えばもっとシンプルになるかもですが勉強不足です
OpenCV SDKのインポート
初めにAndroidStudioプロジェクトにSDKをインポートします。
1.[File]→[Import Module…]
2.”OpenCV-2.4.10-android-sdk/sdk/java”をインポート
3.Appプロジェクトのbuild.gradleに下記を追加
dependencies { ... compile project(':openCVLibrary2410') }
NV21→Mat→Canny検出
Camera.PreviewCallbackで取得できる画像フォーマットはデフォルトではNV21になっています。
これをグレースケールのMatに変換するには、NV21のYデータだけを持ってくれば可能です。
※フォーマットのデータ構造などは検索するとすぐ出てくると思います
ざっくりポイントだけを下記に記載します。
@Override public void surfaceChanged(SurfaceHolder holder,int format, int width, int height) { ... Parameters params = mCamera.getParameters(); Size prevSize = params.getPreviewSize(); mGray = new Mat(prevSize.height, prevSize.width, CvType.CV_8U); //プレビューサイズ分のMatを用意 mGray90 = new Mat(prevSize.width, prevSize.height, CvType.CV_8U); //今回はポートレイト+フロントカメラを使ったので画像を回転させたりするためのバッファ ... } @Override public void onPreviewFrame(byte[] data, Camera camera) { mGray.put(0, 0, data); //プレビュー画像NV21のYデータをコピーすればグレースケール画像になる Core.flip(mGray.t(), mGray90, 0); //ポートレイト+フロントなので回転 Core.flip(mGry90, mGray90, 1); Imgproc.Canny(mGray90, mGray90, 50.0, 100.0); Core.bitwise_not(mGray90, mGary90); //白黒反転 Bitmap bmp = Bitmap.createBitmap(mGray90.cols(), mGray90.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(mGray90, bmp); if (mCallback != null) mCallback.onConvertedFrame(bmp); }
かなりざっくりしたコードですいません。
結構ハマったところでもあり、いろいろ試していたらこんな感じなってしまいました。
もっと賢いやり方もあると思います。
ハマったところ:onPreviewFrameが呼ばれない
カメラプレビュー画像の表示はいらないのでmCamera.setPreviewDisplay(holde)は消していたのですが、ないとonPreviewFrameが呼ばれないようです。
ハマったところ:onPreviewFrameでlockCanvas()できない
カメラのプレビュー表示で使っているからかな?できません。
あとがき
スマホカメラの動画をリアルタイムで画像処理するとどんな感じになるか(主に処理負荷的に)を検証するために作ってみました。
GalaxyS4で大体10fps@640×480で処理できたので、まぁやりたいことは出来そうかな。
今回はかなり適当な内容ですいませんが以上です。
OpenGL ESの勉強もした方がいいのかも。
ではでは〜。