X

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

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      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

    @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

    @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

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

    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)を編集すると、描画が乱れるためです。

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

mhidaka: Software Engineerだよ。DroidKaigi Organizer / Androidと組込とRe:VIEW。techbooster主宰。mhidaka's writings http://booklog.jp/users/mhidaka 技術書典! http://techbookfest.org