カメラプレビューをキャプチャする


Androidのカメラ機能を活用し、リアルタイムに画像処理をしてみましょう。
Android SDKでカメラを扱うにはandroid.hardware.cameraパッケージを利用します。cameraパッケージではカメラからの入力映像をそのまま簡単に画面に表示する「プレビュー」機能があります。
画像を編集する前段階として、プレビュー画面に表示したカメラ画像をキャプチャする方法を紹介します。

便利なことにAndroid SDKのAPI Demos内でカメラ機能について纏まっていますので、このGraphics/CameraPreview.javaをベースに改造を加えていきましょう。サンプルではプレビューしている画像データをキャプチャ、保存する機能を追加します。

Cameraクラスの主なメソッドと関連クラス

メソッド名説明
getNumberOfCameras()Android端末のカメラデバイス数を取得する
getCameraInfo(i, camerainfo)指定デバイスNoのCameraInfoを取得する。
open()カメラデバイスリソースの取得
release()カメラデバイスリソースの解放
setPreviewCallback()プレビュー完了時のコールバックメソッドの登録
Camera.PreviewCallbackプレビュー完了時に呼び出されるコールバックメソッド
Camera.PreviewCallback.onPreviewFrame(byte[] data, Camera camera)プレビュー更新時のフレーム情報
startPreview()プレビューの開始
stopPreview()プレビューの終了

複数のカメラを使う https://techbooster.org/andriod/device/2358/
カメラの使用方法(2) https://techbooster.org/andriod/device/362/

パーミッションの設定

カメラ機能を利用するにはパーミッションの設定が必要です。忘れないように、はじめに追加しておくとよいでしょう
■AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
      package="org.jpn.techbooster.sample.camerapreviews"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...省略...
</manifest>

カメラ機能を利用する権限を取得するため、7行目にuses-permission要素を追加しています。このサンプルでは外部ディスクへのデータ保存を行うため、WRITE_EXTERNAL_STORAGEも付与しています。

カメラ情報の取得

カメラプレビューを表示するため、ActivityのonCreateメソッド内でカメラ数の取得や利用するカメラの種類(フロントカメラ・バックカメラ)など初期設定を行います。
■src/CameraPreviewsActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    //タイトルを非表示
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
    // プレビュー画面を作成
    mPreview = new Preview(this);
    setContentView(mPreview);
 
    // カメラ数を取得
    numberOfCameras = Camera.getNumberOfCameras();
 
    // 複数カメラがあった場合に備えて、BACKカメラを指定
    CameraInfo cameraInfo = new CameraInfo();
    for (int i = 0; i < numberOfCameras; i++) {
        Camera.getCameraInfo(i, cameraInfo);
        if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
            defaultCameraId = i;
        }
    }
}

10行目のPreviewクラスはApiDemosのGraphics/CameraPreview.java内にあるカメラプレビュー用のクラスです。カメラ制御にかかわる機能がまとめられており、11行目のsetContentViewメソッドの引数にできるように、Viewクラスを継承しています。
14行目 CameraクラスのgetNumberOfCamerasメソッドでAndroid端末のカメラ数を取得し、20行目でバックカメラを探します

カメラリソースの取得と解放

カメラリソースの取得/解放は、onResumeメソッドとonPauseメソッドで行います。
まれに、デバッグ中に強制終了した場合、リソースを解放できずにリソースを再取得できないケースがあります。リソース取得/解放ができていないと不具合に繋がるため、Tipsとして覚えておくとよいでしょう。最悪、Android端末の再起動が必要になります。
■src/CameraPreviewsActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
protected void onResume() {
    super.onResume();
 
    // カメラを開く
    mCamera = Camera.open();
    cameraCurrentlyLocked = defaultCameraId;
    mPreview.setCamera(mCamera);
}
 
@Override
protected void onPause() {
    super.onPause();
 
    // カメラを停止する
    if (mCamera != null) {
        mPreview.setCamera(null);
        mCamera.release();
        mCamera = null;
    }
}

6行目 mCameraインスタンスはandroid.hardware.Cameraクラスです。18行目 mCameraがnullでなければリソースを取得していますので、必ずonPause時に解放(Releaseメソッド)するようにしましょう。

カメラプレビューの取得と保存

最後に、プレビュー画像を取得するためにPreviewクラスを変更します。
画像データを取得するtakePreviewRawDataメソッドを作成します。
■src/CameraPreviewsActivity.java

1
2
3
4
5
6
7
8
9
10
11
class Preview extends ViewGroup implements SurfaceHolder.Callback {
    ...省略...
    private boolean mProgressFlag = false;
 
    public void takePreviewRawData() {
        if (!mProgressFlag) {
            mProgressFlag = true;
            mCamera.setPreviewCallback(editPreviewImage);
                //プレビューコールバックをセット
        }
    }

8行目 CameraクラスのsetPreviewCallbackメソッドはプレビューする度に呼ばれるコールバックメソッドです。PreviewCallbackを設定すると、毎プレビューごとに画像データを取得できます。

以下のサンプルコードは、プレビュー時のコールバックの内容になります。
editPreviewImage内部の処理で画像を保存しています。保存した画像はjpegなど一般的なフォーマットではなく、搭載されたカメラ制御デバイスに依存し、YUVデータなどハードウェアデバイスごとに最適化された生データになります。
これらはAndroid端末ごとに異なる可能性があり、互換性の少ない固有のフォーマット(Sony Xperia arcなどはYUVデータですが、色の格納順番が異なります)になります。
■src/CameraPreviewsActivity.java

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
private final Camera.PreviewCallback editPreviewImage =
        new Camera.PreviewCallback() {
 
    public void onPreviewFrame(byte[] data, Camera camera) {
        mCamera.setPreviewCallback(null);  // プレビューコールバックを解除
 
        mCamera.stopPreview();
        // 画像の保存処理
 
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MMddHH_mmss_SSS");
 
        //  画像を保存
        String path = Environment.getExternalStorageDirectory().getPath() +
                '/' + dateFormat.format(new Date()) + ".raw";
 
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            fos.write(data);
            fos.close();
        } catch (IOException e) {
            Log.e("CAMERA", e.getMessage());
        }
 
        mCamera.startPreview();
 
        mProgressFlag = false;
    }
};

5行目、処理を継続しない場合は、コールバックを解除しておくといいでしょう。今回は一度かぎりの実行ということでsetPreviewCallbackメソッドにnullを設定します。
7行目から25行目にかけてプレビューを一端停止~画像処理~プレビュー再開します。プレビュー表示中に画像データ(onPreviewFrameメソッドの第1引数data)を編集すると、描画が乱れるためです。

以上で画像データの取得は完成です。お疲れ様でした!