【今更だけど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;
}
}