Android EXIF данные всегда 0, как его изменить?
У меня есть приложение, которое захватывает фотографии с использованием родного Camera
, а затем загружает их на сервер. Моя проблема в том, что все фотографии имеют значение ориентации EXIF 0, и это помешает отображению в другом месте.
Как изменить ориентацию EXIF? Я не ищу способ исправить это для каждого обстоятельства, просто измените его на другое значение.
Я использую Samsung Galaxy Note 4
Я попробовал это решение, которое устанавливает ориентацию камеры перед фотографированием: Настройка ориентации фотофайла Android Photo
Camera c = Camera.open();
c.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
params.setRotation(0); // tried 0, 90, 180
c.setParameters(params);
но он не влияет на итоговые данные EXIF, его все еще всегда 0
Я также пробовал эти решения, когда изображение поворачивается после его принятия: Значение тега ориентации EXIF всегда 0 для изображения, сделанного с помощью приложения для портфолио с вертикальной камерой
и пока это поворачивает фотографию, ориентация EXIF по-прежнему всегда равна 0.
Я также попытался напрямую установить данные EXIF: Как сохранить данные Exif после сжатия растрового изображения в Android
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
final File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, "");
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
ExifInterface exif = new ExifInterface(pictureFile.toString());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
exif.saveAttributes();
fos.write(data);
fos.close();
//upload photo..
}
}
}
но EXIF Orientation по-прежнему 0 после загрузки.
Я также рассмотрел эти решения:
Данные Exif TAG_ORIENTATION всегда 0
Как записать exif-данные в изображение на Android?
Как получить правильную ориентацию изображения, выбранного из галереи изображений по умолчанию
как установить ориентацию изображения камеры?
но все они связаны с корректировкой ориентации путем вращения, что не влияет на данные EXIF, или напрямую на данные EXIF, которые, похоже, не работают.
Как я могу изменить данные ориентации EXIF файла от 0 до 3?
UPDATE:
вот мой код загрузки:
Bitmap sBitmap = null;
final File sResizedFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, "_2");
try {
sBitmap = BitmapFactory.decodeStream(new FileInputStream(pictureFile), null, options);
} catch (FileNotFoundException e) {
Log.e("App", "[MainActivity] unable to convert pictureFile to bitmap");
e.printStackTrace();
return;
}
// ... compute sw and sh int values
Bitmap sOut = Bitmap.createScaledBitmap(sBitmap, sw, sh, false);
Bitmap rotatedBitmap = rotateBitmap(sOut, 3);
FileOutputStream sfOut;
try {
sfOut = new FileOutputStream(sResizedFile);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, sfOut);
sfOut.flush();
sfOut.close();
sBitmap.recycle();
sOut.recycle();
rotatedBitmap.recycle();
} catch (Exception e) {
Log.e("App", "[MainActivity] unable to save thumbnail");
e.printStackTrace();
return;
}
// upload small thumbnail
TransferObserver sObserver = transferUtility.upload(
"stills/small", /* The bucket to upload to */
filename + ".jpg", /* The key for the uploaded object */
sResizedFile /* The file where the data to upload exists */
);
Ответы
Ответ 1
Оказывается, мой код смог установить данные EXIF, но существует расхождение между тем, как Android интерпретирует эти данные и как интерпретирует его iOS и Chrome на Mac (где я проверял результирующий файл).
Это единственный код, необходимый для установки ориентации EXIF:
ExifInterface exif = new ExifInterface(pictureFile.toString());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
exif.saveAttributes();
Однако установка 3
отображается как 0
на iOS, а изображение находится в стороне в Chrome.
6
отображается как 3
на iOS, и изображение выглядит правильно в Chrome.
Ответ 2
Как вы можете видеть, информация EXIF не надежна на Android (особенно устройства Samsung).
Однако телефонная база данных SQL, содержащая ссылки на объект Media, надежна. Я бы предложил пойти таким путем.
Получение ориентации из 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;
}
Затем инициализируйте повернутый 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;
}
Если вы хотите изменить ориентацию изображения, попробуйте следующий фрагмент:
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;
}
Если вы установите ориентацию изображения, в дальнейшем он будет постоянно установлен в правильной ориентации. Необходимо использовать ExifInterface
позже, потому что изображение уже повернуто соответствующим образом.
Если этот метод не является удовлетворительным, вы можете попробовать этот метод
Ответ 3
Вы приняли свой собственный ответ в качестве решения. Мой допрос - это просто полезная боковая информация, так или иначе...
"Однако..." в вашем ответе подсказывает, что теперь вы знаете причину, у вас нет исправления.
Оказывается, мой код смог установить данные EXIF, но есть несоответствие между тем, как Android интерпретирует эти данные и как iOS... интерпретирует его.
Это может быть проблема с эндитантностью. Вы можете вручную изменить настройку endianness в Exif, открыв jpeg в шестнадцатеричном редакторе и обнаружив...
- Байт
45 78 69 66
(делает текст "Exif" ), за которым следуют два нуля байта 00 00
.
- Тогда это должно быть
49 49
(делает текст "II" ), что означает чтение данных как минимальный формат.
- Если вы замените его текстом
4D 4D
(или "ММ" ), то сторона чтения
будет рассматривать данные как большие endian.
Проверьте это в iOS, чтобы узнать, правильны ли числа.
![4Km4H.png]()
и относительно этого...
Однако установка 3
отображается как 0
на iOS, и изображение находится вбок в Хром.
Настройка 6
отображается как 3
на iOS, и изображение выглядит правильно в Chrome.
Единственное, что я могу добавить, это то, что iOS Mac Big Endian *, а Android/ПК - Маленький Endian. По сути, Little Endian читает/записывает байты как справа налево, в то время как Big Endian противоположна.
В двоичном формате: 011
означает 3 и 110
означает 6. Разница между 3 и 6 - это просто порядок чтения этих бит единиц и нулей. Таким образом, система, которая читает как zero-one-one
, получает результат 3, но другая система Endian будет читать байт с теми же битами, что и one-one-zero
, и сообщит, что результат - 6. Я не могу объяснить, почему "3 отображается как 0" без тестового файла для анализа байтов, но для меня это странный результат.
</end rant>
<sleep>
* Примечание: в то время как Mac - Big Endian, двойная проверка говорит, что iOS использует систему Little Endian в конце концов. Ваши номера по-прежнему предполагают проблему с большим или маленьким эндиантом.
Ответ 4
Обратитесь к проекту GitHub https://github.com/pandiaraj44/Camera. Он имеет пользовательскую активность камеры, где EXIF TAG_ORIENTATION обрабатывается правильно. Вы можете клонировать проект и проверять его. Подробные сведения о кодах см. https://github.com/pandiaraj44/Camera/blob/master/app/src/main/java/com/pansapp/cameraview/CameraFragment.java
Ответ 5
Существует один класс для чтения и обновления данных изображений.
Чтобы обновить атрибуты, которые можно использовать следующим образом
ExifInterface ef = new ExifInterface(filePath);
ef.setAttribute(MAKE_TAG, MAKE_TAG);
ef.setAttribute(ExifInterface.TAG_ORIENTATION, orientation+"");
ef.saveAttributes();
и для чтения вы можете использовать это как
ExifInterface exif = null;
try {
exif = new ExifInterface(absolutePath+path);
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
Я надеюсь, что это поможет вам