ここ最近はAndroidアプリ開発の勉強をしています。今回はOpenCVでデバイスのカメラを利用した動画像処理を試してみました。
技術Wikiの方にもAndroid関連のメモを残しています。
* Android – Tech Note
* OpenCV for Android – Tech Note
Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.
環境
* OpenCV4Android SDK 2.4.4 (今回はJava APIのみ使用)
* 検証デバイス: Galaxy S2 LTE(Android 2.3.6), AQUOS PHONE ZETA SH-02E(Android 4.0.4)
* 開発機: MacBook Air Mid 2012
基本的にAndroid 2.2(API Level 8)でも動作するように確認しながら進めていきたいと思います。
環境構築とサンプルアプリのビルドまでの手順は、公式サイトにスクリーンショット付きで丁寧に説明してくれています。Windows 7用のガイドですが、ほぼEclipse上での手順説明なのでMacでもパスを読み替える程度で簡単にできました。僕の場合はSDKを ~/Library/OpenCV2.4.4
以下にインストールしておきました。
* OpenCV4Android SDK — OpenCV 2.4.4 documentation
OpenCV Managerについて
OpenCV4Android SDK 2.4.2からライブラリ自体はOpenCV Managerというアプリで管理する構成に変更されました。これまでのようにアプリのバイナリにライブラリをコピーするのは無駄だからというのが主な理由とのことです。デバイスのアーキテクチャをOpenCV Managerが判別して適切なライブラリをインストールしてくれます。
確かにサイズは大きく減っていますね。ただ、アプリのユーザーにOpenCVを意識させることになるので(OpenCV Managerを別途インストールしてもらう必要がある)その辺りは微妙な気はしますけど。実際にAndroid端末にOpenCV ManagerをGoogle Playからインストール、実行してみました。ライブラリのインストールが成功すると以下のような画像が見られます。
カメラアクセスの許可
OpenCVの使い方の前に、けっこう忘れがちなカメラアクセスの許可設定を行っておきます。AndroidManifest.xml ファイルを以下のように編集します。
1 2 3 |
<uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> |
以下、OpenCVでデバイスのカメラを利用した動画像処理を行う際の基本フローを整理していきます。
1. レイアウトの定義
OpenCVではカメラを扱う2つのクラス JavaCameraView と NativeCameraView が提供されています(両者の違いは後述)。これのどちらかをビューとしてXMLレイアウトファイル(res/layout/*.xml)に配置します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:opencv="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:keepScreenOn="true" tools:context=".MainActivity" > <org.opencv.android.JavaCameraView android:id="@+id/camera_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:visibility="visible" opencv:camera_id="any" opencv:show_fps="true" /> </RelativeLayout> |
ここでは JavaCameraView を配置しています。利用できる属性は camera_id (any/front/back) と show_fps (true/false) です。camera_id には利用するカメラ(フロント/バックカメラ)を指定するのですが、any を指定しておけば自動的にカメラを判別してくれます。show_fps を true にすると画面左上にFPSと画角が表示されます。デバッグ時は有効にしておくと良いです。
2. OpenCVライブラリの読み込みと初期化
OpenCV Managerを使う場合、Activityの中でOpenCVライブラリの読み込みを行う OpenCVLoader.initAsync メソッドを呼び出す必要があります。このメソッドは非同期で実行されるので、読み込み完了後にUIスレッドで呼ばれるコールバックメソッドも併せて実装しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public class MainActivity extends Activity implements CvCameraViewListener { // カメラビューのインスタンス // CameraBridgeViewBase は JavaCameraView/NativeCameraView のスーパークラス private CameraBridgeViewBase mCameraView; // ライブラリ初期化完了後に呼ばれるコールバック (onManagerConnected) // public abstract class BaseLoaderCallback implements LoaderCallbackInterface private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { // 読み込みが成功したらカメラプレビューを開始 case LoaderCallbackInterface.SUCCESS: mCameraView.enableView(); break; default: super.onManagerConnected(status); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // カメラビューのインスタンスを変数にバインド mCameraView = (CameraBridgeViewBase) findViewById(R.id.camera_view); // リスナーの設定 (後述) mCameraView.setCvCameraViewListener(this); } @Override protected void onResume() { super.onResume(); // 非同期でライブラリの読み込み/初期化を行う // static boolean initAsync(String Version, Context AppContext, LoaderCallbackInterface Callback) OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_4, this, mLoaderCallback); } |
JavaCameraViewとNativeCameraViewの違い
JavaCameraView と NativeCameraView は両方とも CameraBridgeViewBase のサブクラスです。
JavaCameraView は android.hardware.Camera を利用した実装、NativeCameraView はOpenCVの VideoCapture を利用した実装という違いがあります。安定性という観点ではAndroid SDKの機能を利用した JavaCameraView の方が安定しているかと思われますが、そんなに気にしなくて大丈夫でしょう。
CvCameraViewListenerインタフェースの実装
CvCameraViewListener または CvCameraViewListener2 インタフェースの以下の3つのメソッドを実装します。
* onCameraViewStarted
* onCameraViewStopped
* onCameraFrame
3つめの onCameraFrame メソッドに任意の画像処理を実装することになります。また、CvCameraViewListener と CvCameraViewListener2 の違いは onCameraFrame の引数のみで、onCameraViewStarted および onCameraViewStopped は共通です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Override public void onCameraViewStarted(int width, int height) { // カメラプレビュー開始時に呼ばれる } @Override public void onCameraViewStopped() { // カメラプレビュー終了時に呼ばれる } // CvCameraViewListener の場合 @Override public Mat onCameraFrame(Mat inputFrame) { // フレームをキャプチャする毎(30fpsなら毎秒30回)に呼ばれる } // CvCameraViewListener2 の場合 @Override public Mat onCameraFrame(CvCameraViewFrame inputFrame) { } |
CvCameraViewFrame インタフェースは以下のようになっています。CvCameraViewListener2.onCameraFrame ではこのインタフェースを実装した JavaCameraFrame/NativeCameraFrame のインスタンスが渡されます。
1 2 3 4 5 6 7 |
public interface CvCameraViewFrame { // 4チャンネルRGBAカラーのMatインスタンスを返す public Mat rgba(); // 1チャンネルグレースケールのMatインスタンスを返す public Mat gray(); }; |
キャプチャしたフレーム画像をそのままカメラプレビューに表示させるには、CvCameraViewListener.onCameraFrame の場合は引数の inputFrame をそのまま return し、CvCameraViewListener2.onCameraFrame の場合は inputFrame.rgba() を return します。
OpenCVでカメラを利用した動画像処理を行う際の基本フローは以上になります。ここまでのまとめとして、カメラから取得したフレーム画像を線画風に変換する処理のサンプルコードを載せておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
// package, import文は省略 public class MainActivity extends Activity implements CvCameraViewListener2 { private CameraBridgeViewBase mCameraView; private Mat mOutputFrame; private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: mCameraView.enableView(); break; default: super.onManagerConnected(status); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mCameraView = (CameraBridgeViewBase)findViewById(R.id.camera_view); mCameraView.setCvCameraViewListener(this); } @Override public void onPause() { if (mCameraView != null) { mCameraView.disableView(); } super.onPause(); } @Override protected void onResume() { super.onResume(); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_4, this, mLoaderCallback); } @Override public void onDestroy() { super.onDestroy(); if (mCameraView != null) { mCameraView.disableView(); } } @Override public void onCameraViewStarted(int width, int height) { // Mat(int rows, int cols, int type) // rows(行): height, cols(列): width mOutputFrame = new Mat(height, width, CvType.CV_8UC1); } @Override public void onCameraViewStopped() { mOutputFrame.release(); } @Override public Mat onCameraFrame(CvCameraViewFrame inputFrame) { // Cannyフィルタをかける Imgproc.Canny(inputFrame.gray(), mOutputFrame, 80, 100); // ビット反転 Core.bitwise_not(mOutputFrame, mOutputFrame); return mOutputFrame; } } |
Galaxy S2 LTEでだいたい15fpsほどで動作しました。スマートフォンでもこの速度が出せるのには驚きました。
今回はAndroidでOpenCVを使ったアプリ開発を試してみましたが、特に難しいところもなくスムーズに進めることができました。OpenCVがAndroidをサポートした当初はバグだらけで大変だったらしいですが、現在はとても使いやすくなっているので昔挫折してしまった人はリベンジしてみてください。これからも引き続きいろいろ試していきたいです。
* 参考
opencv.org – Home > PLATFORMS > ANDROID
Android | Blog はじめました
One thought