9
votes

I have written an Android activity that captures a photo programatically. I want to save the image as a JPEG with the correct EXIF orientation data (just like the native Android Camera app does automatically).

Here is the method for actually taking the photo (I removed the try/catch blocks):

private void takePhoto() {

    camera = Camera.open();
    SurfaceTexture dummySurfaceTexture = new SurfaceTexture(0);
    camera.setPreviewTexture(dummySurfaceTexture);
    camera.startPreview();
    camera.takePicture(null, null, jpgCallback);
}

...and the callback:

private Camera.PictureCallback jpgCallback = new Camera.PictureCallback() {
    public void onPictureTaken(byte[] data, Camera camera) {

        releaseCamera();
        savePhoto(data);
};

The photo is taken properly, but my problem is that the EXIF data shows that the orientation is set to "Image Orientation: Top, Left-Hand" regardless of the orientation of the device, so that when I upload the photo it appears upside down or rotated.

Do I really need to capture the device orientation manually (roll, pitch, azimuth) and write the EXIF orientation myself? How does the Camera app automatically write this data correctly? Does anyone know of a way to set this attribute correctly?

EDIT: I can't use the screen orientation as the Activity is locked to portrait mode.

2
I read this answer already, but it cannot be used since my Activity is locked to portrait mode. stackoverflow.com/questions/13245556/…Joshua W

2 Answers

15
votes

You don't have to write the EXIF orientation yourself, but you do need to tell the camera subsystem your device's orientation before you take a picture. It doesn't have access to that information on its own. Once set, the camera subsystem will either set the EXIF field or rotate the image data to orient the image correctly (which is done depends on your specific device).

To tell the camera the orientation you want for your still pictures, use Camera.Parameters.setRotation():

There is reference code in the developer docs for how to use it correctly, which is a bit tricky because the value you set depends on 1) the orientation of your camera sensor and 2) the orientation of your UI, relative to the device's normal orientation. I've copied the sample code here, which uses an OrientationEventListener and assumes you have a Camera.Parameters object called mParameters:

 public void onOrientationChanged(int orientation) {
   if (orientation == ORIENTATION_UNKNOWN) return;
   android.hardware.Camera.CameraInfo info =
        new android.hardware.Camera.CameraInfo();
   android.hardware.Camera.getCameraInfo(cameraId, info);
   orientation = (orientation + 45) / 90 * 90;
   int rotation = 0;
   if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
     rotation = (info.orientation - orientation + 360) % 360;
   } else {  // back-facing camera
     rotation = (info.orientation + orientation) % 360;
   }
   mParameters.setRotation(rotation);
}

Then before your takePicture call, you need to call Camera.setParameters(mParameters).

In your specific case, you might want to query the orientation right before you take the picture, and use the logic in the sample code to calculate the rotation. And then get the camera parameters with Camera.getParameters(), call setRotation, and then call Camera.setParameters().

-3
votes
ExifInterface exif;
        try {
            exif = new ExifInterface(filePath);

            int orientation = exif.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION, 0);
            Log.d("EXIF", "Exif: " + orientation);
            Matrix matrix = new Matrix();
            if (orientation == 6) {
                matrix.postRotate(90);
                Log.d("EXIF", "Exif: " + orientation);
            } else if (orientation == 3) {
                matrix.postRotate(180);
                Log.d("EXIF", "Exif: " + orientation);
            } else if (orientation == 8) {
                matrix.postRotate(270);
                Log.d("EXIF", "Exif: " + orientation);
            }