17

The documentation page for Camera.setDisplayOrientation contains the following code sample stating that using it will "make the camera image show in the same orientation as the display":

 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);
 }

However, I had problems using it, sometimes the image would appear upside down. After some trial-and-error, I found that the correct code would be (replacing the last 8 lines of the method):

    int result = (360 + info.orientation - degrees) % 360;
    camera.setDisplayOrientation(result);

(Which means the calculation for back-facing cameras is correct for front cameras too.) The "compensate the mirror" comment is a bit weird since mirroring cannot be undone by rotating, that operation only swaps 90° and 270° rotations which doesn't really make sense to me.

So the question is: is the sample wrong indeed or am I missing something? I tried it on multiple devices, both back and front cameras and all supported orientations, so I know that my code IS working. One little detail that might be worth mentioning: all my devices returned 90° as info.orientation.

EDIT: Here is my camera-related code, I have tested it on a Nexus One and a Samsung Galaxy S Plus. It is used in my head-tracking 3D app, the preview is shown in the lower left corner and should always have the correct orientation.

SOLUTION (sort of): It looks like the code is correct, but my testing phone (Samsung Galaxy S Plus) returns an incorrect value for the front camera's CameraInfo.orientation. There are many related discussions about the preview being shown upside down on this model (examples here and here). A way to fix is to include an option to manually rotate the image.

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
molnarm
  • 9,856
  • 2
  • 42
  • 60
  • 5
    Unfortunately, the Android docs have had plenty of bugs in them, but they're getting better. I tested your code, and experienced the same thing you have on my devices. I suggest you go here: https://code.google.com/p/android/issues/entry?template=Developer%20Documentation – Richard Taylor Oct 30 '12 at 10:36

1 Answers1

13

The snippet you've quoted, which I used and applied in my project, is no problem in my circumstances.

For all the devices (Galaxy Note, Galaxy S III, Galaxy Nexus, Galaxy Ace II, Nexus S) I used for test, info.Orientation all return 270 on front camera, and 90 on back camera.

After a few discuss with the question raiser, I found I misunderstood the questions, so I divide the answer in two parts.

For the wrong orientation in camera preview, please refer to this solution:

Solution for wrong orientation in camera preview:

First please ensure info.Orientation will return 270 on front camera, 90 on back camera. Then please try set your camera preview activity (or similar class that handles the preview) orientation to landscape.

Thus, when you go through the code, you'll find:

degree = 90 for screen orientation, info.Orientation = 270 for the front camera. Then you'll get result = (270 - 90 + 360) % 360, result = 180, which means it will rotate clock wise 180 for your front camera view that will correct the front camera upside-down issue.

Solution for wrong orientation in camera photo results:

If this info.Orientation applies to you, then the problem may be:

  1. For some Samsung (or other) devices (Like Galaxy Note, Galaxy SIII), the camera will only write the Orientation Tag instead of rotate the real pixels.
  2. If you're using the front camera and using the code above, it will display the preview with correct orientation, but will show u the "upside down" picture if you take the shot.

Solution:

/**
 *
 * Get the orientation from EXIF
 * @param filepath
 * @return orientation
 */
public int getExifOrientation(String filepath) {
    int degree = 0;
    ExifInterface exif = null;
    try {
        exif = new ExifInterface(filepath);
    } catch (IOException ex) {
        Log.e("EXIF info", "cannot read exif", ex);
    }
    if (exif != null) {
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
        if (orientation != -1) {
            // We only recognize a subset of orientation tag values.
            switch(orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                degree = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                degree = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                degree = 270;
                break;
            }
        }
    } else {
        degree = 1;
    }
    Log.i("EXIF info", "Orientation degrees: " + degree);
    return degree;
}

Then

if (isFromCamera) {

    if (fromFrontCam) {
        // try change here if the orientation still wrong, -90 means rotate counter-clockwise 90 degrees.
        matrix.preRotate(-90);
     } else {
        matrix.preRotate(90);
     }
} else {
    // read EXIF data
    getExifOrientation(path)
    // don't forget to handle the situation that some devices won't write exif data
    matrix.preRotate(degree);
}
dumbfingers
  • 7,001
  • 5
  • 54
  • 80
  • Thanks for your answer. The first line is very interesting (you say the sample is working for you), but the rest is irrelevant, sorry. The method in my question ([Camera.setDisplayOrientation()](http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation%28int%29)) "does not affect the order of byte array passed in onPreviewFrame(byte[], Camera), *JPEG pictures*, or recorded videos." I don't want to take pictures or load them, I only need the preview in the right orientation. – molnarm Nov 01 '12 at 12:09
  • @MártonMolnár Yes, the Document's method working for me, I can get the correct orientation on the camera preview. May I ask which device you're using? Since your question is to ask about the preview orientation, I'll update my answer as well, sorry I didn't fully understand your question at first :) – dumbfingers Nov 01 '12 at 12:13
  • @MártonMolnár may I have a look at how you apply the code into your project? i.e where did you call this method? thx – dumbfingers Nov 01 '12 at 12:16
  • I've added the details you asked for. – molnarm Nov 02 '12 at 13:50
  • @MártonMolnár could you try set your preview layout orientation to landscape? That'll make sense. – dumbfingers Nov 02 '12 at 14:02
  • It is landscape when the device is. Please try my app and see if the preview is right for you. – molnarm Nov 05 '12 at 13:29
  • @MártonMolnár sure, i'm heading to the Play store. – dumbfingers Nov 05 '12 at 14:21
  • @MártonMolnár I meant to fix your camera preview orientation in XML to landscape? Because the only difference between your and my code is the orientation in XML I think. If u set to landscape, `degree`=90 for screen, `info.orientation`=270 for front cam, `result=(270-90+360)%360=180` just correct the upside down. – dumbfingers Nov 05 '12 at 14:30
  • 1
    @MártonMolnár according my experience, you should set your activity (which your camera in) to Landscape. – landry Nov 06 '12 at 08:17
  • I'm setting LayoutParams of SurfaceView from code, that is what the *swapMetrics* variable is for in the code in pastebin. – molnarm Nov 06 '12 at 08:37
  • I gave you the bounty because you were trying to help me, but this still isn't really an answer :D – molnarm Nov 06 '12 at 09:16
  • 1
    @MártonMolnár thx, but I think I still got a way to help, since u've said u set the view via code, then could you try: `setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);` to set the camera activity (like @landry said, that's what i was trying to tell you) to landscape. Hope you've understand my calculation in my previous comment. – dumbfingers Nov 06 '12 at 09:26
  • I tried my app on a friend's Galaxy SIII and you were right, my code flips the image and the sample is right! But it is the opposite on my Galaxy S Plus, because as I wrote in my question, it returns 90 as info.orientation for both cameras. Well, now what? – molnarm Nov 29 '12 at 09:53
  • @MártonMolnár maybe u can try using a `if (front orientation=90 && back orientation=90)` then do a correcting calculation? Is it only happens on Galaxy S Plus? – dumbfingers Nov 29 '12 at 11:14
  • Unfortunately, the first part is not necessarily true, with the most famous device being [Nexus 5x](https://stackoverflow.com/q/35414141/192373). Sometimes the CameraInfo.orientation is not *270 on front camera, and 90 on back camera*, and your code must cope with this. The devices like *Samsung Galaxy S Plus* require special shims to fix the buggy firmware. – Alex Cohn Aug 18 '20 at 10:29