Background: I am writing a camera app for a messenger program. I cannot save the captured image to persistent disk at any time. The camera must support all orientations. My implementation is that of the familiar Surfaceview examples. I use the Display class to detect orientation and rotate the camera accordingly. In the takePicture jpeg callback, I construct a bitmap from the byte[] in order to get around some aspect ratio issues I was having: Camera API: Cross device issues
Problem Description: On some devices, the constructed Bitmap taken at ROTATION_270 (device rotated 90 degrees clockwise) comes in upside down. So far, it seems to be Samsung. I can only assume that maybe the camera is soldered on the other way or something to that affect but that's neither here nor there. While I can check if a Bitmap is sideways I can't logically check if it is upside down by dimensions so I need access to the EXIF data.
Android provides a parser for this http://developer.android.com/reference/android/media/ExifInterface.html but unfortunately it has a single constructor which accepts a file... which I don't have and don't want. Intuitively I could write a constructor for a byte array but that seems really painful given their calls into native code http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2.1_r1/android/media/ExifInterface.java
My question then has two parts:
Does anyone know if the byte[] array contains full EXIF jpeg header data as is or is the path through the BitmapFactory.decode(...) / BitmapFactory.compress(...) adding that somehow?
If this EXIF data exits in the byte array how can I parse out the orientation information in a dependable manner?
Edit 10/18/12
pcans' answer below involves part 2 of my question. As I pointed to in the comments below his answer, if you want to use that parser you'll have to incorporate the source into your project. The changes mentioned in that linked SO post have already been made and reposted here: https://github.com/strangecargo/metadata-extractor
NOTE newer versions of metadata-extractor work directly on Android without modification, and are available via Maven.
However, as to part 1, I'm getting 0 tags back from the parser when I run it with the byte array I get from takePicture. I'm becoming concerned that the byte array doesn't have the data I need. I will continue to look into this but welcome any further insight.
Just right-click on the photo in question and select “Properties”. Click on the “Details” tab and scroll down—you'll see all kinds of information about the camera used, and the settings the photo was taken with.
Follow these steps to view EXIF data on your Android smartphone. Open Google Photos on the phone - install it if needed. Open any photo and tap the i icon. This will show you all the EXIF data you need.
android.media.ExifInterface. This is a class for reading and writing Exif tags in various image file formats. Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF, AVIF. Supported for writing: JPEG, PNG, WebP, DNG.
To read metadata/EXIF from image byte[]
(useful for Camera.takePicture()
) using version 2.9.1 of the metadata extraction library in Java by Drew Noakes:
try { // Extract metadata. Metadata metadata = ImageMetadataReader.readMetadata(new BufferedInputStream(new ByteArrayInputStream(imageData)), imageData.length); // Log each directory. for(Directory directory : metadata.getDirectories()) { Log.d("LOG", "Directory: " + directory.getName()); // Log all errors. for(String error : directory.getErrors()) { Log.d("LOG", "> error: " + error); } // Log all tags. for(Tag tag : directory.getTags()) { Log.d("LOG", "> tag: " + tag.getTagName() + " = " + tag.getDescription()); } } } catch(Exception e) { // TODO: handle exception }
To read the EXIF orientation of the image (not the orientation of the thumbnail):
try { // Get the EXIF orientation. final ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); if(exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { final int exifOrientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); /* Work on exifOrientation */ } else { /* Not found */ } } catch(Exception e) { // TODO: handle exception }
The orientation is from 1 to 8. See here, here, here or here.
To transform a bitmap based on its EXIF orientation:
try { final Matrix bitmapMatrix = new Matrix(); switch(exifOrientation) { case 1: break; // top left case 2: bitmapMatrix.postScale(-1, 1); break; // top right case 3: bitmapMatrix.postRotate(180); break; // bottom right case 4: bitmapMatrix.postRotate(180); bitmapMatrix.postScale(-1, 1); break; // bottom left case 5: bitmapMatrix.postRotate(90); bitmapMatrix.postScale(-1, 1); break; // left top case 6: bitmapMatrix.postRotate(90); break; // right top case 7: bitmapMatrix.postRotate(270); bitmapMatrix.postScale(-1, 1); break; // right bottom case 8: bitmapMatrix.postRotate(270); break; // left bottom default: break; // Unknown } // Create new bitmap. final Bitmap transformedBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, imageBitmap.getWidth(), imageBitmap.getHeight(), bitmapMatrix, false); } catch(Exception e) { // TODO: handle exception }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With