464

I'm capturing an image and setting it to image view.

public void captureImage() {

    Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
    File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
    imageUri = Uri.fromFile(filePhoto);
    MyApplicationGlobal.imageUri = imageUri.getPath();
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intentCamera, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intentFromCamera) {
    super.onActivityResult(requestCode, resultCode, intentFromCamera);

    if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {

        if (intentFromCamera != null) {
            Bundle extras = intentFromCamera.getExtras();
            if (extras.containsKey("data")) {
                bitmap = (Bitmap) extras.get("data");
            }
            else {
                bitmap = getBitmapFromUri();
            }
        }
        else {
            bitmap = getBitmapFromUri();
        }
        // imageView.setImageBitmap(bitmap);
        imageView.setImageURI(imageUri);
    }
    else {
    }
}

public Bitmap getBitmapFromUri() {

    getContentResolver().notifyChange(imageUri, null);
    ContentResolver cr = getContentResolver();
    Bitmap bitmap;

    try {
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
        return bitmap;
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

But the problem is, the image on some devices every time it gets rotated. For example, on a Samsung device it works good, but on a Sony Xperia the image gets rotated by 90 degrees and on Toshiba Thrive (tablet) by 180 degrees.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shirish Herwade
  • 11,461
  • 20
  • 72
  • 111
  • 1
    try this in you activity menifest android:configChanges="orientation" android:screenOrientation="portrait" – Narendra Pal Dec 28 '12 at 07:32
  • @nick it doesn't work, now the image gets rotated to 90 degrees rather than 180 degrees on tab – Shirish Herwade Dec 28 '12 at 08:19
  • 1
    as i think when you use the internal intent for dealing with the camera app,then it rotates image.This is depends upon how you hold the device to capture image. So you can restrict the user to take image in particular manner means the user will always capture image by holding the device in portrait or landscape. After that you can change it to specific angle to get image as you want.. OR ANOTHER OPTION, MAKE YOUR OWN CAMERA APP. – Narendra Pal Dec 28 '12 at 08:38
  • @nick "you can restrict the user to take image in particular manner" means is it same as setting orientation ="potrait"? And how to "After that you can change it to specific angle to get image as you want" achieve? Please can you give some useful links – Shirish Herwade Dec 28 '12 at 09:38
  • 3
    I believe the capture intent always brings up the default camera app which has specific orientation on each device and consequently - fixed photo orientation. It does not depend on the way the user hold the device or the orientation of your activity that invoked the intent. – Alex Cohn Dec 28 '12 at 17:45
  • 1
    To avoid Storage permission, see [this](https://stackoverflow.com/a/31720143/6131611) or [this](https://stackoverflow.com/a/8914291/6131611) answer or [use Glide](https://stackoverflow.com/a/47630783/6131611) – Pavel Sep 23 '18 at 00:35
  • Not exactly duplicate (different list of devices, and different root cause), but highly relevant: *[Images taken with ACTION_IMAGE_CAPTURE always returns 1 for ExifInterface.TAG_ORIENTATION on some Gingerbread devices](https://stackoverflow.com/q/8450539/192373)*. – Alex Cohn Oct 13 '18 at 12:02
  • Another relevant edge case: bug on Nexus 5 with some updates for the system camera app, *[Android camera saves photo without ExIf](https://stackoverflow.com/q/31171204/192373)*. – Alex Cohn Oct 13 '18 at 12:35
  • 1
    Anyone found any solution for ORIENTATION_UNDEFINED, as on some devices (Android 8 Emulator) image gets rotated whereas on some devices (Android 9 Emulator) it doesn't with the same Orientation value. How to know if the image needs to be rotated or not? – Harshit Saxena Aug 24 '21 at 10:31

23 Answers23

537

Most phone cameras are landscape, meaning if you take the photo in portrait, the resulting photos will be rotated 90 degrees. In this case, the camera software should populate the Exif data with the orientation that the photo should be viewed in.

Note that the below solution depends on the camera software/device manufacturer populating the Exif data, so it will work in most cases, but it is not a 100% reliable solution.

ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                                     ExifInterface.ORIENTATION_UNDEFINED);

Bitmap rotatedBitmap = null;
switch(orientation) {

    case ExifInterface.ORIENTATION_ROTATE_90:
        rotatedBitmap = rotateImage(bitmap, 90);
        break;

    case ExifInterface.ORIENTATION_ROTATE_180:
        rotatedBitmap = rotateImage(bitmap, 180);
        break;

    case ExifInterface.ORIENTATION_ROTATE_270:
        rotatedBitmap = rotateImage(bitmap, 270);
        break;

    case ExifInterface.ORIENTATION_NORMAL:
    default:
        rotatedBitmap = bitmap;
}

Here is the rotateImage method:

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
                               matrix, true);
}
Jason Robinson
  • 31,005
  • 19
  • 77
  • 131
  • 1
    From @JasonRobinson code's I learn how to get the actual orientation and by combining with [these code](http://androidtrainningcenter.blogspot.com/2012/05/bitmap-operations-like-re-sizing.html) I'm successfully manage the orientation. – Raditya Kurnianto Sep 21 '14 at 04:31
  • Second option of `exif.getAttributeInt` using `ExifInterface.ORIENTATION_UNDEFINED` is almost same, as the second parameter is default value in case the function fails to provide the value. – Darpan Sep 04 '15 at 10:32
  • 10
    This code is for a image already been written to disk, right? I get no results using this method for bitmap about to be written to disk. – Thracian Apr 03 '17 at 13:07
  • @FatihOzcan looks like `ExitInterface` supports File and InputStream, so you could [create an InputStream](http://stackoverflow.com/a/7698445/291827). – Jason Robinson Apr 03 '17 at 17:54
  • @Jason Robinson, this code is very useful for getting image from disk and rotating it accordingly but when you are doing a hardwork like i do, i get 4k image from camera and merge it with canvas drawing after scaling drawing from camera preview up to 4k, and save it to disk. Opening a stream again is not efficient, rotating the bitmap with postRotate() method of matix before saving the image sufficient for me. In my case, user should select rotation manually – Thracian Apr 03 '17 at 18:56
  • @FatihOzcan It should be the same speed regardless of the photo size because it's only reading the Exif headers. It's not reading the pixel data. If you have a Bitmap that you have manually captured, then there would be no Exif data associated with it anyway. – Jason Robinson Apr 03 '17 at 22:14
  • @Jason Robinson, it should be same speed but opening a stream after saving the bitmap as png or jpeg is extra work. Yes, exactly what i was asking the first place, without saving the image you don't have exif info. I had to rotate image before saving it disk because some models like Nexus 5x have upside down orientation. Thanks for the answers. – Thracian Apr 04 '17 at 07:24
  • bitmap is an int but rotateImage takes a Bitmap. What should I do? – user1744228 Aug 03 '17 at 18:40
  • @user1744228 you pass in your source Bitmap in that method. – Jason Robinson Aug 04 '17 at 01:32
  • it works like a charm on my s3 ( kitkat ) note 3 ( lollipop) nexus 5 (marshmallow) but not on my s6edge (nougat ) , can someone tell me why ? – ismail alaoui May 18 '18 at 01:08
  • More here --> https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html – Sotti Nov 02 '18 at 17:33
  • 13
    It is always return me 0 value. Please tell how to get actual orientation. – Anurag Srivastava Nov 13 '18 at 06:33
  • 12
    Getting 0 always, any idea why? – Navya Ramesan May 23 '19 at 14:16
  • It crops the image by default when the image orientation actually tooks place inside switch. – Umair Nov 05 '19 at 09:38
  • Currently exist any solution? – luke cross May 22 '20 at 18:37
  • what is "photoPath" variable? – luke cross Jul 28 '20 at 18:07
  • 4
    Note: use androidx.exifinterface.media.ExifInterface instead of android.media.ExifInterface – Andrew Oct 09 '20 at 08:31
  • Using `androidx.exifinterface` works on my own phone and all emulators, but doesn't work on Samsung devices, returning ORIENTATION_NORMAL when taking a photo in portrait, when the value should be ORIENTATION_270. Easy to reproduce on [Samsung's Remote Test Lab](https://developer.samsung.com/remotetestlab/rtlDeviceList.action). – 0101100101 Mar 01 '21 at 02:54
  • @lukecross too simple man, it's just path of image.. – Vivek Thummar Mar 02 '21 at 11:42
  • Any info about how to use this code, i i am uploading multiple images with for loop ? – MiniDev99 Dec 04 '21 at 23:07
221

By combining Jason Robinson's answer with Felix's answer and filling the missing parts, here is the final complete solution for this issue that will do the following after testing it on Android Android 4.1 (Jelly Bean), Android 4.4 (KitKat) and Android 5.0 (Lollipop).

Steps

  1. Scale down the image if it was bigger than 1024x1024.

  2. Rotate the image to the right orientation only if it was rotate 90, 180 or 270 degree.

  3. Recycle the rotated image for memory purposes.

Here is the code part:

Call the following method with the current Context and the image URI that you want to fix

/**
 * This method is responsible for solving the rotation issue if exist. Also scale the images to
 * 1024x1024 resolution
 *
 * @param context       The current context
 * @param selectedImage The Image URI
 * @return Bitmap image results
 * @throws IOException
 */
public static Bitmap handleSamplingAndRotationBitmap(Context context, Uri selectedImage)
        throws IOException {
    int MAX_HEIGHT = 1024;
    int MAX_WIDTH = 1024;

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(context, img, selectedImage);
    return img;
}

Here is the CalculateInSampleSize method from the pre mentioned source:

/**
  * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
  * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
  * the closest inSampleSize that will result in the final decoded bitmap having a width and
  * height equal to or larger than the requested width and height. This implementation does not
  * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
  * results in a larger bitmap which isn't as useful for caching purposes.
  *
  * @param options   An options object with out* params already populated (run through a decode*
  *                  method with inJustDecodeBounds==true
  * @param reqWidth  The requested width of the resulting bitmap
  * @param reqHeight The requested height of the resulting bitmap
  * @return The value to be used for inSampleSize
  */
private static int calculateInSampleSize(BitmapFactory.Options options,
                                         int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
        // with both dimensions larger than or equal to the requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}

Then comes the method that will check the current image orientation to decide the rotation angle

 /**
 * Rotate an image if required.
 *
 * @param img           The image bitmap
 * @param selectedImage Image URI
 * @return The resulted Bitmap after manipulation
 */
private static Bitmap rotateImageIfRequired(Context context, Bitmap img, Uri selectedImage) throws IOException {

InputStream input = context.getContentResolver().openInputStream(selectedImage);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
    ei = new ExifInterface(input);
else
    ei = new ExifInterface(selectedImage.getPath());
        
    int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            return rotateImage(img, 90);
        case ExifInterface.ORIENTATION_ROTATE_180:
            return rotateImage(img, 180);
        case ExifInterface.ORIENTATION_ROTATE_270:
            return rotateImage(img, 270);
        default:
            return img;
    }
}

Finally the rotation method itself

private static Bitmap rotateImage(Bitmap img, int degree) {
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
    img.recycle();
    return rotatedImg;
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Sami Eltamawy
  • 9,874
  • 8
  • 48
  • 66
  • 1
    the method rotateImageIfRequired() works very well.. thanks !! – mapo Oct 03 '18 at 18:46
  • 11
    Doesn't work for me. Sometimes my phone gives portrait, sometimes landscape photos, but detected orientation is always 0 degrees. – Makalele Oct 27 '18 at 09:59
  • @Makalele Is this issue also occur while taking photos and attaching through WhatsApp? – Manoj Perumarath Feb 13 '19 at 11:31
  • I don't use WhatsApp so I cannot say, but most probably yes. That's because it even happens in stock photo app (Google Stock Camera). – Makalele Feb 13 '19 at 14:40
  • 3
    Kotlin: https://gist.github.com/fada21/feadb8d2feb925a821b6eb233692d31d – fada21 Apr 24 '19 at 09:26
  • I have images on server. I try to use this code but I have a java.io.FileNotFoundException : No content provider: "http://xxxxxxxxxxxxxxx/appli/android_connect/Etablissement/abordage.jpg" after pass uri in parameter of handleSamplingAndRotationBitmap : url = new URL(myUrlStr); uri = Uri.parse( url.toURI().toString() ); Can you help me about the format of URI ? – Mister Fisher Nov 19 '19 at 10:42
  • Using `androidx.exifinterface` works on my own phone and all emulators, but doesn't work on Samsung devices, returning ORIENTATION_NORMAL when taking a photo in portrait, when the value should be ORIENTATION_270. Easy to reproduce on [Samsung's Remote Test Lab](https://developer.samsung.com/remotetestlab/rtlDeviceList.action). – 0101100101 Mar 01 '21 at 02:55
  • `img.recycle();` in `rotateImage` can cause issues if you pass 0 degrees, as you will be recycling the same bitmap you are returning. I added a check to just return the provided bitmap when degrees is 0. – tagy22 Oct 18 '21 at 14:10
  • Ended up using just your `rotateImageIfRequired` function in concert with my existing functions but works great, thank you! – Nickthegoatboy Nov 11 '21 at 18:15
50

It's easy to detect the image orientation and replace the bitmap using:

 /**
 * Rotate an image if required.
 * @param img
 * @param selectedImage
 * @return
 */
private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage) {

    // Detect rotation
    int rotation = getRotation(context, selectedImage);
    if (rotation != 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
    else{
        return img;
    }
}

/**
 * Get the rotation of the last image added.
 * @param context
 * @param selectedImage
 * @return
 */
private static int getRotation(Context context,Uri selectedImage) {

    int rotation = 0;
    ContentResolver content = context.getContentResolver();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                       new String[] { "orientation", "date_added" },
                                       null, null, "date_added desc");

    if (mediaCursor != null && mediaCursor.getCount() != 0) {
        while(mediaCursor.moveToNext()){
            rotation = mediaCursor.getInt(0);
            break;
        }
    }
    mediaCursor.close();
    return rotation;
}

To avoid Out of memories with big images, I'd recommend you to rescale the image using:

private static final int MAX_HEIGHT = 1024;
private static final int MAX_WIDTH = 1024;
public static Bitmap decodeSampledBitmap(Context context, Uri selectedImage)
    throws IOException {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(img, selectedImage);
    return img;
}

It's not posible to use ExifInterface to get the orientation because an Android OS issue: https://code.google.com/p/android/issues/detail?id=19268

And here is calculateInSampleSize

/**
 * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
 * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
 * the closest inSampleSize that will result in the final decoded bitmap having a width and
 * height equal to or larger than the requested width and height. This implementation does not
 * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
 * results in a larger bitmap which isn't as useful for caching purposes.
 *
 * @param options   An options object with out* params already populated (run through a decode*
 *                  method with inJustDecodeBounds==true
 * @param reqWidth  The requested width of the resulting bitmap
 * @param reqHeight The requested height of the resulting bitmap
 * @return The value to be used for inSampleSize
 */
public static int calculateInSampleSize(BitmapFactory.Options options,
                                        int reqWidth, int reqHeight) {

    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
        // with both dimensions larger than or equal to the requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Felix
  • 2,705
  • 1
  • 23
  • 14
  • 1
    what is calculateInSampleSize method here – madhu kotagiri Jul 14 '14 at 06:54
  • 1
    @madhukotagiri here you have an example of implementantion for calculateInSampleSize: https://gist.github.com/anonymous/b7ea25fc2bbc54e43616 – Felix Jul 14 '14 at 15:43
  • Thanks man, you're definitely the one! I'm just wondering how much the resizing will be useful, if the operation is performed just occasionally. – Marino Aug 13 '14 at 22:46
  • 4
    Uri selectedImage parameter not used in getRotation(...) method. How do we need use it? Thank you. – valerybodak Feb 06 '15 at 15:22
  • `rotateImageIfRequired(img, selectedImage);` you are passing two parameters , whereas in your method definition you have defined three `private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage)` , What do I pass in as context from my activity? – Romantic Electron Apr 23 '15 at 05:42
  • Struggled for a while with rotation method since on some device when tacking photo in portrait mode simple rotation makes image stretched. The solution found was: `code Matrix matrix = new Matrix(); matrix.postTranslate(-width / 2, -height / 2); matrix.postRotate(angle); matrix.postTranslate(width / 2, height / 2); ` – fox Sep 21 '15 at 14:36
  • 1
    Parameter 'selectedImage' doesn't seem to be used anywhere. Any reason to be there? – Alex Oct 11 '15 at 22:03
  • I reckon we need to use selectedImage otherwise it is not giving proper orientation. – Tushar Gogna Nov 21 '18 at 13:40
24

One line solution:

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Or

Picasso.with(context).load("file:" + photoPath).into(imageView);

This will autodetect rotation and place image in correct orientation

Picasso is a very powerful library for handling images in your app includes: Complex image transformations with minimal memory use.

voytez
  • 1,814
  • 16
  • 16
  • 14
    It just loads the image into a view, it doesn't give you a bitmap or a file you can manipulate or upload to a server. – Mickäel A. Jul 12 '16 at 11:54
  • 6
    Its displaying image clicked as it is. It is not rotating as required. – seema Feb 24 '17 at 08:24
  • 3
    @Flawyte you can do that by loading file into target instead of view with callback that returns cropped/resized bitmap: Picasso.with(this).load(cropUriToLoad.resize(1080, 810).centerInside().into(target); where target = new Target() { Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { – voytez Aug 09 '17 at 21:30
  • the problem i am still facing is it takes few seconds to display image – Anu Nov 30 '18 at 07:24
  • 2
    not working for me.. this issue exists in Picasso as well – NehaK Jun 24 '20 at 13:34
  • 1
    Nope it doesn't work! Oneplus Android 11 and Samsung Tabs – sud007 Jul 15 '22 at 05:08
16

So first thing, you sholuld know that since Android 7.0 we have to use FileProvider and something called ContentUri, otherwise you will get an annoying error trying to invoke your Intent. This is sample code:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriFromPath(context, "[Your path to save image]"));
startActivityForResult(intent, CAPTURE_IMAGE_RESULT);

Method getUriFromPath(Context, String) basis on user version of Android create FileUri (file://...) or ContentUri (content://...) and there it is:

public Uri getUriFromPath(Context context, String destination) {
    File file =  new File(destination);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
    } else {
        return Uri.fromFile(file);
    }
}

After onActivityResult you can catch that uri where image is saved by camera, but now you have to detect camera rotation, here we will use moddified @Jason Robinson answer:

First we need to create ExifInterface based on Uri

@Nullable
public ExifInterface getExifInterface(Context context, Uri uri) {
    try {
        String path = uri.toString();
        if (path.startsWith("file://")) {
            return new ExifInterface(path);
        }
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (path.startsWith("content://")) {
                InputStream inputStream = context.getContentResolver().openInputStream(uri);
                return new ExifInterface(inputStream);
            }
        }
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Above code can be simplified, but i want to show everything. So from FileUri we can create ExifInterface based on String path, but from ContentUri we can't, Android doesn't support that.

In that case we have to use other constructor based on InputStream. Remember this constructor isn't available by default, you have to add additional library:

compile "com.android.support:exifinterface:XX.X.X"

Now we can use getExifInterface method to get our angle:

public float getExifAngle(Context context, Uri uri) {
    try {
        ExifInterface exifInterface = getExifInterface(context, uri);
        if(exifInterface == null) {
            return -1f;
        }

        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return 90f;
            case ExifInterface.ORIENTATION_ROTATE_180:
                return 180f;
            case ExifInterface.ORIENTATION_ROTATE_270:
                return 270f;
            case ExifInterface.ORIENTATION_NORMAL:
                return 0f;
            case ExifInterface.ORIENTATION_UNDEFINED:
                return -1f;
            default:
                return -1f;
        }
    }
    catch (Exception e) {
        e.printStackTrace();
        return -1f;
    }
}

Now you have Angle to properly rotate you image.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Artur Szymański
  • 1,639
  • 1
  • 19
  • 22
  • 4
    implementation 'androidx.exifinterface:exifinterface:X.X.X' This is for those who are using androidx. thank you for your posting – Doongsil Aug 22 '19 at 02:41
12
// Try this way,hope this will help you to solve your problem...

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center">
        <ImageView
            android:id="@+id/imgFromCameraOrGallery"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:src="@drawable/ic_launcher"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/btnCamera"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="Camera"/>
        <Button
            android:id="@+id/btnGallery"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_marginLeft="5dp"
            android:layout_height="wrap_content"
            android:text="Gallery"/>

    </LinearLayout>
</LinearLayout>

MainActivity.java

    public class MainActivity extends Activity {

    private ImageView imgFromCameraOrGallery;
    private Button btnCamera;
    private Button btnGallery;

    private String imgPath;
    final private int PICK_IMAGE = 1;
    final private int CAPTURE_IMAGE = 2;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imgFromCameraOrGallery = (ImageView) findViewById(R.id.imgFromCameraOrGallery);
        btnCamera = (Button) findViewById(R.id.btnCamera);
        btnGallery = (Button) findViewById(R.id.btnGallery);

        btnCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, setImageUri());
                startActivityForResult(intent, CAPTURE_IMAGE);
            }
        });

        btnGallery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, ""), PICK_IMAGE);
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == CAPTURE_IMAGE) {
                setCapturedImage(getImagePath());
            } else if (requestCode == PICK_IMAGE) {
                imgFromCameraOrGallery.setImageBitmap(BitmapFactory.decodeFile(getAbsolutePath(data.getData())));
            }
        }

    }

    private String getRightAngleImage(String photoPath) {

        try {
            ExifInterface ei = new ExifInterface(photoPath);
            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            int degree = 0;

            switch (orientation) {
                case ExifInterface.ORIENTATION_NORMAL:
                    degree = 0;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
                case ExifInterface.ORIENTATION_UNDEFINED:
                    degree = 0;
                    break;
                default:
                    degree = 90;
            }

            return rotateImage(degree,photoPath);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return photoPath;
    }

    private String rotateImage(int degree, String imagePath){

        if(degree<=0){
            return imagePath;
        }
        try{
            Bitmap b= BitmapFactory.decodeFile(imagePath);

            Matrix matrix = new Matrix();
            if(b.getWidth()>b.getHeight()){
                matrix.setRotate(degree);
                b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
                        matrix, true);
            }

            FileOutputStream fOut = new FileOutputStream(imagePath);
            String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
            String imageType = imageName.substring(imageName.lastIndexOf(".") + 1);

            FileOutputStream out = new FileOutputStream(imagePath);
            if (imageType.equalsIgnoreCase("png")) {
                b.compress(Bitmap.CompressFormat.PNG, 100, out);
            }else if (imageType.equalsIgnoreCase("jpeg")|| imageType.equalsIgnoreCase("jpg")) {
                b.compress(Bitmap.CompressFormat.JPEG, 100, out);
            }
            fOut.flush();
            fOut.close();

            b.recycle();
        }catch (Exception e){
            e.printStackTrace();
        }
        return imagePath;
    }

    private void setCapturedImage(final String imagePath){
        new AsyncTask<Void,Void,String>(){
            @Override
            protected String doInBackground(Void... params) {
                try {
                    return getRightAngleImage(imagePath);
                }catch (Throwable e){
                    e.printStackTrace();
                }
                return imagePath;
            }

            @Override
            protected void onPostExecute(String imagePath) {
                super.onPostExecute(imagePath);
                imgFromCameraOrGallery.setImageBitmap(decodeFile(imagePath));
            }
        }.execute();
    }

    public Bitmap decodeFile(String path) {
        try {
            // Decode deal_image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, o);
            // The new size we want to scale to
            final int REQUIRED_SIZE = 1024;

            // Find the correct scale value. It should be the power of 2.
            int scale = 1;
            while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
                scale *= 2;
            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeFile(path, o2);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getAbsolutePath(Uri uri) {
        if(Build.VERSION.SDK_INT >= 19){
            String id = "";
            if(uri.getLastPathSegment().split(":").length > 1)
                id = uri.getLastPathSegment().split(":")[1];
            else if(uri.getLastPathSegment().split(":").length > 0)
                id = uri.getLastPathSegment().split(":")[0];
            if(id.length() > 0){
                final String[] imageColumns = {MediaStore.Images.Media.DATA };
                final String imageOrderBy = null;
                Uri tempUri = getUri();
                Cursor imageCursor = getContentResolver().query(tempUri, imageColumns, MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
                if (imageCursor.moveToFirst()) {
                    return imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                }else{
                    return null;
                }
            }else{
                return null;
            }
        }else{
            String[] projection = { MediaStore.MediaColumns.DATA };
            Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null) {
                int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                cursor.moveToFirst();
                return cursor.getString(column_index);
            } else
                return null;
        }

    }

    private Uri getUri() {
        String state = Environment.getExternalStorageState();
        if(!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED))
            return MediaStore.Images.Media.INTERNAL_CONTENT_URI;

        return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }

    public Uri setImageUri() {
        Uri imgUri;
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            File file = new File(Environment.getExternalStorageDirectory() + "/DCIM/",getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis() + ".png");
            imgUri = Uri.fromFile(file);
            imgPath = file.getAbsolutePath();
        }else {
            File file = new File(getFilesDir() ,getString(R.string.app_name) + Calendar.getInstance().getTimeInMillis()+ ".png");
            imgUri = Uri.fromFile(file);
            this.imgPath = file.getAbsolutePath();
        }
        return imgUri;
    }

    public String getImagePath() {
        return imgPath;
    }
}
Haresh Chhelana
  • 24,720
  • 5
  • 57
  • 67
12

You can just read the orientation of the camera sensor like indicated by Google in the documentation: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html

SENSOR_ORIENTATION

Added in API level 21
Key<Integer> SENSOR_ORIENTATION
Clockwise angle through which the output image needs to be rotated to be upright on the device screen in its native orientation.

Also defines the direction of rolling shutter readout, which is from top to bottom in the sensor's coordinate system.

Units: Degrees of clockwise rotation; always a multiple of 90

Range of valid values:
0, 90, 180, 270

This key is available on all devices.

Sample code:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
int orientation = 0;
try {
    String cameraId = manager.getCameraIdList()[0];
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
    orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
}
catch (Exception e)
{
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Stephen Shi
  • 210
  • 3
  • 4
7

Jason Robinson's answer and Sami Eltamawy answer are excelent.

Just an improvement to complete the aproach, you should use compat ExifInterface.

com.android.support:exifinterface:${lastLibVersion}

You will be able to instantiate the ExifInterface(pior API <24) with InputStream (from ContentResolver) instead of uri paths avoiding "File not found exceptions"

https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html

Ricard
  • 1,223
  • 1
  • 13
  • 17
5

I created a Kotlin extension function that simplifies the operation for Kotlin developers based on @Jason Robinson's answer. I hope it helps.

fun Bitmap.fixRotation(uri: Uri): Bitmap? {

    val ei = ExifInterface(uri.path)

    val orientation: Int = ei.getAttributeInt(
        ExifInterface.TAG_ORIENTATION,
        ExifInterface.ORIENTATION_UNDEFINED
    )

    return when (orientation) {
        ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage( 90f)
        ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage( 180f)
        ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage( 270f)
        ExifInterface.ORIENTATION_NORMAL -> this
        else -> this
    }
}

fun Bitmap.rotateImage(angle: Float): Bitmap? {
    val matrix = Matrix()
    matrix.postRotate(angle)
    return Bitmap.createBitmap(
        this, 0, 0, width, height,
        matrix, true
    )
}
seyfullah.bilgin
  • 1,421
  • 14
  • 16
  • 3
    awesome but suffers from the same issue as all solutions, as extension or a function - doesn't work on Android 10. – Lior Iluz Jan 07 '20 at 13:37
  • In my case it works fine on Android 10. – Xam Mar 15 '22 at 07:40
  • Doesn't work for me on Android 11, One plus device. I receive `ExifInterface.ORIENTATION_UNDEFINED` sure from metadata, but then it rotates my image to further 90 degree and image goes upside down. I noticed this issue creates Left or right rotations by default, hence in my code it went upside-down on automatic rotation. – sud007 Jul 15 '22 at 05:03
5

here a solution that is based on the ones above but only needs context and the image file as inputs

public static Bitmap rectifyImage(Context context,File imageFile){
    Bitmap originalBitmap= BitmapFactory.decodeFile(imageFile.getAbsolutePath());
    try{
        Uri uri=Uri.fromFile(imageFile);
        InputStream input = context.getContentResolver().openInputStream(uri);
        ExifInterface ei;
        
        if (Build.VERSION.SDK_INT > 23)
            ei = new ExifInterface(input);
        else
            ei = new ExifInterface(uri.getPath());

        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return rotateImage(originalBitmap, 90);
            case ExifInterface.ORIENTATION_ROTATE_180:
                return rotateImage(originalBitmap, 180);
            case ExifInterface.ORIENTATION_ROTATE_270:
                return rotateImage(originalBitmap, 270);
            default:
                return originalBitmap;
        }
    }catch (Exception e){
        return originalBitmap;
    }
}

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
            matrix, true);
}
quealegriamasalegre
  • 2,887
  • 1
  • 13
  • 35
4

Normally it is recommended to solve the problem with the ExifInterface, like @Jason Robinson did suggest. If this approach doesn't work, you could try to look up the Orientation of the latest image taken...

private int getImageOrientation(){
    final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION };
    final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
    Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            imageColumns, null, null, imageOrderBy);

    if(cursor.moveToFirst()){
        int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
        cursor.close();
        return orientation;
    } else {
        return 0;
    }
}
Chris
  • 4,403
  • 4
  • 42
  • 54
  • 1
    I think this code only detects in what degree rotation occurred. Now I'm able to do that, but unable in next task i.e. to rotate the image. – Shirish Herwade Jan 03 '13 at 10:31
  • You're right, but you didn't ask for the rotation in this Thread, so let's keep it clean ;) That's why I put my answer to your rotating problem into your other Thread... Hope that helps, it does work for me: http://stackoverflow.com/questions/14123809/code-to-rotate-image-captured-by-camera-intent-not-working-in-android – Chris Jan 03 '13 at 11:36
  • 1
    `MediaStore.Images.ImageColumns.ORIENTATION` available only in Android 10 and upwards. – Ashutosh Sagar Nov 20 '20 at 11:29
4

Sadly, @jason-robinson answer above didn't work for me.

Although the rotate function works perfectly:

public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix,
            true);
}

I had to do the following to get the orientation as the Exif orientation was always 0

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode,resultCode,data);
    if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
            Uri selectedImage = data.getData();
            String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
            Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
            int orientation = -1;
            if (cur != null && cur.moveToFirst()) {
                    orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
            }
            InputStream imageStream = getContentResolver().openInputStream(selectedImage);
            Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
            switch(orientation) {
                    case 90:
                            bitmap = rotateImage(chosen_image_bitmap, 90);
                            break;
                    case 180:
                            bitmap = rotateImage(chosen_image_bitmap, 180);
                            break;
                    case 270:
                            bitmap = rotateImage(chosen_image_bitmap, 270);
                            break;
                    default:
                            break;
            }
            imageView.setImageBitmap(bitmap );
rharvey
  • 1,987
  • 1
  • 28
  • 23
4

I solved it using a different method. All you have to do is check if the width is greater than height

Matrix rotationMatrix = new Matrix();
if(finalBitmap.getWidth() >= finalBitmap.getHeight()){
    rotationMatrix.setRotate(-90);
}else{
    rotationMatrix.setRotate(0);
}

Bitmap rotatedBitmap = Bitmap.createBitmap(finalBitmap,0,0,finalBitmap.getWidth(),finalBitmap.getHeight(),rotationMatrix,true);
masud_moni
  • 1,121
  • 16
  • 33
SaidTagnit
  • 115
  • 3
  • 14
3

The selected answer uses the most common method answered to this and similar questions. However, it does not work with both front and back cameras on Samsung. For those looking for a solution which works across both front and back cameras for Samsung and other major manufacturers, this answer by nvhausid is awesome:

https://stackoverflow.com/a/18915443/6080472

For those who don't want to click through, the relevant magic is to use the CameraInfo rather then relying on EXIF.

Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);

Full code in the link.

Community
  • 1
  • 1
D. Scott
  • 147
  • 2
  • 4
2

If somebody experiences problems with ExifInterface on Android 4.4 (KitKat) for getting the orientation, it might be because of wrong path got from the URI. See a solution for propoer getPath in Stack Overflow question Get real path from URI, Android KitKat new storage access framework

Community
  • 1
  • 1
peter.bartos
  • 11,855
  • 3
  • 51
  • 62
1

Better try to take the picture in a specific orientation.

android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden"

For best results give landscape orientation in the cameraview activity.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Siva
  • 446
  • 2
  • 9
  • 24
1

This maybe goes without saying but always remember that you can handle some of these image handling issues on your server. I used responses like the ones contained in this thread to handle the immediate display of the image. However my application requires images to be stored on the server (this is probably a common requirement if you want the image to persist as users switch phones).

The solutions contained in many of the threads concerning this topic don't discuss the lack of persistence of the EXIF data which doesn't survive the Bitmap's image compression, meaning you'll need to rotate the image each time your server loads it. Alternatively, you can send the EXIF orientation data to your server, and then rotate the image there if needed.

It was easier for me to create a permanent solution on a server because I didn't have to worry about Android's clandestine file paths.

Braden Holt
  • 1,544
  • 1
  • 18
  • 32
  • Can you rotate it once at image capture time and save it that way so it never needs to be rotated again? – jk7 Sep 06 '17 at 17:30
  • Yea you can and that's actually the process I ended up implementing in the end. I was having trouble getting the file path from the image on the Android phone that would allow me to do that. This is the answer that helped: https://stackoverflow.com/a/36714242/5443056 – Braden Holt Sep 06 '17 at 21:55
  • 1000s apps that can rotate image on a phone, rotate them on server instead. Every situation is different, but I would run away from solution that puts work on your server. You always want to outsource computing to customer phones when possible. – Vaidas Dec 02 '21 at 16:45
1

Below code worked with me, it got the bitmap from the fileUri, and do the rotation fixing if required:

    private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
        val bitmap = when {
            Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
                this.contentResolver,
                selectedPhotoUri
            )
            else -> {
                val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
                ImageDecoder.decodeBitmap(source)
            }
        }

        // If the image is rotated, fix it
        return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
            ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
            ExifInterface.ORIENTATION_ROTATE_90 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(90F) }, true)
            ExifInterface.ORIENTATION_ROTATE_180 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(180F) }, true)
            ExifInterface.ORIENTATION_ROTATE_270 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(270F) }, true)
            else -> bitmap
        } 
    }
Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203
0

Here is Xamarin.Android version:

From @Jason Robinson's answer:

Bitmap rotate(Bitmap bitmap, int angle)
{
    var matrix = new Matrix();
    matrix.PostRotate(angle);

    return Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
}

Bitmap rotateIfRequired(Bitmap bitmap, string imagePath)
{
    var ei = new ExifInterface(imagePath);
    var orientation = ei.GetAttributeInt(ExifInterface.TagOrientation, (int)Android.Media.Orientation.Undefined);

    switch (orientation)
    {
        case (int)Android.Media.Orientation.Rotate90: return rotate(bitmap, 90);
        case (int)Android.Media.Orientation.Rotate180: return rotate(bitmap, 180);
        case (int)Android.Media.Orientation.Rotate270: return rotate(bitmap, 270);
        default: return bitmap;
    }
}

Then calculateInSampleSize method:

int calculateInSampleSize(BitmapFactory.Options options, int reqW, int reqH)
{
    float h = options.OutHeight;
    float w = options.OutWidth;
    var inSampleSize = 1;

    if (h > reqH || w > reqW)
    {
        if (reqH == 0) inSampleSize = (int)Math.Floor(w / reqW);
        else if (reqW == 0) inSampleSize = (int)Math.Floor(h / reqH);
        else
        {
            var hRatio = (int)Math.Floor(h / reqH);
            var wRatio = (int)Math.Floor(w / reqW);
            inSampleSize = false ? Math.Max(hRatio, wRatio) : Math.Min(hRatio, wRatio);
        }
    }

    return inSampleSize;
}

From @Sami Eltamawy's answer:

Bitmap handleSamplingAndRotationBitmap(string imagePath)
{
    var maxHeight = 1024;
    var maxWidth = 1024;

    var options = new BitmapFactory.Options();
    options.InJustDecodeBounds = true;
    BitmapFactory.DecodeFile(imagePath, options);

    options.InSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);

    options.InJustDecodeBounds = false;

    var bitmap = BitmapFactory.DecodeFile(imagePath, options);

    bitmap = rotateIfRequired(bitmap, imagePath);

    return bitmap;
}
Mehdi Dehghani
  • 10,970
  • 6
  • 59
  • 64
  • Hi, I have the rotation issue on my xamarin. android app. The problem is when using back camera the image gets saved on 90 degree right. But using front camera it saves on 90 degree left. So the rotation solve using this code working only on front camera. Did you faced the issue? – Anand Oct 11 '20 at 09:57
  • Hello, I think that's because front camera is flipped by default. if you turns that off, the result of this code for both camera are equal? – Mehdi Dehghani Oct 11 '20 at 10:57
  • Hi, Can you help me on that part? I have asked a question here. But still in the dark. https://stackoverflow.com/questions/64278491/camera-capture-image-rotate-to-portrait-xamarin-android/64294447?noredirect=1#comment113703379_64294447 – Anand Oct 11 '20 at 12:06
0

If you are using Fresco, you can use this -

final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setRotationOptions(RotationOptions.autoRotate())
.build();

mSimpleDraweeView.setController(
Fresco.newDraweeControllerBuilder()
    .setImageRequest(imageRequest)
    .build());

This automatically rotates the images based on Exif data.

Source: https://frescolib.org/docs/rotation.html

Ritesh Chandnani
  • 425
  • 4
  • 14
0

Got an answer for this problem without using ExifInterface. We can get the rotation of the camera either front camera or back camera whichever you are using then while creating the Bitmap we can rotate the bitmap using Matrix.postRotate(degree)

public int getRotationDegree() {
    int degree = 0;

    for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(i, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            degree = info.orientation;

            return degree;
        }
    }

    return degree;
}

After calculating the rotation you can rotate you bitmap like below:

 Matrix matrix = new Matrix();

 matrix.postRotate(getRotationDegree());

 Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);

Herare bm should be your bitmap.

If you want to know the rotation of your front camera just change Camera.CameraInfo.CAMERA_FACING_BACK to Camera.CameraInfo.CAMERA_FACING_FRONT above.

I hope this helps.

  • 2
    Horrible answer but I accidentally upvoted. This code assumes every image from your gallery is made with _your_ camera. This is not the case – Zun Nov 19 '19 at 11:00
  • @Zun The question asked is for image capturing and not for picking image from gallery. – Om Prakash Agrahari Jun 02 '21 at 18:41
0

Use of Glide library worked for me. Rotation is taken cared of automatically.

Bitmap bitmap = Glide.with(myContext).asBitmap().load(imageFilePath).submit(SIZE_ORIGINAL, SIZE_ORIGINAL).get();

Then you save that bitmap to a file in JPEG format, for example.

If you just want to load into an ImageView instead of saving to a file:

Glide.with(myContext).load(imageFilePath).into(myImageView)
0

by using glide lib you can get image with exact orientation not need to check rotation

in kotlin

CoroutineScope(Dispatchers.IO).launch {
         var bitmap = Glide.with(context).asBitmap().load(imagePathOrUriOrLink)
                /*.apply(
                    RequestOptions()
                        .override(MAXIMUM_IMAGE_RESOLUTION)
                )*/ //uncomment it if you want original image
                /*.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)*/ //uncomment it you want to not cache image
                .submit().get()//this is synchronous approach  
}

using this dependency

api 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'
Abdur Rehman
  • 1,247
  • 10
  • 13