Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Liam Hales
Liam Hales
356 Points

How to rotate images to the correct orientation (portrait) by editing the EXIF data once photo has been taken

Hi there, I have currently finished the Ribbit app course for android and when i view the image taken with the camera intent either on Parse or on my device using the Ribbit app, it always displays landscape even though I took the photo portrait.

I have heard you can edit the EXIF data once the photo has been taken so every photo you take on any device will be uploaded to Parse with the correct orientation.

Can someone please help me out?

I'm using the FileHelper.java from the Ribbit app and here is onActivityResult code from the camera intent.

As you can see i have attempted my problem already but had no luck :(

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

        if (resultCode == RESULT_OK)
        {
            if(requestCode == CHOOSE_A_PHOTO_REQUEST || requestCode == CHOOSE_A_VIDEO_REQUEST)
            {
                if (data == null)
                {
                    Toast.makeText(this, (R.string.simple_error), Toast.LENGTH_LONG).show();
                }
                else
                {
                    mMediaUri = data.getData();
                }
            }
            else
            {

                //Adding to the gallery only when we take a photo or video
                Intent mediaScanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                mediaScanFileIntent.setData(mMediaUri);
                sendBroadcast(mediaScanFileIntent);

                mMediaString = mMediaUri.toString();
                BitmapFactory.Options bounds = new BitmapFactory.Options();
                bounds.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(mMediaString, bounds);

                BitmapFactory.Options opts = new BitmapFactory.Options();
                Bitmap bm = BitmapFactory.decodeFile(mMediaString, opts);
                ExifInterface exif = null;
                try
                {
                    exif = new ExifInterface(mMediaString);
                }
                catch (IOException e)
                {
                    //Error
                    e.printStackTrace();
                }
                String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
                int orientation = orientString != null ? Integer.parseInt(orientString) :  ExifInterface.ORIENTATION_NORMAL;

                int rotationAngle = 0;
                if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
                if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
                if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;

                mMediaUri = Uri.parse(mMediaString);
            }

            Intent setPhraseIntent = new Intent(this, SetPhraseActivity.class);
            setPhraseIntent.setData(mMediaUri);

            String fileType;
            if(requestCode == CHOOSE_A_PHOTO_REQUEST || requestCode == TAKE_A_PHOTO_REQUEST)
            {
                fileType = ParseConstants.TYPE_PHOTO;
            }
            else
            {
                fileType = ParseConstants.TYPE_VIDEO;
            }

            setPhraseIntent.putExtra(ParseConstants.KEY_FILE_TYPE, fileType);
            startActivity(setPhraseIntent);
        }

        else if (resultCode != RESULT_CANCELED)
        {
            Toast.makeText(this, R.string.simple_error, Toast.LENGTH_LONG).show();
        }
    }

Thank you for helping me out!

3 Answers

Vincent Rickey
Vincent Rickey
5,581 Points

Hey Liam, by any chance did you come up with a solution to this problem? My log also returns values of all 0's, which I believe is the result of mMediaUri not returning the correct path. Any help would be appreciated. I don't believe that anyone has resolved the sideways loading of portrait images issue. Thanks!

Juan P. Prado
Juan P. Prado
69,294 Points

Hi Liam, I hope my solution would still help people, I found out that this behaviour is mostly found on samsung devices You can call the method below just after the image resizing

public static byte[] rotateImageIfRequired(Context context, Uri uri, byte[] fileBytes) {
    byte[] data = null;
    Bitmap bitmap = BitmapFactory.decodeByteArray(fileBytes, 0, fileBytes.length);
    ByteArrayOutputStream outputStream = null;

    try {

      bitmap = ImageResizer.rotateImageIfRequired(bitmap, context, uri);
      outputStream = new ByteArrayOutputStream();
      bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
      data = outputStream.toByteArray();
    } catch (IOException e) {
      Timber.e(e.getMessage());
      e.printStackTrace();
    } finally {
      try {
        if (outputStream != null) {
          outputStream.close();
        }
      } catch (IOException e) {
        // Intentionally blank
      }
    }

    return data;
  }

Now the inner missing methods, here we depend on the origin of the uri, if it comes from the gallery or the camera that's what the if statement is about, and then we rotate apropietly:

public static Bitmap rotateImageIfRequired(Bitmap img, Context context, Uri selectedImage) throws IOException {

    if (selectedImage.getScheme().equals("content")) {
      String[] projection = { MediaStore.Images.ImageColumns.ORIENTATION };
      Cursor c = context.getContentResolver().query(selectedImage, projection, null, null, null);
      if (c.moveToFirst()) {
        final int rotation = c.getInt(0);
        c.close();
        return rotateImage(img, rotation);
      }
      return img;
    } else {
      ExifInterface ei = new ExifInterface(selectedImage.getPath());
      int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
      Timber.d("orientation: %s", orientation);

      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;
      }
    }
  }

  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);
    return rotatedImg;
  }

Thanks for coming up with this great question! :)

Ben Jakuben
STAFF
Ben Jakuben
Treehouse Teacher

First things first - if you log or debug, can you tell if you are able to detect the orientation properly? Is the correct value being used for the file, or is everything set to Portrait?

Once you have that data, you'll need to update the file itself. Here's one example answer http://stackoverflow.com/a/6909985/475217 that uses the Matrix class to perform a rotation.

Liam Hales
Liam Hales
356 Points

I'm not sure how to view the rotation data, all i see in the debugger under the variables (mMediaUri) is a load of data and numbers.

I have added abit of matrix code so the code form my above post now looks like this...

mMediaString = mMediaUri.toString();
                BitmapFactory.Options bounds = new BitmapFactory.Options();
                bounds.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(mMediaString, bounds);

                BitmapFactory.Options opts = new BitmapFactory.Options();
                Bitmap bm = BitmapFactory.decodeFile(mMediaString, opts);
                ExifInterface exif = null;
                try
                {
                    exif = new ExifInterface(mMediaString);
                }
                catch (IOException e)
                {
                    //Error
                    e.printStackTrace();
                }
                String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
                int orientation = orientString != null ? Integer.parseInt(orientString) :  ExifInterface.ORIENTATION_NORMAL;

                int rotationAngle = 0;
                if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
                if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
                if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;

                Matrix matrix = new Matrix();
                matrix.setRotate(rotationAngle, (float) bm.getWidth() / 2, (float) bm.getHeight() / 2);
                Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 0, 0, bounds.outWidth, bounds.outHeight, matrix, true);

Straight after this code i set the data (mMediaUri) to the recipients intent, is there a way to get the rotatedBitmap from the above code and set it to the mMediaUri variable or is this not possible?

If not then I'm not sure where to go from here :(

Many thanks for your help!

Ben Jakuben
Ben Jakuben
Treehouse Teacher

For the orientation, how about adding a few Log statements like this:

String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
Log.d("TEST", "orientString: " + orientString);

int orientation = orientString != null ? Integer.parseInt(orientString) : ExifInterface.ORIENTATION_NORMAL;
Log.d("TEST", "orientation: " + orientation);

int rotationAngle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;
Log.d("TEST", "rotationAngle: " + rotationAngle);

If you confirm that this part is working, then it's probably easier to make the rotatedBitmap when you retrieve the image from Parse and then just displaying the rotated image in the ImageView. Saving the new version is a little more complex, and you'd need to try something like one of the answers here: http://stackoverflow.com/questions/24444133/how-to-save-image-bitmap-after-rotation

Liam Hales
Liam Hales
356 Points

Hi Ben,

Thanks for showing me how to Log, i didn't know you could do that!

The values for the orientString, orientation and the rotationAngle all show up as 0 which is odd, is this the actual data from the image i took?

in your previous post you gave me a link to show me how to save the new rotated image, i don't need to save my image to the galley as i removed that feature from my code, is the code in the answers from the link you gave me still necessary?

if i was to make the rotatedBitmap when I retrieve the image from Parse would I have to move all the code i posed in my last post to the InboxFragment just before i start the ViewImageActivity?

Thanks for the help again! :)