Android: Bitmaps loaded from gallery are rotated in ImageView

130,371

Solution 1

Have you looked at the EXIF data of the images? It may know the orientation of the camera when the picture was taken.

Solution 2

So, as an example...

First you need to create an ExifInterface:

ExifInterface exif = new ExifInterface(filename);

You can then grab the orientation of the image:

orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

Here's what the orientation values mean: http://sylvana.net/jpegcrop/exif_orientation.html

So, the most important values are 3, 6 and 8. If the orientation is ExifInterface.ORIENTATION_ROTATE_90 (which is 6), for example, you can rotate the image like this:

Matrix matrix = new Matrix();
matrix.postRotate(90);
rotatedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight(), matrix, true);

That's just a quick example, though. I'm sure there are other ways of performing the actual rotation. But you will find those on StackOverflow as well.

Solution 3

This is a full solution (found in the Hackbook example from the Facebook SDK). It has the advantage of not needing access to the file itself. This is extremely useful if you are loading an image from the content resolver thingy (e.g. if your app is responding to a share-photo intent).

public static int getOrientation(Context context, Uri photoUri) {
    /* it's on the external media. */
    Cursor cursor = context.getContentResolver().query(photoUri,
            new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);

    if (cursor.getCount() != 1) {
        return -1;
    }

    cursor.moveToFirst();
    return cursor.getInt(0);
}

And then you can get a rotated Bitmap as follows. This code also scales down the image (badly unfortunately) to MAX_IMAGE_DIMENSION. Otherwise you may run out of memory.

public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException {
    InputStream is = context.getContentResolver().openInputStream(photoUri);
    BitmapFactory.Options dbo = new BitmapFactory.Options();
    dbo.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is, null, dbo);
    is.close();

    int rotatedWidth, rotatedHeight;
    int orientation = getOrientation(context, photoUri);

    if (orientation == 90 || orientation == 270) {
        rotatedWidth = dbo.outHeight;
        rotatedHeight = dbo.outWidth;
    } else {
        rotatedWidth = dbo.outWidth;
        rotatedHeight = dbo.outHeight;
    }

    Bitmap srcBitmap;
    is = context.getContentResolver().openInputStream(photoUri);
    if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
        float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
        float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
        float maxRatio = Math.max(widthRatio, heightRatio);

        // Create the bitmap from file
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = (int) maxRatio;
        srcBitmap = BitmapFactory.decodeStream(is, null, options);
    } else {
        srcBitmap = BitmapFactory.decodeStream(is);
    }
    is.close();

    /*
     * if the orientation is not 0 (or -1, which means we don't know), we
     * have to do a rotation.
     */
    if (orientation > 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(orientation);

        srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
                srcBitmap.getHeight(), matrix, true);
    }

    return srcBitmap;
}

Solution 4

Solved it in my case with this code using help of this post:

            Bitmap myBitmap = getBitmap(imgFile.getAbsolutePath());

            try {
                ExifInterface exif = new ExifInterface(imgFile.getAbsolutePath());
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
                Log.d("EXIF", "Exif: " + orientation);
                Matrix matrix = new Matrix();
                if (orientation == 6) {
                    matrix.postRotate(90);
                }
                else if (orientation == 3) {
                    matrix.postRotate(180);
                }
                else if (orientation == 8) {
                    matrix.postRotate(270);
                }
                myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true); // rotating bitmap
            }
            catch (Exception e) {

            }
            ImageView img = (ImageView) findViewById(R.id.imgTakingPic);
            img.setImageBitmap(myBitmap);

Hope it saves someone's time!

Solution 5

Use a Utility to do the Heavy Lifting.

9re created a simple utility to handle the heavy lifting of dealing with EXIF data and rotating images to their correct orientation.

You can find the utility code here: https://gist.github.com/9re/1990019

Simply download this, add it to your project's src directory and use ExifUtil.rotateBitmap() to get the correct orientation, like so:

String imagePath = photoFile.getAbsolutePath();             // photoFile is a File class.
Bitmap myBitmap  = BitmapFactory.decodeFile(imagePath);

Bitmap orientedBitmap = ExifUtil.rotateBitmap(imagePath, myBitmap);
Share:
130,371

Related videos on Youtube

Manuel
Author by

Manuel

Updated on June 12, 2020

Comments

  • Manuel
    Manuel almost 4 years

    When I load an image from the media gallery into a Bitmap, everything is working fine, except that pictures that were shot with the camera while holding the phone vertically, are rotated so that I always get a horizontal picture even though it appears vertical in the gallery. Why is that and how can I load it correctly?

  • Manuel
    Manuel over 13 years
    You're right, that was of course the solution. I'm gonna post my code as an example in a separate answer, later, but I mark this one as accepted because it got me on the right track.
  • Timmmm
    Timmmm over 12 years
    This isn't going to work properly. What about portrait images? Upside-down images? Using the exif data is much better.
  • Martin
    Martin over 12 years
    It works properly in one of my apps, but of course I have not tested all types of scenarios. @Timmmm could you please be more specific in what scenarios it does not work? I am also quite puzzled about you voting my post down. It seems to be a quite harsh response to an honest try to share a potential solution.
  • Timmmm
    Timmmm over 12 years
    I didn't mean to be harsh; sorry! I just didn't want anyone to copy your solution hoping it would work. As I said, it won't work for portrait or upside-down images. I will add the correct solution as an answer.
  • Martin
    Martin over 12 years
    I see. I will add a comment highlighting your solution above as the prefered one.
  • Display name
    Display name about 12 years
    Here are all the rotation values for the different orientations: 3: 180, 6: 90, 8: 270
  • d60402
    d60402 about 11 years
    Don't use magic numbers when you can use named constants: ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_ROTATE_270.
  • Alex Bonel
    Alex Bonel over 10 years
    Watch out of OutOfMemoryError when use this approach as you hold two bitmaps in memory at the same time.
  • Sazzad Hissain Khan
    Sazzad Hissain Khan over 10 years
    what does that MAX_IMAGE_DIMENDION means?
  • Timmmm
    Timmmm over 10 years
    It's the maximum width or height of the image you get. I.e. Say you only need a 512x512 image, if you open a 24 megapixel image it is much more efficient to open it already subsampled than to open the whole thing and then scale it down - that would probably exhaust all your memory anyway.
  • Chandranshu
    Chandranshu over 10 years
    You can merge all the if conditions together to have a smaller code.
  • Gunnar Bernstein
    Gunnar Bernstein about 10 years
    In my programs I found it useful to define the Bitmap variable in the activity/fragment as private static and setting it to null in the functions. Had less memory troubles then.
  • MSaudi
    MSaudi over 9 years
    This is to rotate but we don't know if image needs rotation.
  • Phil
    Phil over 9 years
    Works for me ! I just resized bitmap to HD format before passing it to ExifUtil.rotateBitmap() to avoid OutOfMemoryError like that : Bitmap resized = Bitmap.createScaledBitmap(myBitmap, 720, 1280, true); photo = ExifUtil.rotateBitmap(picturePath, resized);
  • Joshua Pinter
    Joshua Pinter over 9 years
    @Phil Nice addition. I haven't run into that (I'm using older, shittier Android devices) but that's really good to know.
  • fnc12
    fnc12 over 8 years
    It is smarter to replace MAX_IMAGE_DIMENDION to MAX_IMAGE_WIDTH and MAX_IMAGE_HEIGHT
  • asozcan
    asozcan almost 8 years
    Many thanks for you. After hours of work with cursors and exifs, this saved may day. As you said, in fact exif has true and reliable data instead of cursor returns. Just give it correct path than it works.
  • Lendl Leyba
    Lendl Leyba about 7 years
    you are a hero my friend :)
  • Joshua Pinter
    Joshua Pinter about 7 years
    @klutch You just made my day. :) To be fair, 9re made wrote the utility code so he's the real hero.
  • Sreekanth Karumanaghat
    Sreekanth Karumanaghat almost 7 years
    But just wondering why they need both the imagePath and the original bitmap as arguments, is not just the image path enough :|
  • Joshua Pinter
    Joshua Pinter almost 7 years
    @SreekanthKarumanaghat Great question! I probably knew why this made sense when I was deep into this but right now it seems redundant for me as well. Been spending too much time in React Native perhaps.
  • Cee McSharpface
    Cee McSharpface almost 7 years
    edit suggestions: are there no proper named constants for the orientations 6, 3, 8? could we not skip the new bitmap if no rotation is required?
  • Atul
    Atul over 6 years
    Saved lots of my time :) Many thanks. For those getting null cursor, you may try ExifInterface exif = new ExifInterface(photoUri.getPath()); and then exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1) to get orientation (e.g. ORIENTATION_ROTATE_90, ORIENTATION_ROTATE_180)
  • Adrian
    Adrian over 6 years
    As @d60402 previously said in a comment, you can use named constants: ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_ROTATE_270.
  • CGR
    CGR about 6 years
    Another complete example... stackoverflow.com/questions/14066038/…
  • Miloš Černilovský
    Miloš Černilovský about 5 years
    Always returns 0 in Samsung Galaxy S9.
  • Joshua Pinter
    Joshua Pinter over 4 years
    @SreekanthKarumanaghat Coming back to this, my guess is that the first argument of rotateBitmap is actually where to save the rotated bitmap file to. In our case, we're saving overtop of the existing file so we pass in the imagePath for that argument. If you wanted to save a new file (i.e. to not lose the original file), you could provide a different path to save the rotated bitmap to.
  • Alex Cohn
    Alex Cohn almost 4 years
    Bitmap.createBitmap(…, matrix, …) is a much faster way to create a rotated bitmap
  • JCarlosR
    JCarlosR about 3 years
    Works like a charm! I just wonder why you store the result in a different bitmap instead of assigning it to the same variable. Does it improve performance?