2

I'm finding in onPictureTaken that the bitmap saved is mirrored about the y-axis and rotated 90 degrees clockwise even though the camera preview was not. This is on my Nexus S that's running 2.3.6. The same program running on my Nexus 4 with 4.2 has the resulting bitmap mirrored about the y-axis and rotated 180 degrees clockwise.

This is the code I'm running in onPictureTaken:

@Override
public void onPictureTaken(final byte[] data, Camera camera) {
    Bitmap picture = BitmapFactory.decodeByteArray(data, 0, data.length);
    String path = MediaStore.Images.Media.insertImage(getContentResolver(), picture, "name" , "description");
    Log.e("tag", "path: " + path); // prints something like "path: content://media/external/images/media/819"

    try {
        ExifInterface exif = new ExifInterface(path); // prints this error: "04-25 21:28:21.063: E/JHEAD(12201): can't open 'content://media/external/images/media/819'"
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
        Log.e("tag", "exif orientation: " + orientation); // this is outputting orientation unknown
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Can anyone show me how I rectify this given that I seem to be getting different results from different devices? How do I detect the orientation of the resulting bitmap so that I know to rotate it either 90 or 180 degrees counterclockwise?

[EDIT]

I added some more information using the ExifInterface stuff I've been reading about, but that information doesn't seem to pan out...

boo-urns
  • 10,136
  • 26
  • 71
  • 107
  • i think ExifInterface when i see this, im not sure, just a comment, i never used raw camera – JRowan Apr 26 '13 at 04:18
  • Wait, @JRowan so have you used camera at all? If so, how do you get the bitmap from the camera? – boo-urns Apr 26 '13 at 04:19
  • onActivityResult() with new Intent(MediaStore.ACTION_IMAGE_CAPTURE); – JRowan Apr 26 '13 at 04:20
  • Ah, I see. I'm not using an Intent... The camera preview I'm running is being overlayed by another view... So I don't think I could use that, right? – boo-urns Apr 26 '13 at 04:22
  • i know that what your talking about you can resolve through intent by getting the ExifInterface attribute to the picture, how your doing it maybe you can set ExifInterface attribute, im not sure – JRowan Apr 26 '13 at 04:24
  • do you want me to post the method, maybe its the same – JRowan Apr 26 '13 at 04:26
  • hmm, ExifInterface is giving me orientation unknown, though. – boo-urns Apr 26 '13 at 04:32
  • try to compress the bitmap and then get the path of the file and send that to Exif interface, maybe the path is incorrect, use getAbsolutePath() – JRowan Apr 26 '13 at 04:46

4 Answers4

15

I have put a lot of work into this and thought, I'd share my solution. It is tested on a Motorola Devy, Samsung Xcover 1 and Samsung XCover 2.

As I work with a custom camera preview, the solution basically has two parts. 1. Take care of the camera preview and set rotation of the preview according to device rotation. 2. Once a picture is taken, that is the 'onPictureTaken' callback is invoked rotate the picture by the correct angle, such that it shows what the preview just showed.

1


private void initPreview(int width, int height) {
    if (camera != null && holder.getSurface() != null) {
        try {
            camera.setPreviewDisplay(holder);
        } catch (Throwable t) {
            Log.e("PreviewDemo-surfaceCallback",
              "Exception in setPreviewDisplay()", t);
            Toast.makeText(getContext(), t.getMessage(),
                       Toast.LENGTH_LONG).show();
        }

        try {
            Camera.Parameters parameters=camera.getParameters();

            Camera.Size size=getBestPreviewSize(width, height, parameters);
            Camera.Size pictureSize=getSmallestPictureSize(parameters);

            Display display = windowManager.getDefaultDisplay();
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { // for 2.1 and before
                if (isPortrait(display)) {
                    parameters.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
                } else {
                    parameters.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
                }
            } else { // for 2.2 and later
                switch (display.getRotation()) {
                    case Surface.ROTATION_0: // This is display orientation
                        if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
                        else parameters.setPreviewSize(size.width, size.height);
                        camera.setDisplayOrientation(90);
                        break;
                    case Surface.ROTATION_90:
                        if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
                        else parameters.setPreviewSize(size.width, size.height);
                        camera.setDisplayOrientation(0);
                        break;
                    case Surface.ROTATION_180:
                        if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
                        else parameters.setPreviewSize(size.width, size.height);
                        camera.setDisplayOrientation(270);
                        break;
                    case Surface.ROTATION_270:
                        if (size.height > size.width) parameters.setPreviewSize(size.height, size.width);
                        else parameters.setPreviewSize(size.width, size.height);
                        camera.setDisplayOrientation(180);
                        break;
                }
            }

            parameters.setPictureSize(pictureSize.width, pictureSize.height);
            //parameters.setPictureFormat(ImageFormat.JPEG);
            camera.setParameters(parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Your 'surfaceChanged' method, in your camera preview (SurfaceView), you should look like this:


public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        stopPreview();
        initPreview(w, h);
        startPreview();
    }

where

stopPreview:

private void stopPreview() {
    if (camera != null) {
        camera.stopPreview();
    }
}

startPreview:

private void startPreview() {
    if (camera != null) {
        camera.startPreview();
    }
}

2


In your 'onPictureTaken' callback rotate the picture, using the following code:

Display display = getWindowManager().getDefaultDisplay();
    int rotation = 0;
    switch (display.getRotation()) {
        case Surface.ROTATION_0: // This is display orientation
        rotation = 90;
        break;
    case Surface.ROTATION_90:
        rotation = 0;
        break;
    case Surface.ROTATION_180:
        rotation = 270;
        break;
    case Surface.ROTATION_270:
        rotation = 180;
            break;
     }

     Bitmap bitmap = BitmapTools.toBitmap(data);
     bitmap = BitmapTools.rotate(bitmap, rotation);

BitmapTools.java

public class BitmapTools {

        public static Bitmap toBitmap(byte[] data) {
            return BitmapFactory.decodeByteArray(data , 0, data.length);
        }

        public static Bitmap rotate(Bitmap in, int angle) {
            Matrix mat = new Matrix();
            mat.postRotate(angle);
            return Bitmap.createBitmap(in, 0, 0, in.getWidth(), in.getHeight(), mat, true);
        }
    }
Mohsinali
  • 543
  • 7
  • 14
fabian
  • 642
  • 6
  • 18
  • 1
    If you are doing this and using the rear-facing camera, you have to do the rotation in step 2 differently. – Liron Dec 25 '13 at 21:04
  • 1
    This is a great, detailed answer. I didn't use your code exactly, but it definitely helped me understand the problem, so I'm marking this as the accepted answer. I hope it helps out other developers who have this problem. – boo-urns Dec 30 '13 at 03:29
  • 1
    In this answer [link](http://stackoverflow.com/a/26141254/1332549) explain it better. – MiguelHincapieC Mar 09 '15 at 17:33
0

here you go check this out, save the picture and maybe this will work and remember if(bitmap.getWidth > bitmap.getHeight()) as another check

public static int getExifRotation(String imgPath) 
    {
        try 
        {
            ExifInterface exif = new ExifInterface(imgPath);
            String rotationAmount = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
            if (!TextUtils.isEmpty(rotationAmount)) 
            {
                int rotationParam = Integer.parseInt(rotationAmount);
                switch (rotationParam) 
                {
                    case ExifInterface.ORIENTATION_NORMAL:
                        return 0;
                    case ExifInterface.ORIENTATION_ROTATE_90:
                        return 90;
                    case ExifInterface.ORIENTATION_ROTATE_180:
                        return 180;
                    case ExifInterface.ORIENTATION_ROTATE_270:
                        return 270;
                    default:
                        return 0;
                }
            } 
            else 
            {
                return 0;
            }
        }
        catch (Exception ex) 
        {
            return 0;
        }
    }
JRowan
  • 6,824
  • 8
  • 40
  • 59
  • I like the `if(width > height)` check, but what if a different phone rotates the image 270 degrees? *sigh* – boo-urns Apr 26 '13 at 04:33
  • you tried this method, i use this because when i take picture the camera takes it and rotates it 90 degrees how its saved but when displayed in gallery or whatever it displays how i took the picture, so opening it programmatically i have to check like this – JRowan Apr 26 '13 at 04:38
  • and i use matrix.preRotate() or matrix.postRotate() with Bitmap.createBitmap() after check depending – JRowan Apr 26 '13 at 04:39
0

you must read about ExifInterface to resolve this issue.

i have this function in my application to check weather weather image taken from camera needs to be rotateed or not.

      if(ExifNeedsRotate(GetPathFromUri(context, selectedImage))){
    // Rotate your bitmap using the Matrix
     }


public static boolean ExifNeedsRotate(String paramString){

      if (android.os.Build.VERSION.SDK_INT >= 5){

          try
          {

                Class localClass = Class.forName("android.media.ExifInterface");
                Class[] arrayOfClass1 = new Class[1];
                arrayOfClass1[0] = String.class;
                Constructor localConstructor = localClass.getConstructor(arrayOfClass1);
                Class[] arrayOfClass2 = new Class[1];
                arrayOfClass2[0] = String.class;
                Method localMethod = localClass.getMethod("getAttribute", arrayOfClass2);
                Object[] arrayOfObject1 = new Object[1];
                arrayOfObject1[0] = paramString;
                Object localObject1 = localConstructor.newInstance(arrayOfObject1);
                Object[] arrayOfObject2 = new Object[1];
                arrayOfObject2[0] = "Orientation";
                Object localObject2 = localMethod.invoke(localObject1, arrayOfObject2);
                if (localObject2 != null){
                      boolean bool = localObject2.equals("6");
                      if (bool)
                         return true;
                }

          }
          catch (Exception localException){
              return false;
          }

    }

    return false;

}

Pass the path of the ImageUri as a input.

 public static String GetPathFromUri(Context paramContext, Uri paramUri)
  {
       String str;
       try
       {
            if (paramUri.toString().startsWith("file:")){
                    str = paramUri.getPath();
            }
            else
            {
                    str = null;
                    String[] arrayOfString = new String[1];
                    arrayOfString[0] = "_data";
                    Cursor localCursor = paramContext.getContentResolver().query(paramUri, arrayOfString, null, null, null);
                    if (localCursor != null)
                    {
                            localCursor.moveToFirst();
                            int i = localCursor.getColumnIndex(arrayOfString[0]);
                            if ((localCursor.getCount() >= 1) && (localCursor.getColumnCount() >= i + 1))
                                str = localCursor.getString(i);
                            localCursor.close();
                    }
            }

      }
      catch (Exception localException){
          str = null;
      }

      return str;
  }
strike
  • 1,031
  • 8
  • 24
0

Instead of rotating the picture explicitly in the picture taken callback, you can configure the camera to have picture rotated for you when the picture is taken.

camera.SetDisplayOrientation(degrees) //sets the orientation in the preview

while

cameraParameters.SetRotation(degress) //rotates the actual captured image
zafar
  • 1,965
  • 1
  • 15
  • 14