【今更だけどandroid.hardware.Cameraを使う】カメラプレビューの向きを補正する
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; } }