How to set Android camera orientation properly?

AndroidCameraOrientationSurfaceview

Android Problem Overview


I want to set the camera orientation according to the device orientation in Android but nothing seems to be working. I tried to rotate the Surface as well as the camera parameters but the camera preview in portrait mode always comes upside down. I would need to rotate it by 90 degree clockwise for it to be correct. Here is the code I am using right now which works in landscape mode only.

    SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		camera.stopPreview();
		camera.release();
		camera = null;
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {			
		initCamera();			
	}

	private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
		final double ASPECT_TOLERANCE = 0.2;
		double targetRatio = (double) w / h;
		if (sizes == null)
			return null;

		Size optimalSize = null;
		double minDiff = Double.MAX_VALUE;

		int targetHeight = h;

		// Try to find an size match aspect ratio and size
		for (Size size : sizes) {
			Log.d(TAG, "Checking size " + size.width + "w " + size.height
					+ "h");
			double ratio = (double) size.width / size.height;
			if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
				continue;
			if (Math.abs(size.height - targetHeight) < minDiff) {
				optimalSize = size;
				minDiff = Math.abs(size.height - targetHeight);
			}
		}

		// Cannot find the one match the aspect ratio, ignore the
		// requirement
		if (optimalSize == null) {
			minDiff = Double.MAX_VALUE;
			for (Size size : sizes) {
				if (Math.abs(size.height - targetHeight) < minDiff) {
					optimalSize = size;
					minDiff = Math.abs(size.height - targetHeight);
				}
			}
		}
		return optimalSize;
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		Camera.Parameters parameters = camera.getParameters();

		List<Size> sizes = parameters.getSupportedPreviewSizes();
		Size optimalSize = getOptimalPreviewSize(sizes, width, height);			
		Log.d(TAG, "Surface size is " + width + "w " + height + "h");
		Log.d(TAG, "Optimal size is " + optimalSize.width + "w " + optimalSize.height + "h");			
		parameters.setPreviewSize(optimalSize.width, optimalSize.height);			
		// parameters.setPreviewSize(width, height);			
		camera.setParameters(parameters);
		camera.startPreview();
	}
};	

Android Solutions


Solution 1 - Android

From other member and my problem:

Camera Rotation issue depend on different Devices and certain Version.

Version 1.6: to fix the Rotation Issue, and it is good for most of devices

if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
		{	
			p.set("orientation", "portrait");
			p.set("rotation",90);
		}
		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
		{								
			p.set("orientation", "landscape");			
			p.set("rotation", 90);
		}

Version 2.1: depend on kind of devices, for example, Cannt fix the issue with XPeria X10, but it is good for X8, and Mini

Camera.Parameters parameters = camera.getParameters();
parameters.set("orientation", "portrait");
camera.setParameters(parameters);

Version 2.2: not for all devices

camera.setDisplayOrientation(90);

http://code.google.com/p/android/issues/detail?id=1193#c42

Solution 2 - Android

From the Javadocs for setDisplayOrientation(int) (Requires API level 9):

 public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, info);
     int rotation = activity.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;
     }
     camera.setDisplayOrientation(result);
 }

Solution 3 - Android

This solution will work for all versions of Android. You can use reflection in Java to make it work for all Android devices:

Basically you should create a reflection wrapper to call the Android 2.2 setDisplayOrientation, instead of calling the specific method.

The method:

	protected void setDisplayOrientation(Camera camera, int angle){
	Method downPolymorphic;
	try
	{
		downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
		if (downPolymorphic != null)
			downPolymorphic.invoke(camera, new Object[] { angle });
	}
	catch (Exception e1)
	{
	}
}

And instead of using camera.setDisplayOrientation(x) use setDisplayOrientation(camera, x) :

	if (Integer.parseInt(Build.VERSION.SDK) >= 8)
		setDisplayOrientation(mCamera, 90);
	else
	{
		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
		{
			p.set("orientation", "portrait");
			p.set("rotation", 90);
		}
		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
		{
			p.set("orientation", "landscape");
			p.set("rotation", 90);
		}
	}	

Solution 4 - Android

I faced the issue when i was using ZBar for scanning in tabs. Camera orientation issue. Using below code i was able to resolve issue. This is not the whole code snippet, Please take only help from this.

 public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
     if (isPreviewRunning) {
            mCamera.stopPreview();
        }

 setCameraDisplayOrientation(mCamera);

        previewCamera();

    }



 public void previewCamera() {

        try {
            // Hard code camera surface rotation 90 degs to match Activity view
            // in portrait
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(previewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(autoFocusCallback);
            isPreviewRunning = true;
        } catch (Exception e) {
            Log.d("DBG", "Error starting camera preview: " + e.getMessage());
        }


    }


public void setCameraDisplayOrientation(android.hardware.Camera camera) {
        Camera.Parameters parameters = camera.getParameters();

        android.hardware.Camera.CameraInfo camInfo =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(getBackFacingCameraId(), camInfo);


        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        int rotation = display.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 (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (camInfo.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (camInfo.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }




    private int getBackFacingCameraId() {
        int cameraId = -1;
        // Search for the front facing camera
        int numberOfCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {

                cameraId = i;
                break;
            }
        }
        return cameraId;
    }

Solution 5 - Android

I finally fixed this using the Google's camera app. It gets the phone's orientation by using a sensor and then sets the EXIF tag appropriately. The JPEG which comes out of the camera is not oriented automatically.

Also, the camera preview works properly only in the landscape mode. If you need your activity layout to be oriented in portrait, you will have to do it manually using the value from the orientation sensor.

Solution 6 - Android

This problem was solved a long time ago but I encountered some difficulties to put all pieces together so here is my final solution, I hope this will help others :

public void startPreview() {
		try {
			Log.i(TAG, "starting preview: " + started);
			
			// ....
			Camera.CameraInfo camInfo = new Camera.CameraInfo();
			Camera.getCameraInfo(cameraIndex, camInfo);
			int cameraRotationOffset = camInfo.orientation;
			// ...
			
			Camera.Parameters parameters = camera.getParameters();
			List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
			Camera.Size previewSize = null;
			float closestRatio = Float.MAX_VALUE;

			int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
			int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
			float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;

			Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
			for (Camera.Size candidateSize : previewSizes) {
				float whRatio = candidateSize.width / (float) candidateSize.height;
				if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
					closestRatio = whRatio;
					previewSize = candidateSize;
				}
			}

			int rotation = getWindowManager().getDefaultDisplay().getRotation();
			int degrees = 0;
			switch (rotation) {
			case Surface.ROTATION_0:
				degrees = 0;
				break; // Natural orientation
			case Surface.ROTATION_90:
				degrees = 90;
				break; // Landscape left
			case Surface.ROTATION_180:
				degrees = 180;
				break;// Upside down
			case Surface.ROTATION_270:
				degrees = 270;
				break;// Landscape right
			}
			int displayRotation;
			if (isFrontFacingCam) {
				displayRotation = (cameraRotationOffset + degrees) % 360;
				displayRotation = (360 - displayRotation) % 360; // compensate
																	// the
																	// mirror
			} else { // back-facing
				displayRotation = (cameraRotationOffset - degrees + 360) % 360;
			}

			Log.v(TAG, "rotation cam / phone = displayRotation: " + cameraRotationOffset + " / " + degrees + " = "
					+ displayRotation);

			this.camera.setDisplayOrientation(displayRotation);

			int rotate;
			if (isFrontFacingCam) {
				rotate = (360 + cameraRotationOffset + degrees) % 360;
			} else {
				rotate = (360 + cameraRotationOffset - degrees) % 360;
			}

			Log.v(TAG, "screenshot rotation: " + cameraRotationOffset + " / " + degrees + " = " + rotate);

			Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
			parameters.setPreviewSize(previewSize.width, previewSize.height);
			parameters.setRotation(rotate);
			camera.setParameters(parameters);
			camera.setPreviewDisplay(mHolder);
			camera.startPreview();

			Log.d(TAG, "preview started");

			started = true;
		} catch (IOException e) {
			Log.d(TAG, "Error setting camera preview: " + e.getMessage());
		}
	}

Solution 7 - Android

check out this solution

 public static void setCameraDisplayOrientation(Activity activity,
                                                   int cameraId, android.hardware.Camera camera) {
        android.hardware.Camera.CameraInfo info =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.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;
        }
        camera.setDisplayOrientation(result);
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbhinavView Question on Stackoverflow
Solution 1 - AndroidLTEHUBView Answer on Stackoverflow
Solution 2 - AndroidJason RobinsonView Answer on Stackoverflow
Solution 3 - AndroidhnvietView Answer on Stackoverflow
Solution 4 - AndroidDeepakPanwarView Answer on Stackoverflow
Solution 5 - AndroidAbhinavView Answer on Stackoverflow
Solution 6 - AndroidLouis GRIGNONView Answer on Stackoverflow
Solution 7 - AndroidMudassir KhanView Answer on Stackoverflow