56
votes

If your Android app uses the device camera to take a picture and then resizes it (this is very, very common to reduce the size for upload), you might not realize that this resize operation strips the Exif metadata.

This can cause problems, especially if the device in question relies on the 'Orientation' tag to properly show the image upright.

Different Android devices handle camera/image rotation in different ways - my trusty old Nexus One seems to always rotate the image immediately post capture, so the file's native contents are always 'upright' when viewed.

However, other devices (especially Samsung phones in my testing), do not rotate the contents of the image file - rather, they set the Exif 'Orientation' tag. Whenever the image is displayed later, the relevant image code should detect the presence of the Orientation 'tag' and rotate the image appropriately. But if you have done any bitmap processing on the image and saved it to a new file, all of that Exif data is lost.

In addition to Orientation data, you might also lose other valuable metadata such as make/model, etc.

This confused me for a few weeks (image appears upright when displayed in phone gallery, but then arrives on my server with bad orientation and no apparent metadata). I'm adding this self-question here to help others. This blog post was very helpful:

Android re-size image without loosing EXIF information

5

5 Answers

21
votes

As others have indicated, you must copy the Exif data from the original image to the final resized image. The Sanselan Android library is typically best for this. Depending on Android OS version, the ExifInterface sometimes corrupts the Exifdata.

In addition, the ExifInterface also handles a limited number of Exif tags -- namely only the tags that it "knows" about. Sanselan on the other hand will keep all Exiftags and marker notes.

Here is a blog post that shows how to use Sanselan for copying image data:

Copying Exif metadata using Sanselan

BTW, on Android I also tend to rotate the images and remove the Orientation Exiftag. For example, on a Nexus S with Android 4.03, the camera was setting an orientation tag in the Exifmetadata, but the webview was ignoring that information and displaying the image incorrectly. Sadly, rotating the actual image data and removing the Exiforientation tag is the only way to get every program to display images correctly.

49
votes

As far as I can tell, there is no mechanism to persist the metadata automatically or even snapshot whatever is there and transfer in bulk.

Rather, it seems you must explicitly check for specific properties and copy them yourself to the new image file using the ExifInterface.

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

So something like:

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();
}
38
votes

For the lazy ones, here's a reusable function:

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();
}
1
votes

It's already 2019 and there is still no better answer than those proposed by @prom85, Mike Repass and Theo.

In 2016, Android team introduced ExifInterface Support Library what can be the best option if you want to have consisent behaviour between Android versions. I ended up creating a subset of tags ExifInterface#EXIF_TAGS (source code) and I just iterate over this subset to extract metadata from input file and set it in an output. If you ever get a requirement to copy over all tags I recommend you to not do it! Value of some of the tags will need to be updated accordingly anyway (e.g. TAG_IMAGE_LENGTH and TAG_IMAGE_WIDTH). Personally, I kept asking questions why we need to keep all metedata data in the first place (it differs what you get between devices and camera you use anyway) and we realised that gps location and date/time data is as well need to keep.

1
votes

Why not just modify the ExifInterface source and add your own implementation to support reading/writing in bulk so you don't have to specify tag by tag. Here is a snippet of what I would do.

Add new method to expose the internal attributes:

public HashMap<String, ExifAttribute>[] getAllAttributes() {
    return mAttributes;
}

Add new method to set all the attributes:

public void setAttributes(HashMap<String, ExifAttribute>[] attributes) {
    for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
        mAttributes[i] = attributes[i];
    }
}

Then use it like this to preserve Exif and save to another File

// Grab all the original exif attributes from an image file and save to memory or wherever
let attributes = ExifInterface2(sourceImagePath).attributes

// Later on you can just copy those attributes to another image
ExifInterface2(destImagePath)
    .setAttributes(attributes)
    .saveAttributes();