I have an app that captures photos using the native Camera
and then uploads them to a server. My problem is that all the photos have an EXIF orientation value of 0,
You have accepted your own answer as solution. My rant is just useful side-info, anyways...
The "However..." in your Answer suggests while you now know the cause, you don't have a fix.
Turns out my code was able to set the EXIF data, but there is a discrepancy between how Android interprets this data and how iOS... interprets it.
This could be an endianness issue. You can try manually changing the endianness setting of Exif by opening your jpeg in a hex editor and finding...
45 78 69 66
(makes "Exif" text) followed by two zero bytes 00 00
. 49 49
(makes "II" text) which means read data as little endian format.4D 4D
(or "MM" text) then the reading side
will consider data as big endian.Test this in iOS to see if numbers are now correct.
and regarding this...
However, setting
3
shows up as0
on iOS and the image is sideways in Chrome.
Setting6
shows up as3
on iOS and the image looks right in Chrome.
Only thing I can add is that iOS Mac is Big Endian* and Android/PC is Little Endian. Essentially Little Endian reads/writes bytes as right-to-left whilst Big Endian is opposite.
In binary : 011
means 3 and 110
means 6. The difference between 3 and 6 is simply the reading order of those bits of ones & zeroes. So a system that reads as zero-one-one
gets a result of 3 but the other Endian system will read a byte with same bits as one-one-zero
and tell you result is a 6. I can't explain why "3 shows up as 0" without a test file to analyse bytes but it's a strange result to me.
</end rant>
<sleep>
*note: While Macs are Big Endian, double-checking says iOS uses Little Endian system after all. Your numbers still suggest a Big vs Little Endian issue though.
Refer this GitHub project https://github.com/pandiaraj44/Camera. It has the custom camera activity where EXIF TAG_ORIENTATION was handled correctly. You can clone the project and check. For code details please refer https://github.com/pandiaraj44/Camera/blob/master/app/src/main/java/com/pansapp/cameraview/CameraFragment.java
As you can see, the The EXIF information is not reliable on Android (especially Samsung devices).
However the phone SQL database holding the references to Media object is reliable. I would propose going this way.
Getting the orientation from the Uri:
private static int getOrientation(Context context, Uri photoUri) {
Cursor cursor = context.getContentResolver().query(photoUri,
new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null);
if (cursor.getCount() != 1) {
cursor.close();
return -1;
}
cursor.moveToFirst();
int orientation = cursor.getInt(0);
cursor.close();
cursor = null;
return orientation;
}
Then initialize rotated Bitmap
:
public static Bitmap rotateBitmap(Context context, Uri photoUri, Bitmap bitmap) {
int orientation = getOrientation(context, photoUri);
if (orientation <= 0) {
return bitmap;
}
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
return bitmap;
}
If you want to change the orientation of the image, try the following snippet:
public static boolean setOrientation(Context context, Uri fileUri, int orientation) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.ORIENTATION, orientation);
int rowsUpdated = context.getContentResolver().update(fileUri, values, null, null);
return rowsUpdated > 0;
}
If you set the orientation of the image, later it will be constantly set at the correct orientation. There is need to make use of ExifInterface
later, because the image is already rotated in proper way.
If this method is not satisfactory, then you could try this method
Turns out my code was able to set the EXIF data, but there is a discrepancy between how Android interprets this data and how iOS and Chrome on a Mac (where I was checking the resulting file) interprets it.
This is the only code needed to set EXIF orientation:
ExifInterface exif = new ExifInterface(pictureFile.toString());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
exif.saveAttributes();
However, setting 3
shows up as 0
on iOS and the image is sideways in Chrome.
setting 6
shows up as 3
on iOS and the image looks right in Chrome.
There is one class to read and update these information of images.
To update the attributes you can use like this
ExifInterface ef = new ExifInterface(filePath);
ef.setAttribute(MAKE_TAG, MAKE_TAG);
ef.setAttribute(ExifInterface.TAG_ORIENTATION, orientation+"");
ef.saveAttributes();
and for reading you can use like this
ExifInterface exif = null;
try {
exif = new ExifInterface(absolutePath+path);
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
I hope it will help you