2

I have created a custom camera. When I click on the capture button in the application, image has been taken. Moreover, I am getting the data in the form of byte array in the function named as onPictureTaken.

I am converting the byte array into the bitmap using the library known as Glide.

My problem is that in Samsung device the images rotates itself. I have been researching on it for quite a while. I found the library called as metadata extraction library to get the Exif information from byte[] and rotate the image on it but it is not working on the Samsung devices. The metadata extraction library every time returns a value of 1 for portrait image which shows that image does not need rotation however, the image taken in portrait mode is always 90 degree rotated.

Whenever, the photo is taken in portrait mode it is rotated at an angle of 90 degrees for both front and back camera and meta extraction library shows a value of 1.

Is there something other then metadata extraction extraction library which extract Exif information stream data?

Note: I cannot use ExifInterface because it requires the minimum Api level of 24 whereas, I am testing on API level 22

I have tried many solution but nothing is working. Is there any solution for this?

The code is given below:

public void onPictureTaken(byte[] data, Camera camera) {
            mCamera.stopPreview();

            Glide.with(this).load(data)
    .asBitmap().centerCrop().animate(R.anim.abc_fade_in)
    .into(new SimpleTarget<Bitmap>(width, height) {
                            @Override
                            public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                                camera_view.setVisibility(View.INVISIBLE);
                                int w = resource.getWidth();
                                int h = resource.getHeight();

                                // Setting post rotate to 90

                                Matrix mtx = new Matrix();


                                try {

                                    InputStream is = new ByteArrayInputStream(data);
                                    Metadata metadata = ImageMetadataReader.readMetadata(is);
                                    final ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
                                    if (exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
                                        final int exifOrientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
                                        switch (exifOrientation) {

                                            case 6:
                                                mtx.postRotate(90);
                                                break;  // top left
                                            case 3:
                                                mtx.postRotate(180);;
                                                break;  // top right
                                            case 8:
                                                mtx.postRotate(270);
                                                break;  // bottom right

                                        }
                                        photo = Bitmap.createBitmap(resource, 0, 0, w, h, mtx, true);
                                            /* Work on exifOrientation */
                                    } 


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

        }

I am using Samsung J5 for the testing.

Zoe
  • 27,060
  • 21
  • 118
  • 148
mimetype
  • 31
  • 3
  • From what i've seen the actual problem is that the `ExifIFD0Directory` doesn't actually exist on the metadata coming from a Samsung device. However, I'm currently using a cordova plugin that has been able to fix the orientation for images coming from a Samsung device. Here is a link to the source: https://github.com/apache/cordova-plugin-camera/blob/master/src/android/CameraLauncher.java#L1000 – Victor Ramos Aug 20 '18 at 18:56

3 Answers3

1

You don't need a library for this. Here is a couple methods that I wrote that should do the trick for you.

 public static int getCapturedImageOrientation(Context context, Uri imageUri){
    int rotate = 0;
    try {
        context.getContentResolver().notifyChange(imageUri, null);
        File imageFile = new File(imageUri.getPath());

        ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_270:
                rotate = 270;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                rotate = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_90:
                rotate = 90;
                break;
        }

        Log.i("RotateImage", "Exif orientation: " + orientation);
        Log.i("RotateImage", "Rotate value: " + rotate);
    } catch (Exception e) {
        Log.e(TAG, "Error getting rotation of image");

    }

    return rotate;
}
public static int GetRotateAngle(Context context, Uri imageUri) {
    String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION };
    Cursor cursor = context.getContentResolver().query(imageUri, columns, null, null, null);
    if (cursor == null) {
        //If null, it is not in the gallery, so may be temporary image
        return getCapturedImageOrientation(context, imageUri);

    }

    cursor.moveToFirst();

    int orientationColumnIndex = cursor.getColumnIndex(columns[1]);
    int orientation = cursor.getInt(orientationColumnIndex);
    cursor.close();
    return orientation;
}

I wrap these in a class called ImageHelper. You can use it like this:

 rotateImage(ImageHelper.GetRotateAngle(Context, mCropImageUri));

Then of course the rotateImage code would be:

 private void rotateImage(int degrees) {
        Matrix mat = new Matrix();
        mat.postRotate(degrees);
        mCropImage = Bitmap.createBitmap(mCropImage, 0, 0, mCropImage.getWidth(), mCropImage.getHeight(), mat, true);
        setImageForCropping(mCropImage);

    }

Of course i was doing all this for a photo editing, cropping and scaling app, so you can ignore some of the extras, but this should take care of ya. Goodluck.

Sam
  • 5,342
  • 1
  • 23
  • 39
  • Thanks. After having the first look I have a question that form where I will get the image URI? I am not storing it to any media file – mimetype Dec 12 '17 at 18:59
  • that's ok, just skip that piece of the code then. notice the code says if "cursor==null" then call getCapturedImageOrientation, this is for when you did not store it in the media store on the device. And you already found another way to get the ExifInterface that didn't involve using the file absolute path, so you have everything you need, just try fixing your code to match the getOrientation exif handling that I supplied. I don't know what your magic numbers are of 3,6, and 8. I can't clearly spot your issue, but the above code i provided works on every device tested on hundreds of devices – Sam Dec 12 '17 at 19:12
  • Okay Thank you I will test it – mimetype Dec 12 '17 at 19:40
  • Okay Thank you I will test it but your function seems to be same as of mine and my function is not working. The magic numbers 3,6 and 8 shows the same things you have in your switch statements – mimetype Dec 12 '17 at 19:46
  • oh I just saw your note, you can NOT use Exifinterface? That was added in level 5, so why would you not be able to use this? https://developer.android.com/reference/android/media/ExifInterface.html – Sam Dec 12 '17 at 22:03
  • Also your code is slightly different in the way that you get the attribute for example. You don't use getAttributeint, so just saying try my way and see if it works for you. – Sam Dec 12 '17 at 22:04
  • I cannot use ExifInterface because I have to get EXIF data from Inputstream not from File. To get EXIF data from Inputstream the requirement is API level 24. I am adding the link you can read it from there. https://stackoverflow.com/questions/39540646/how-to-get-exif-data-from-an-inputstream-rather-than-a-file – mimetype Dec 12 '17 at 22:33
  • In your case you are getting Exif data from file which is introduced in API level 5. – mimetype Dec 12 '17 at 22:34
  • so have you benchmarked using Exif from input stream vs creating file from input stream and using Exif from File? I can't imagine it is saving you that much processing power to force you to go to the new APIs for Exif vs the old ones – Sam Dec 12 '17 at 23:47
  • Use the support library for ExifInterface because the android.media one could have some security issues, lint also warns about that. Also, this answer should be marked as correct. Helped me! Cheers @Sam! – Rahul Sainani Jan 25 '18 at 20:36
1

Almost in all Samsung Devices the image rotation Issue is common ,in my case i am using Samsung Note 3 and this same issue occurs but i am using below code to solve this issue

public static Bitmap decodeFile(String path) { // this method is for avoiding the image rotation
        int orientation;
        try {
            if (path == null) {
                return null;
            }
            // decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            // Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE = 70;
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 4;
            while (true) {
                if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale++;
            }
            // decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            Bitmap bm = BitmapFactory.decodeFile(path, o2);
            Bitmap bitmap = bm;
            ExifInterface exif = new ExifInterface(path);
            orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
            Log.e("orientation", "" + orientation);
            Matrix m = new Matrix();
            if ((orientation == 3)) {
                m.postRotate(180);
                m.postScale((float) bm.getWidth(), (float) bm.getHeight());
//               if(m.preRotate(90)){
                Log.e("in orientation", "" + orientation);
                bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
                return bitmap;
            } else if (orientation == 6) {
                m.postRotate(90);
                Log.e("in orientation", "" + orientation);
                bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
                return bitmap;
            } else if (orientation == 8) {
                m.postRotate(270);
                Log.e("in orientation", "" + orientation);
                bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);
                return bitmap;
            }
            return bitmap;
        } catch (Exception e) {
        }
        return null;
    }

This code is work for me so i hope this will helpful for you

1

This can be easily fixed by using ExifInterface provided by Google.
You can add it to your project as follows:

implementation "androidx.exifinterface:exifinterface:1.1.0"

After this, get the rotation from your image and apply it to your ImageView:

        // uri of the image
        val inputStream = contentResolver.openInputStream(Uri.parse(uri))
        val exifInterface = ExifInterface(requireNotNull(inputStream))

        var rotation = 0

        when (exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) {
            ExifInterface.ORIENTATION_ROTATE_90 -> rotation = 90
            ExifInterface.ORIENTATION_ROTATE_180 -> rotation = 180
            ExifInterface.ORIENTATION_ROTATE_270 -> rotation = 270
        }
harshithdwivedi
  • 1,411
  • 16
  • 37
  • This approach worked for me for Samsung devices; simplest and easiest. [Note that Google has since updated the version of the exifinterface dependency since this answer was posted.] – shagberg Apr 23 '21 at 22:08