OpenCV for Android入門 – カメラ編

ここ最近は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が判別して適切なライブラリをインストールしてくれます。

using OpenCV Manager
using OpenCV Manager, feel the difference

確かにサイズは大きく減っていますね。ただ、アプリのユーザーにOpenCVを意識させることになるので(OpenCV Managerを別途インストールしてもらう必要がある)その辺りは微妙な気はしますけど。実際にAndroid端末にOpenCV ManagerをGoogle Playからインストール、実行してみました。ライブラリのインストールが成功すると以下のような画像が見られます。
opencv_manager_aquosphone

カメラアクセスの許可

OpenCVの使い方の前に、けっこう忘れがちなカメラアクセスの許可設定を行っておきます。AndroidManifest.xml ファイルを以下のように編集します。
[xml]



[/xml]

以下、OpenCVでデバイスのカメラを利用した動画像処理を行う際の基本フローを整理していきます。

1. レイアウトの定義

OpenCVではカメラを扱う2つのクラス JavaCameraViewNativeCameraView が提供されています(両者の違いは後述)。これのどちらかをビューとしてXMLレイアウトファイル(res/layout/*.xml)に配置します。
[xml]


[/xml]
ここでは 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スレッドで呼ばれるコールバックメソッドも併せて実装しておきます。
[java]
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);
}
[/java]

JavaCameraViewとNativeCameraViewの違い

JavaCameraView と NativeCameraView は両方とも CameraBridgeViewBase のサブクラスです。
opencv4android_cameraview
JavaCameraView は android.hardware.Camera を利用した実装、NativeCameraView はOpenCVの VideoCapture を利用した実装という違いがあります。安定性という観点ではAndroid SDKの機能を利用した JavaCameraView の方が安定しているかと思われますが、そんなに気にしなくて大丈夫でしょう。

CvCameraViewListenerインタフェースの実装

CvCameraViewListener または CvCameraViewListener2 インタフェースの以下の3つのメソッドを実装します。
* onCameraViewStarted
* onCameraViewStopped
* onCameraFrame
3つめの onCameraFrame メソッドに任意の画像処理を実装することになります。また、CvCameraViewListener と CvCameraViewListener2 の違いは onCameraFrame の引数のみで、onCameraViewStarted および onCameraViewStopped は共通です。
[java]
@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) {
}
[/java]
CvCameraViewFrame インタフェースは以下のようになっています。CvCameraViewListener2.onCameraFrame ではこのインタフェースを実装した JavaCameraFrame/NativeCameraFrame のインスタンスが渡されます。
[java]
public interface CvCameraViewFrame {
// 4チャンネルRGBAカラーのMatインスタンスを返す
public Mat rgba();

// 1チャンネルグレースケールのMatインスタンスを返す
public Mat gray();
};
[/java]
キャプチャしたフレーム画像をそのままカメラプレビューに表示させるには、CvCameraViewListener.onCameraFrame の場合は引数の inputFrame をそのまま return し、CvCameraViewListener2.onCameraFrame の場合は inputFrame.rgba() を return します。

OpenCVでカメラを利用した動画像処理を行う際の基本フローは以上になります。ここまでのまとめとして、カメラから取得したフレーム画像を線画風に変換する処理のサンプルコードを載せておきます。
[java]
// 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;
}
}
[/java]

opencv4android_linedraw
opencv4android line drawing

Galaxy S2 LTEでだいたい15fpsほどで動作しました。スマートフォンでもこの速度が出せるのには驚きました。

今回はAndroidでOpenCVを使ったアプリ開発を試してみましたが、特に難しいところもなくスムーズに進めることができました。OpenCVがAndroidをサポートした当初はバグだらけで大変だったらしいですが、現在はとても使いやすくなっているので昔挫折してしまった人はリベンジしてみてください。これからも引き続きいろいろ試していきたいです。

* 参考
opencv.org – Home > PLATFORMS > ANDROID
Android | Blog はじめました

あわせて読む:

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です