From 3855bb499fbfc073771fa951babfe1922d90ffc9 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Mon, 25 Apr 2016 15:58:48 -0700 Subject: Fix portrait-orientation assumptions Not all devices are native portrait, so account for other sensor orientations in drawing gyro output and saving JPEGs. With this, Pixel C gyro overlay looks reasonable Bug: 27543253 Change-Id: I03f48a84f2dd206c468d00cca9ba09a46c1432a6 --- Android.mk | 1 + src/com/android/devcamera/Api2Camera.java | 9 +++-- src/com/android/devcamera/CameraInfoCache.java | 16 +++++++-- src/com/android/devcamera/CameraInterface.java | 7 ++++ src/com/android/devcamera/DevCameraActivity.java | 5 +-- src/com/android/devcamera/PreviewOverlay.java | 44 ++++++++++++++++++++---- 6 files changed, 68 insertions(+), 14 deletions(-) diff --git a/Android.mk b/Android.mk index 6433b95..d3deaac 100644 --- a/Android.mk +++ b/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SDK_VERSION := current +LOCAL_MIN_SDK_VERSION := 21 LOCAL_SRC_FILES := \ $(call all-java-files-under, src) diff --git a/src/com/android/devcamera/Api2Camera.java b/src/com/android/devcamera/Api2Camera.java index 65308a5..73e5c87 100644 --- a/src/com/android/devcamera/Api2Camera.java +++ b/src/com/android/devcamera/Api2Camera.java @@ -304,6 +304,11 @@ public class Api2Camera implements CameraInterface, SurfaceTexture.OnFrameAvaila return mCameraInfoCache.getFieldOfView(); } + @Override + public int getOrientation() { + return mCameraInfoCache.sensorOrientation(); + } + @Override public void openCamera() { // If API2 FULL mode is not available, display toast @@ -556,8 +561,8 @@ public class Api2Camera implements CameraInterface, SurfaceTexture.OnFrameAvaila Log.v(TAG, " Sent YUV1 image to ImageWriter.queueInputImage()"); try { CaptureRequest.Builder b1 = mCameraDevice.createReprocessCaptureRequest(mLastTotalCaptureResult); - // Portrait. - b1.set(CaptureRequest.JPEG_ORIENTATION, 90); + // Todo: Read current orientation instead of just assuming device is in native orientation + b1.set(CaptureRequest.JPEG_ORIENTATION, mCameraInfoCache.sensorOrientation()); b1.set(CaptureRequest.JPEG_QUALITY, (byte) 95); b1.set(CaptureRequest.NOISE_REDUCTION_MODE, mReprocessingNoiseMode); b1.set(CaptureRequest.EDGE_MODE, mReprocessingEdgeMode); diff --git a/src/com/android/devcamera/CameraInfoCache.java b/src/com/android/devcamera/CameraInfoCache.java index 1a5b0b1..699fd97 100644 --- a/src/com/android/devcamera/CameraInfoCache.java +++ b/src/com/android/devcamera/CameraInfoCache.java @@ -186,11 +186,21 @@ public class CameraInfoCache { Log.e(TAG, "No physical sensor dimensions reported by camera device, assuming default " + physicalSize); } + + // Only active array is actually visible, so calculate fraction of physicalSize that it takes up + Size pixelArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE); + Rect activeArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + + float activeWidthFraction = activeArraySize.width() / (float) pixelArraySize.getWidth(); + float activeHeightFraction = activeArraySize.height() / (float) pixelArraySize.getHeight(); + // Simple rectilinear lens field of view formula: - // angle of view = 2 * arctan ( sensor size / (2 * focal length) ) + // angle of view = 2 * arctan ( active size / (2 * focal length) ) float[] fieldOfView = new float[2]; - fieldOfView[0] = (float) Math.toDegrees(2 * Math.atan(physicalSize.getWidth() / 2 / focalLength)); - fieldOfView[1] = (float) Math.toDegrees(2 * Math.atan(physicalSize.getHeight() / 2 / focalLength)); + fieldOfView[0] = (float) Math.toDegrees( + 2 * Math.atan(physicalSize.getWidth() * activeWidthFraction / 2 / focalLength)); + fieldOfView[1] = (float) Math.toDegrees( + 2 * Math.atan(physicalSize.getHeight() * activeHeightFraction / 2 / focalLength)); return fieldOfView; } diff --git a/src/com/android/devcamera/CameraInterface.java b/src/com/android/devcamera/CameraInterface.java index e16c9e5..e6df5ce 100644 --- a/src/com/android/devcamera/CameraInterface.java +++ b/src/com/android/devcamera/CameraInterface.java @@ -32,6 +32,13 @@ public interface CameraInterface { */ float[] getFieldOfView(); + /** + * Get the camera sensor orientation relative to device native orientation + * Typically 90 or 270 for phones, 0 or 180 for tablets, though many tables are also + * portrait-native. + */ + int getOrientation(); + /** * Open the camera. Call startPreview() to actually see something. */ diff --git a/src/com/android/devcamera/DevCameraActivity.java b/src/com/android/devcamera/DevCameraActivity.java index 6194915..869e065 100644 --- a/src/com/android/devcamera/DevCameraActivity.java +++ b/src/com/android/devcamera/DevCameraActivity.java @@ -542,8 +542,9 @@ public class DevCameraActivity extends Activity implements CameraInterface.MyCam float[] fovs = mCamera.getFieldOfView(); mPreviewOverlay.setFieldOfView(fovs[0], fovs[1]); - mPreviewOverlay.setFacing(mToggleFrontCam.isChecked() ? - CameraCharacteristics.LENS_FACING_FRONT : CameraCharacteristics.LENS_FACING_BACK); + mPreviewOverlay.setFacingAndOrientation(mToggleFrontCam.isChecked() ? + CameraCharacteristics.LENS_FACING_FRONT : CameraCharacteristics.LENS_FACING_BACK, + mCamera.getOrientation()); if (mGyroOperations == null) { SensorManager sensorManager = (SensorManager) getSystemService(this.SENSOR_SERVICE); mGyroOperations = new GyroOperations(sensorManager); diff --git a/src/com/android/devcamera/PreviewOverlay.java b/src/com/android/devcamera/PreviewOverlay.java index 7a4dd02..5909b2d 100644 --- a/src/com/android/devcamera/PreviewOverlay.java +++ b/src/com/android/devcamera/PreviewOverlay.java @@ -43,8 +43,11 @@ public class PreviewOverlay extends View { private float mFovLargeDegrees; private float mFovSmallDegrees; private int mFacing = CameraCharacteristics.LENS_FACING_BACK; + private int mOrientation = 0; // degrees + float[] mAngles = new float[2]; + public PreviewOverlay(Context context, AttributeSet attrs) { super(context, attrs); Resources res = getResources(); @@ -75,8 +78,9 @@ public class PreviewOverlay extends View { * Set the facing of the current camera, for correct coordinate mapping. * One of the CameraCharacteristics.LENS_INFO_FACING_* constants */ - public void setFacing(int facing) { + public void setFacingAndOrientation(int facing, int orientation) { mFacing = facing; + mOrientation = orientation; } public void show3AInfo(boolean show) { @@ -86,13 +90,39 @@ public class PreviewOverlay extends View { } public void setGyroAngles(float[] angles) { - if (mFacing == CameraCharacteristics.LENS_FACING_BACK) { + boolean front = (mFacing == CameraCharacteristics.LENS_FACING_BACK); + // Rotate gyro coordinates to match camera orientation + // Gyro data is always presented in the device native coordinate system, which + // is either portrait or landscape depending on device. + // (http://developer.android.com/reference/android/hardware/SensorEvent.html) + // DevCamera locks itself to portrait, and the camera sensor long edge is always aligned + // with the long edge of the device. + // mOrientation is the relative orientation of the camera sensor and the device native + // orientation, so it can be used to decide if the gyro data is meant to be interpreted + // in landscape or portrait and flip coordinates/sign accordingly. + // Additionally, front-facing cameras are mirrored, so an additional sign flip is needed. + switch (mOrientation) { + case 0: + mAngles[1] = -angles[0]; + mAngles[0] = angles[1]; + break; + case 90: + mAngles[0] = angles[0]; + mAngles[1] = angles[1]; + break; + case 180: + mAngles[1] = -angles[0]; + mAngles[0] = angles[1]; + break; + case 270: + mAngles[0] = angles[0]; + mAngles[1] = angles[1]; + break; + } + if (mFacing != CameraCharacteristics.LENS_FACING_BACK) { // Reverse sensor readout for front/external facing cameras - mAngles[0] = angles[0]; - mAngles[1] = angles[1]; - } else { - mAngles[0] = -angles[0]; - mAngles[1] = -angles[1]; + mAngles[0] = -mAngles[0]; + mAngles[1] = -mAngles[1]; } } -- cgit v1.2.3