Изменение размера изображения Android и сохранение данных EXIF (ориентация, поворот и т.д.)

Если ваше приложение Android использует камеру устройства, чтобы сделать снимок, а затем изменить его размер (это очень, очень часто используется для уменьшения размера загрузки), вы можете не осознавать, что эта операция изменения размера удаляет метаданные Exif.

Это может вызвать проблемы, особенно если рассматриваемое устройство использует тег "Ориентация" для правильного отображения изображения в вертикальном положении.

Разные устройства Android обрабатывают поворот камеры/изображения по-разному - мой надежный старый Nexus One, кажется, всегда поворачивает изображение сразу после съемки, поэтому исходное содержимое файла всегда "в вертикальном положении" при просмотре.

Однако другие устройства (особенно телефоны Samsung в моем тестировании) не поворачивают содержимое файла изображения - скорее, они устанавливают тег Exif 'Orientation'. Всякий раз, когда изображение отображается позже, соответствующий код изображения должен обнаружить наличие тега ориентации и соответствующим образом повернуть изображение. Но если вы выполнили какую-либо растровую обработку изображения и сохранили его в новом файле, все эти данные Exif будут потеряны.

В дополнение к данным ориентации вы также можете потерять другие ценные метаданные, такие как марка/модель и т.д.

Это смущало меня в течение нескольких недель (изображение отображается в вертикальном положении при отображении в телефонной галерее, но затем поступает на мой сервер с плохой ориентацией и без видимых метаданных). Я добавляю этот вопрос себе, чтобы помочь другим. Этот пост был очень полезным:

Android изменяет размер изображения без потери информации EXIF

Ответ 1

Как указали другие, вы должны скопировать данные Exif из исходного изображения в окончательное изображение с измененным размером. Для этого обычно лучше всего подходит библиотека Sanselan Android. В зависимости от версии ОС Android, ExifInterface иногда портит Exifdata.

Кроме того, ExifInterface также обрабатывает ограниченное количество тегов Exif, а именно только те теги, о которых он "знает". Sanselan, с другой стороны, сохранит все Exiftags и заметки на маркерах.

Вот запись в блоге, которая показывает, как использовать Sanselan для копирования данных изображения:

Копирование метаданных Exif с использованием Sanselan

Кстати, на Android я также склонен поворачивать изображения и удалять Exiftag Orientation. Например, на Nexus S с Android 4.03 камера устанавливала тег ориентации в Exifmetadata, но веб-просмотр игнорировал эту информацию и неправильно отображал изображение. К сожалению, вращение фактических данных изображения и удаление тега Exiforientation - единственный способ заставить каждую программу правильно отображать изображения.

Ответ 2

Насколько я могу судить, не существует механизма для автоматического сохранения метаданных или даже моментального снимка, что бы там ни было, и массовой передачи.

Скорее, кажется, что вы должны явно проверить конкретные свойства и скопировать их самостоятельно в новый файл изображения, используя ExifInterface.

http://developer.android.com/reference/android/media/ExifInterface.html

Так что-то вроде:

ExifInterface oldExif = new ExifInterface(oldImagePath);
String exifOrientation = oldExif.getAttribute(ExifInterface.TAG_ORIENTATION);

if (exifOrientation != null) {
   ExifInterface newExif = new ExifInterface(imagePath);
   newExif.setAttribute(ExifInterface.TAG_ORIENTATION, exifOrientation);
   newExif.saveAttributes();
}

Ответ 3

Для ленивых здесь используется многоразовая функция:

public static void copyExif(String oldPath, String newPath) throws IOException
{
    ExifInterface oldExif = new ExifInterface(oldPath);

    String[] attributes = new String[]
    {
            ExifInterface.TAG_APERTURE,
            ExifInterface.TAG_DATETIME,
            ExifInterface.TAG_DATETIME_DIGITIZED,
            ExifInterface.TAG_EXPOSURE_TIME,
            ExifInterface.TAG_FLASH,
            ExifInterface.TAG_FOCAL_LENGTH,
            ExifInterface.TAG_GPS_ALTITUDE,
            ExifInterface.TAG_GPS_ALTITUDE_REF,
            ExifInterface.TAG_GPS_DATESTAMP,
            ExifInterface.TAG_GPS_LATITUDE,
            ExifInterface.TAG_GPS_LATITUDE_REF,
            ExifInterface.TAG_GPS_LONGITUDE,
            ExifInterface.TAG_GPS_LONGITUDE_REF,
            ExifInterface.TAG_GPS_PROCESSING_METHOD,
            ExifInterface.TAG_GPS_TIMESTAMP,
            ExifInterface.TAG_IMAGE_LENGTH,
            ExifInterface.TAG_IMAGE_WIDTH,
            ExifInterface.TAG_ISO,
            ExifInterface.TAG_MAKE,
            ExifInterface.TAG_MODEL,
            ExifInterface.TAG_ORIENTATION,
            ExifInterface.TAG_SUBSEC_TIME,
            ExifInterface.TAG_SUBSEC_TIME_DIG,
            ExifInterface.TAG_SUBSEC_TIME_ORIG,
            ExifInterface.TAG_WHITE_BALANCE
    };

    ExifInterface newExif = new ExifInterface(newPath);
    for (int i = 0; i < attributes.length; i++)
    {
        String value = oldExif.getAttribute(attributes[i]);
        if (value != null)
            newExif.setAttribute(attributes[i], value);
    }
    newExif.saveAttributes();
}

Ответ 4

Здесь xamarin android версия ответа @prom85 (С#)

public static void CopyExif(String oldPath, String newPath) {
    ExifInterface oldExif = new ExifInterface(oldPath);
    String[] attributes;#region Exif Tags based on android version
    if (Build.VERSION.SdkInt <= BuildVersionCodes.M) {
        attributes = new String[] {

            ExifInterface.TagGpsLongitudeRef,
            ExifInterface.TagGpsLongitude,
            ExifInterface.TagGpsLatitudeRef,
            ExifInterface.TagGpsLatitude,
            ExifInterface.TagSubsecTimeOrig,
            ExifInterface.TagSubsecTimeDigitized,
            ExifInterface.TagSubsecTimeDig,
            //ExifInterface.TagImageLength,
            ExifInterface.TagMake,
            ExifInterface.TagModel,
            ExifInterface.TagDatetime,
            ExifInterface.TagFocalLength,
            ExifInterface.TagGpsDatestamp,
            ExifInterface.TagGpsTimestamp,
            ExifInterface.TagGpsProcessingMethod,
            ExifInterface.TagGpsAltitude,
            ExifInterface.TagGpsAltitudeRef,
            ExifInterface.TagIso,
            ExifInterface.TagExposureTime,
            ExifInterface.TagDatetimeDigitized,
            ExifInterface.TagSubsecTime,

        };
    }
    else if (Build.VERSION.SdkInt <= BuildVersionCodes.N) {
        attributes = new String[] {

            ExifInterface.TagGpsLongitudeRef,
            ExifInterface.TagGpsLongitude,
            ExifInterface.TagGpsLatitudeRef,
            ExifInterface.TagGpsLatitude,
            ExifInterface.TagSubsecTimeOrig,
            ExifInterface.TagSubsecTimeDigitized,
            ExifInterface.TagSubsecTimeDig,
            // ExifInterface.TagImageLength,
            ExifInterface.TagMake,
            ExifInterface.TagModel,
            ExifInterface.TagDatetime,
            ExifInterface.TagFocalLength,
            ExifInterface.TagGpsDatestamp,
            ExifInterface.TagGpsTimestamp,
            ExifInterface.TagGpsProcessingMethod,
            ExifInterface.TagGpsAltitude,
            ExifInterface.TagGpsAltitudeRef,
            ExifInterface.TagIso,
            ExifInterface.TagExposureTime,
            ExifInterface.TagDatetimeDigitized,
            ExifInterface.TagSubsecTime,
            ExifInterface.TagGpsDestBearing,
            ExifInterface.TagGpsDestBearingRef,
            ExifInterface.TagGpsImgDirection,
            ExifInterface.TagGpsDestDistance,
            ExifInterface.TagGpsDestDistanceRef,
            ExifInterface.TagGpsDestLatitude,
            ExifInterface.TagGpsDestLatitudeRef,
            ExifInterface.TagGpsDestLongitude,
            ExifInterface.TagGpsDestLongitudeRef,
            ExifInterface.TagGpsDifferential,
            ExifInterface.TagGpsMeasureMode,
            ExifInterface.TagGpsMapDatum,
            ExifInterface.TagFocalPlaneYResolution,
            ExifInterface.TagGpsTrackRef,
            ExifInterface.TagGpsTrack,
            ExifInterface.TagGpsStatus,
            ExifInterface.TagGpsSpeedRef,
            ExifInterface.TagGpsSpeed,
            ExifInterface.TagFocalPlaneXResolution,
            ExifInterface.TagCompressedBitsPerPixel,
            ExifInterface.TagFocalPlaneResolutionUnit,
            ExifInterface.TagDigitalZoomRatio,
            ExifInterface.TagDeviceSettingDescription,
            ExifInterface.TagDatetimeOriginal,
            ExifInterface.TagApertureValue,
            ExifInterface.TagArtist,
            ExifInterface.TagBitsPerSample,
            ExifInterface.TagBrightnessValue,
            ExifInterface.TagCfaPattern,
            ExifInterface.TagColorSpace,
            ExifInterface.TagFocalLengthIn35mmFilm,
            ExifInterface.TagComponentsConfiguration,
            ExifInterface.TagCompression,
            ExifInterface.TagContrast,
            ExifInterface.TagCopyright,
            ExifInterface.TagCustomRendered,
            ExifInterface.TagFlashpixVersion,
            ExifInterface.TagFlashEnergy,
            ExifInterface.TagFlash,
            ExifInterface.TagFileSource,
            ExifInterface.TagFNumber,
            ExifInterface.TagImageDescription,
            ExifInterface.TagExposureProgram,
            ExifInterface.TagExposureMode,
            ExifInterface.TagExposureIndex,
            ExifInterface.TagExposureBiasValue,
            ExifInterface.TagExifVersion,
            ExifInterface.TagGpsImgDirectionRef,
            ExifInterface.TagGpsSatellites,
            ExifInterface.TagGpsDop,
            ExifInterface.TagSubjectArea,
            ExifInterface.TagSubjectDistance,
            ExifInterface.TagSubjectDistanceRange,
            ExifInterface.TagSpectralSensitivity,
            ExifInterface.TagSaturation,
            ExifInterface.TagYResolution,
            ExifInterface.TagYCbCrSubSampling,
            ExifInterface.TagYCbCrPositioning,
            ExifInterface.TagYCbCrCoefficients,
            ExifInterface.TagXResolution,
            ExifInterface.TagIsoSpeedRatings,
            ExifInterface.TagJpegInterchangeFormat,

            ExifInterface.TagJpegInterchangeFormatLength,

            ExifInterface.TagMakerNote,

            ExifInterface.TagImageUniqueId,

            ExifInterface.TagLightSource,

            ExifInterface.TagMaxApertureValue,

            ExifInterface.TagMeteringMode,

            ExifInterface.TagWhitePoint,

            ExifInterface.TagWhiteBalance,

            ExifInterface.TagUserComment,

            ExifInterface.TagTransferFunction,

            // ExifInterface.TagThumbnailImageWidth,

            //ExifInterface.TagThumbnailImageLength,

            ExifInterface.TagSubsecTimeOriginal,

            ExifInterface.TagSubjectLocation,

            ExifInterface.TagSceneCaptureType,

            ExifInterface.TagSceneType,

            ExifInterface.TagSensingMethod,

            ExifInterface.TagSharpness,

            ExifInterface.TagShutterSpeedValue,

            ExifInterface.TagSoftware,

            ExifInterface.TagSamplesPerPixel,

            ExifInterface.TagSpatialFrequencyResponse,

            ExifInterface.TagStripByteCounts,

            ExifInterface.TagStripOffsets,

            ExifInterface.TagOecf,

            ExifInterface.TagGpsVersionId,

        };
    }
    else

    {
        attributes = new String[] {

            ExifInterface.TagGpsLongitudeRef,
            ExifInterface.TagGpsLongitude,
            ExifInterface.TagGpsLatitudeRef,
            ExifInterface.TagGpsLatitude,
            ExifInterface.TagSubsecTimeOrig,
            ExifInterface.TagSubsecTimeDigitized,
            ExifInterface.TagSubsecTimeDig,
            // ExifInterface.TagImageLength,
            ExifInterface.TagMake,
            ExifInterface.TagModel,
            ExifInterface.TagDatetime,
            ExifInterface.TagFocalLength,
            ExifInterface.TagGpsDatestamp,
            ExifInterface.TagGpsTimestamp,
            ExifInterface.TagGpsProcessingMethod,
            ExifInterface.TagGpsAltitude,
            ExifInterface.TagGpsAltitudeRef,
            ExifInterface.TagIso,
            ExifInterface.TagExposureTime,
            ExifInterface.TagDatetimeDigitized,
            ExifInterface.TagSubsecTime,
            ExifInterface.TagGpsDestBearing,
            ExifInterface.TagGpsDestBearingRef,
            ExifInterface.TagGpsImgDirection,
            ExifInterface.TagGpsDestDistance,
            ExifInterface.TagGpsDestDistanceRef,
            ExifInterface.TagGpsDestLatitude,
            ExifInterface.TagGpsDestLatitudeRef,
            ExifInterface.TagGpsDestLongitude,
            ExifInterface.TagGpsDestLongitudeRef,
            ExifInterface.TagGpsDifferential,
            ExifInterface.TagGpsMeasureMode,
            ExifInterface.TagGpsMapDatum,
            ExifInterface.TagFocalPlaneYResolution,
            ExifInterface.TagGpsTrackRef,
            ExifInterface.TagGpsTrack,
            ExifInterface.TagGpsStatus,

            ExifInterface.TagGpsSpeedRef,

            ExifInterface.TagGpsSpeed,

            ExifInterface.TagFocalPlaneXResolution,

            ExifInterface.TagCompressedBitsPerPixel,

            ExifInterface.TagFocalPlaneResolutionUnit,

            ExifInterface.TagDigitalZoomRatio,

            ExifInterface.TagDeviceSettingDescription,

            ExifInterface.TagDatetimeOriginal,

            ExifInterface.TagApertureValue,

            ExifInterface.TagArtist,

            ExifInterface.TagBitsPerSample,

            ExifInterface.TagBrightnessValue,

            ExifInterface.TagCfaPattern,

            ExifInterface.TagColorSpace,

            ExifInterface.TagFocalLengthIn35mmFilm,

            ExifInterface.TagComponentsConfiguration,

            ExifInterface.TagCompression,

            ExifInterface.TagContrast,

            ExifInterface.TagCopyright,

            ExifInterface.TagCustomRendered,

            ExifInterface.TagFlashpixVersion,

            ExifInterface.TagFlashEnergy,

            ExifInterface.TagFlash,

            ExifInterface.TagFileSource,

            ExifInterface.TagFNumber,

            ExifInterface.TagImageDescription,

            ExifInterface.TagExposureProgram,

            ExifInterface.TagExposureMode,

            ExifInterface.TagExposureIndex,

            ExifInterface.TagExposureBiasValue,

            ExifInterface.TagExifVersion,

            ExifInterface.TagGpsImgDirectionRef,

            ExifInterface.TagGpsSatellites,

            ExifInterface.TagGpsDop,

            ExifInterface.TagSubjectArea,

            ExifInterface.TagSubjectDistance,

            ExifInterface.TagSubjectDistanceRange,

            ExifInterface.TagSpectralSensitivity,

            ExifInterface.TagSaturation,

            ExifInterface.TagYResolution,

            ExifInterface.TagYCbCrSubSampling,

            ExifInterface.TagYCbCrPositioning,

            ExifInterface.TagYCbCrCoefficients,

            ExifInterface.TagXResolution,

            ExifInterface.TagIsoSpeedRatings,

            ExifInterface.TagJpegInterchangeFormat,

            ExifInterface.TagJpegInterchangeFormatLength,

            ExifInterface.TagMakerNote,

            ExifInterface.TagImageUniqueId,

            ExifInterface.TagLightSource,

            ExifInterface.TagMaxApertureValue,

            ExifInterface.TagMeteringMode,

            ExifInterface.TagWhitePoint,

            ExifInterface.TagWhiteBalance,

            ExifInterface.TagUserComment,

            ExifInterface.TagTransferFunction,

            // ExifInterface.TagThumbnailImageWidth,

            //ExifInterface.TagThumbnailImageLength,

            ExifInterface.TagSubsecTimeOriginal,

            ExifInterface.TagSubjectLocation,

            ExifInterface.TagSceneCaptureType,

            ExifInterface.TagSceneType,

            ExifInterface.TagSensingMethod,

            ExifInterface.TagSharpness,

            ExifInterface.TagShutterSpeedValue,

            ExifInterface.TagSoftware,

            ExifInterface.TagSamplesPerPixel,

            ExifInterface.TagSpatialFrequencyResponse,

            ExifInterface.TagStripByteCounts,

            ExifInterface.TagStripOffsets,

            ExifInterface.TagOecf,

            ExifInterface.TagGpsVersionId,
            ExifInterface.TagRw2SensorRightBorder,

            ExifInterface.TagNewSubfileType,

            ExifInterface.TagOrfAspectFrame,

            ExifInterface.TagRw2SensorTopBorder,

            ExifInterface.TagSubfileType,

            ExifInterface.TagDngVersion,

            ExifInterface.TagDefaultCropSize

        };
    }#endregion

    ExifInterface newExif = new ExifInterface(newPath);

    foreach(var attribute in attributes) {
        String value = oldExif.GetAttribute(attribute);
        if (value != null) newExif.SetAttribute(attribute, value);
    }
    newExif.SaveAttributes();

}