469

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.

9
  • 2
    try this in you activity menifest android:configChanges="orientation" android:screenOrientation="portrait" Dec 28, 2012 at 7:32
  • 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. Dec 28, 2012 at 8: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, 2012 at 17:45
  • 1
    To avoid Storage permission, see this or this answer or use Glide
    – Pavel
    Sep 23, 2018 at 0: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? Aug 24, 2021 at 10:31

23 Answers 23

542

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);
}
20
  • 1
    From @JasonRobinson code's I learn how to get the actual orientation and by combining with these code I'm successfully manage the orientation. Sep 21, 2014 at 4:31
  • 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 3, 2017 at 13:07
  • 13
    It is always return me 0 value. Please tell how to get actual orientation. Nov 13, 2018 at 6:33
  • 12
    Getting 0 always, any idea why? May 23, 2019 at 14:16
  • 4
    Note: use androidx.exifinterface.media.ExifInterface instead of android.media.ExifInterface
    – Andrew
    Oct 9, 2020 at 8:31
224

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;
}
9
  • 1
    the method rotateImageIfRequired() works very well.. thanks !!
    – mapo
    Oct 3, 2018 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, 2018 at 9:59
  • @Makalele Is this issue also occur while taking photos and attaching through WhatsApp? Feb 13, 2019 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, 2019 at 14:40
  • 3
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;
}
8
  • 1
    what is calculateInSampleSize method here Jul 14, 2014 at 6:54
  • 1
    @madhukotagiri here you have an example of implementantion for calculateInSampleSize: gist.github.com/anonymous/b7ea25fc2bbc54e43616
    – Felix
    Jul 14, 2014 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, 2014 at 22:46
  • 4
    Uri selectedImage parameter not used in getRotation(...) method. How do we need use it? Thank you. Feb 6, 2015 at 15:22
  • 1
    Parameter 'selectedImage' doesn't seem to be used anywhere. Any reason to be there?
    – Alex
    Oct 11, 2015 at 22:03
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.

6
  • 15
    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. Jul 12, 2016 at 11:54
  • 7
    Its displaying image clicked as it is. It is not rotating as required.
    – seema
    Feb 24, 2017 at 8: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 9, 2017 at 21:30
  • 2
    not working for me.. this issue exists in Picasso as well
    – NehaK
    Jun 24, 2020 at 13:34
  • 1
    Nope it doesn't work! Oneplus Android 11 and Samsung Tabs
    – sud007
    Jul 15, 2022 at 5: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.

1
  • 4
    implementation 'androidx.exifinterface:exifinterface:X.X.X' This is for those who are using androidx. thank you for your posting
    – Doongsil
    Aug 22, 2019 at 2: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;
    }
}
0
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)
{
}
2
  • this should be marked as the answer. Rotation is resulted from the camera orientation so this is awesome! Jul 27, 2020 at 10:50
  • 1
    How do you know which camera took the picture and whether the phone is still in the same orientation as when the user took the picture? Aug 22, 2020 at 15:18
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

6

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);
1
  • 2
    In my case I need : rotationMatrix.setRotate(90); Nov 11, 2021 at 11:37
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
    )
}
3
  • 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 7, 2020 at 13:37
  • In my case it works fine on Android 10.
    – Xam
    Mar 15, 2022 at 7: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, 2022 at 5: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);
}
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;
    }
}
3
  • 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. Jan 3, 2013 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: stackoverflow.com/questions/14123809/… Jan 3, 2013 at 11:36
  • 1
    MediaStore.Images.ImageColumns.ORIENTATION available only in Android 10 and upwards. Nov 20, 2020 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 );
1
  • 2
    alwasys 0, samsung 7
    – djdance
    Dec 1, 2016 at 20:46
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.

1
  • no, wrong rotation in different angles (smasung s7). I mean gallery of course
    – djdance
    Dec 1, 2016 at 20:39
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

1
  • This one comment is what I needed. Dude, thank you so much. May 4, 2017 at 22:19
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.

2
  • sorry, it doesn't work. In-fact on tab, every time after finishing execution of onActivityResult, strangely onCreate gets called. Dec 28, 2012 at 12:00
  • 1
    sorry, the problem is as it is Jan 2, 2013 at 13:40
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.

3
  • Can you rotate it once at image capture time and save it that way so it never needs to be rotated again?
    – jk7
    Sep 6, 2017 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: stackoverflow.com/a/36714242/5443056 Sep 6, 2017 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 2, 2021 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
        } 
    }
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;
}
3
  • 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, 2020 at 9: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? Oct 11, 2020 at 10:57
  • Hi, Can you help me on that part? I have asked a question here. But still in the dark. stackoverflow.com/questions/64278491/…
    – Anand
    Oct 11, 2020 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

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
  • 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, 2019 at 11:00
  • @Zun The question asked is for image capturing and not for picking image from gallery. Jun 2, 2021 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'

Not the answer you're looking for? Browse other questions tagged or ask your own question.