From the results of UIImagePickerControlleHow, how do I get JPEG with metadata included in it?

2.6k views Asked by At

On iOS 4.2 when I use UIImagePickerController to let the user select a image from the photo library these are the dictionary keys that are returned to me:

2011-03-02 13:15:59.518 xxx[15098:307] didFinishPickingMediaWithInfo: 
  info dictionary: {
    UIImagePickerControllerMediaType = "public.image";
    UIImagePickerControllerOriginalImage = "<UIImage: 0x3405d0>";
    UIImagePickerControllerReferenceURL = 
      "assets-library://asset/asset.JPG?id=1000000050&ext=JPG";
}

Using one or more of these keys, how can I get a JPEG representation that would include the image metadata (such as exposure information and GPS location data) such that I can upload that somewhere and have the metadata included (not stripped off)?

I see from Warren Burton's very nice answer in Display image from URL retrieved from ALAsset in iPhone? how to use the UIImagePickerControllerReferenceURL and the ALAssetsLibrary assetForURL method to get to the ALAsset and the ALAssetRepresentation. But what do I do then to get to the JPEG that includes in it all the metadata?

Or is there a mechanism through the UIImage?

The bottom line here is that I want to get JPEG with the metadata included in it...

1

There are 1 answers

2
Chris Markle On BEST ANSWER

Since I asked the question I have done some more experimentation and think I know the answer now. All results were gotten on iOS 4.2 which is all I care about...

First of all, we were using UIImageJPEGRepresentation ala:

NSData *imageData = UIImageJPEGRepresentation(self.selectedImage, 0.9);

which seems to not give you (much of) the metadata (EXIF, GPS, etc.) that is in the image. Fair enough and I think that's well-known.

My testing shows that the JPEG in the "default representation" for the image asset will contain all the metadata, including EXIF and GPS information (assuming it's there in the first place). You can get that image by going from the asset URL to the Asset to the asset's default representation (ALAssetRepresentation) and then using the getBytes method/message to retrieve the bytes for the JPEG image. That stream of bytes has the aforementioned metadata in it.

Here's some example code that I use for this. It takes an Asset URL, presumed to be for an image, and returns NSData with with the JPEG. Caveat emptor with respect to your use, error handling in the code, etc.

/*
 * Example invocation assuming that info is the dictionary returned by 
 * didFinishPickingMediaWithInfo (see original SO question where
 * UIImagePickerControllerReferenceURL = "assets-library://asset/asset.JPG?id=1000000050&ext=JPG").
 */
[self getJPEGFromAssetForURL:[info objectForKey:UIImagePickerControllerReferenceURL]];
// ...

/* 
 * Take Asset URL and set imageJPEG property to NSData containing the
 * associated JPEG, including the metadata we're after.
 */
-(void)getJPEGFromAssetForURL:(NSURL *)url {
    ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
    [assetslibrary assetForURL:url
        resultBlock: ^(ALAsset *myasset) {
            ALAssetRepresentation *rep = [myasset defaultRepresentation];
#if DEBUG
            NSLog(@"getJPEGFromAssetForURL: default asset representation for %@: uti: %@ size: %lld url: %@ orientation: %d scale: %f metadata: %@", 
            url, [rep UTI], [rep size], [rep url], [rep orientation], 
            [rep scale], [rep metadata]);
#endif

            Byte *buf = malloc([rep size]);  // will be freed automatically when associated NSData is deallocated
            NSError *err = nil;
            NSUInteger bytes = [rep getBytes:buf fromOffset:0LL 
                                length:[rep size] error:&err];
            if (err || bytes == 0) {
                // Are err and bytes == 0 redundant? Doc says 0 return means 
                // error occurred which presumably means NSError is returned.

                NSLog(@"error from getBytes: %@", err);
                self.imageJPEG = nil;
                return;
            } 
            self.imageJPEG = [NSData dataWithBytesNoCopy:buf length:[rep size] 
                                     freeWhenDone:YES];  // YES means free malloc'ed buf that backs this when deallocated
        }
        failureBlock: ^(NSError *err) {
            NSLog(@"can't get asset %@: %@", url, err);
        }];
    [assetslibrary release];
}