AndroidでOpenCV3.0を使ってカメラ映像をグレースケールにする

# やりたいこと
画像認識、画像処理ライブラリOpenCVをAndroidのプロジェクトに組み込んで、カメラ映像に簡単な処理を加えてみます。一度組み込みに成功すれば、画像処理は変更可能と思いますので、簡単な例としてカメラ映像をグレースケールで出力する例を記述致します。

# 準備
以下のサイトからダウンロード可能です
http://sourceforge.net/projects/opencvlibrary/files/opencv-android/

今回は Version 3.0.0を選択しました。

準備は以下のサイトがとても詳しいので、以下の記事を参考にして頂き、OpenCVを使いたいプロジェクトにライブラリを追加します。
http://smartphone-zine.com/android/use-opencv-for-android-in-the-android-studio.html

1点異なる点は、この記事ではjniLibraryをコピーしているのですが、OpenCV Managerをインストールすれば動作可能なので必要ありません。jniLibraryを組み込むとアプリケーションサイズが大きくなってしまいますので、OpenCV Managerの使用をオススメします。

https://play.google.com/store/apps/details?id=org.opencv.engine

アプリケーション実行前にインストールしても良いですが、インストールされていない場合も、OpenCVの機能を使う前に以下のようなダイアログが表示されますので、そこからインストールが可能です。

<img width="433" alt="スクリーンショット 2015-10-29 18.33.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/85182/7648bd25-c486-8501-3f1b-e3c133052a08.png">

# アプリケーションの作成
カメラを使いますのでPermissionの追加をAndroidManifest.xmlに行います。

```xml
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

```

次にカメラの映像をOpenCVライブラリに渡す方法なのですが、OpenCV SDKには専用のViewが用意されています。ActivityもしくはFragmentのレイアウトファイルにJavaCameraViewを追加して下さい。

```xml
    <org.opencv.android.JavaCameraView
        android:id="@+id/camera_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="visible"/>
```

今回はFragmentにJavaCameraViewを貼付けた場合の例でコードを追記していきます。

まず、Viewが生成後にJavaCameraViewのインスタンスを取得します。またカメラのフレームを受けるためにリスナーを追加します。ここでは、Fragment自体でCvCameraViewListener2を実現します。Fragmentクラスの定義は以下のようになります

```java
public class HogeFragment extends Fragment implements CvCameraViewListener2 {
...
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mCameraView = (CameraBridgeViewBase)view.findViewById(R.id.camera_view);
        mCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mCameraView = null;
    }
    @Override
    public void onCameraViewStarted(int width, int height) {
    }

    @Override
    public void onCameraViewStopped() {
    }

    @Override
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        ...
    }
```

次にonResumeの段階でOpenCVライブラリの読み込みを行います。読み込みが完了した時点で、JavaCameraViewをEnableにしてフレームを受信し始めます。読み込み完了を知るためにはListenerを登録する必要があります。

```java
    private static class OpenCVLoaderCallback extends BaseLoaderCallback {
        private final CameraBridgeViewBase mCameraView;
        private OpenCVLoaderCallback(Context context, CameraBridgeViewBase cameraView) {
            super(context);
            mCameraView = cameraView;
        }

        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                    mCameraView.enableView();
                    break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, getActivity(),
                new OpenCVLoaderCallback(getActivity(), mCameraView));
    }

    @Override
    public void onPause() {
        super.onPause();
        mCameraView.disableView();
    }
```
これでonCameraFrameにフレームがきます。グレーアウトする場合は、実は何もする必要がなくて、入力されたフレームからグレースケールのデータだけを以下のように返せば実現が可能です。

```java
    @Override
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        return inputFrame.gray();
    }
```