【今更だけどandroid.hardware.Cameraを使う】カメラプレビューの向きを補正する

このエントリーを Google ブックマーク に追加
Pocket
[`yahoo` not found]

android.hardware.Cameraを使うことで、Android端末にあるカメラデバイスにアクセスして使うことができます。
android.hardware.Cameraは、現在では非推奨となっていますがOpenCV等ではまだまだ使われています。

通常、カメラの上下左右とスクリーンの上下左右は一致しません。さらに言えばフロントカメラとバックカメラの向きも異なります。
そのため、上記を考慮に入れずにカメラプレビューを表示すると向きがおかしくなります。

今回は、カメラプレビューの向きを補正します。
下記の実装はすべて前回までに作ったクラスを修正しています。

補正する角度を取得するメソッド

前回作ったMyCameraPreviewに補正する角度を取得するメソッドを定義します。
このメソッドはCamera#setDisplayOrientationに提示されているメソッドを流用します。このメソッドはフロントカメラ・バックカメラ、カメラの向き等を考慮してCamera#setDisplayOrientationに渡すべき値を計算します。
    public int getCameraDisplayOrientation() {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotation = ((Activity)getContext()).getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0: degrees = 0; break;
            case Surface.ROTATION_90: degrees = 90; break;
            case Surface.ROTATION_180: degrees = 180; break;
            case Surface.ROTATION_270: degrees = 270; break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
            // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }

Camera#setDisplayOrientationを使う

Camera#setDisplayOrientationはプレビューを表示する前に設定する必要があります。
そのため、プレビューを開始する前、またはプレビューを開始済の場合は一度プレビューを止める必要があります。
前回作ったMyCameraPreview#startPreviewを修正します。
    private void startPreview() {
        Camera camera = Camera.open(this.cameraId);
        mCamera = camera;
        try {
            mCamera.setPreviewDisplay(mHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mCamera.setDisplayOrientation(getCameraDisplayOrientation());
        mCamera.startPreview();
    }

まとめ

今回はカメラプレビューの向きを補正する方法を説明しました。
public class MyCameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private final static String TAG = "MyCameraPreview";

    private int cameraId;

    private SurfaceHolder mHolder;

    private Camera mCamera;

    public MyCameraPreview(Context context, int cameraId) {
        super(context);
        this.cameraId = cameraId;
        mHolder = getHolder();
        mHolder.addCallback(this);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        mHolder = holder;
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mHolder.getSurface() == null){
            return;
        }
        stopPreview();
        startPreview();
    }

    private void startPreview() {
        Camera camera = Camera.open(this.cameraId);
        mCamera = camera;
        try {
            mCamera.setPreviewDisplay(mHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mCamera.setDisplayOrientation(getCameraDisplayOrientation());
        mCamera.startPreview();
    }

    private void stopPreview(){
        if (mCamera != null) {
            mCamera.stopPreview();
        }
    }

    public void onPause(){
        if(mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
    }

    public int getCameraDisplayOrientation() {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotation = ((Activity)getContext()).getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0: degrees = 0; break;
            case Surface.ROTATION_90: degrees = 90; break;
            case Surface.ROTATION_180: degrees = 180; break;
            case Surface.ROTATION_270: degrees = 270; break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }
}