Getting metadata from images on iOS

Email This Post Email This Post

Recap

My latest post was on how to write image metadata on iOS. I got a lot of good feedback from it so I think people are interested in this kind of stuff. I got 33 people watching my repo on GitHub. Cool, I got code stalkers!

One thing was missing from the post though: how to get metadata from existing images. In this post I’ll show you a few methods to do this as well as how to use my NSMutableDictionary category to do this.

Getting images using UIImagePickerController

If you’re getting images from an UIImagePickerController you have to implement this delegate method:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

In iOS 4.1 or greater your info dictionary has a key called UIImagePickerControllerReferenceURL (for images from the library) or UIImagePickerControllerMediaMetadata (for images taken from the camera). If your info has the UIImagePickerControllerMediaMetadata key, then you just have to initialize your NSMutableDictionary with the NSDictionary you get from the info dictionary:

NSMutableDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:[info objectForKey:UIImagePickerControllerMediaMetadata]];

But if you took an image from the library things are a little more complicated and not obvious at first sight. All you get in a NSURL object. How to get the metadata from this?? Using the AssetsLibrary framework, that’s how!

NSMutableDictionary *imageMetadata = nil;
NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
 
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:assetURL
    resultBlock:^(ALAsset *asset)  {
        NSDictionary *metadata = asset.defaultRepresentation.metadata;
        imageMetadata = [[NSMutableDictionary alloc] initWithDictionary:metadata];
        [self addEntriesFromDictionary:metadata];
    }
    failureBlock:^(NSError *error) {
    }];
[library autorelease];

One caveat on using this: because it uses blocks, there’s no guarantee that your imageMetadata dictionary will be populated when this code runs. In some testing I’ve done it sometimes runs the code inside the block even before the [library autorelease] is executed. But the first time you run this, the code inside the block will only run on another cycle of the apps main loop. So, if you need to use this info right away, it’s better to schedule a method on the run queue for later with:

[self performSelectorOnMainThread:SELECTOR withObject:SOME_OBJECT waitUntilDone:NO];

To make things easier, I’ve created an init method to my category:

- (id)initWithInfoFromImagePicker:(NSDictionary *)info;

You just have to add the NSMutableDictionary+ImageMetadata.h to your file and then use:

NSMutableDictionary *metadata = [[NSMutableDictionary alloc] initWithInfoFromImagePicker:info];

And you’re done! The category checks for the iOS version and for the correct keys and does everything for you. Just be careful about the issue with blocks I mentioned above.

Reading from the asset library

Well, I kinda spoiled the answer to this one already. If you’re using the AssetsLibrary to read images, you can use the method above, with the same caveat: it might not be accessible until some time after the method is called.

Again I created an init method in my category:

- (id)initFromAssetURL:(NSURL*)assetURL;

Using AVFoundation

iOS 4.0 introduced AVFoundation. AVFoundation gives us a lot of possibilities to work with pictures and the camera. Before iOS 4 if you wanted to take a picture you’d have to use an UIImagePickerController. Now you can use AVFoundation and have a lot of control over the camera, the flash, the preview, etc…

If you use AVFoundation to capture photos you’ll probably use AVCaptureStillImageOutput‘s:

- (void)captureStillImageAsynchronouslyFromConnection:(AVCaptureConnection *)connection 
                                    completionHandler:(void (^)(CMSampleBufferRef imageDataSampleBuffer, NSError *error))handler

The completion handler gives you a CMSampleBufferRef that has the metadata. But how to get it out f there is not clear from the documentation. It turns out it’s really simple:

CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate);

Since CFDictionaryRef is toll free bridged with NSDictionary, the whole process would look like this:

CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate);
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(NSDictionary*)metadataDict];
CFRelease(metadataDict);

At the risk of repeating myself, I again created an init method for this:

- (id)initWithImageSampleBuffer:(CMSampleBufferRef) imageDataSampleBuffer;

Wrapping up

So, there you have it, now you can read and write metadata.

What’s still missing are some methods to easily extract information from this dictionary. I have already created another method to extract the CLLocation information from it. As I now have a way to get and set this information I even converted it to a @property on the category, giving our NSMutableDictionary a nice way to access the location using the dot notation.

It’s very easy to add getter methods for every property but I have not done so yet. Feel free to fork my repo on GitHub and send pull requests for me to incorporate.

I also added another method to add the image’s digital zoom as the next update of Snap will have digital zoom and I’m writing this information to the pictures as well.

Snap iPhone camera app

Oh, and have I mentioned that you should get Snap for your iPhone? Check it out. You don’t know how useful and fun your iPhone camera can be until you have Snap!

Growth hormone therapy kids growth hormone therapy kids. Muziektherapie bij tinnitus muziektherapie bij tinnitus. Chinese herbs weight loss chinese herbs weight loss. Cam transgender cam transgender. Gay chat and webcam gay chat and webcam. Hemoroid treatment hemoroid treatment. Treatments for fibroids treatments for fibroids. Olympian labs hoodia gordonii review olympian labs hoodia gordonii review. Anoxine no prescription anoxine no prescription. Piano lessons little rock arkansas piano lessons little rock arkansas. Argan oil psoriasis treatment argan oil psoriasis treatment. Herpes pillow herpes pillow. Gay webcam sex gay webcam sex. Blood pressure medication and heartburn blood pressure medication and heartburn. Hemorrhoids removal procedure hemorrhoids removal procedure. Production of growth hormone production of growth hormone. Will shingles vaccine help herpes will shingles vaccine help herpes. Huge cock on cam huge cock on cam. Tall clock plans tall clock plans. Vaser liposuction gynecomastia cost vaser liposuction gynecomastia cost. Building plans for a shed building plans for a shed. Isotretinoin rosacea cure isotretinoin rosacea cure. How to read notes for piano how to read notes for piano


Keep tuned for more technical and non technical posts. You can subscribe by email, RSS or following me on twitter.


Email This Post Email This Post
  • Pingback: The CodeCropper » Adding metadata to iOS images the easy way

  • Seung-Un Ham

    just use this code

    - (void)imagePickerController:(UIImagePickerController *)picker 
    didFinishPickingMediaWithInfo:(NSDictionary *)info {
        NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
        
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        
        [library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
            ALAssetRepresentation *representation = [asset defaultRepresentation];
            NSDictionary *metadataDict = [representation metadata]; 
            NSLog(@”%@”,metadataDict);
            
        } failureBlock:^(NSError *error) {
            NSLog(@”%@”,[error description]);
        }];
        [library release];
        
        imgView.image = [info objectForKey:@"UIImagePickerControllerEditedImage"];
        [self dismissModalViewControllerAnimated:YES];   
    }

    • http://twitter.com/codecrop CodeCrop Software

      This is almost exactly like the code I mentioned. The problem is that it sometimes does not run the block until some time later.

      • CullenSUn

        Good to know that

      • Sebastian

         I’ve been trying to make your code more synchronous without much success.  I either get stuck in a dead lock or get exceptions due to the fact that the block executes some moments later.

        Have you been able to work around this?
        Or perhaps rewrite it to not use blocks and guarantee synchronous execution?

  • Cullen Sun

    Hi Gustavo,
    Thanks for the nice Category.
    Realized that you intended to write two convenient init method for reading the metadata from imagepicker’s info/Asset URL.
    However, I had difficulty to setup a quick demo or read/write image’s metadata. Eventually, I found that the two convenience init method has some problem: the object ‘self’ is returned before block is executed, so it always return empty NSMutableDictionary.
    I have rewritten the two methods to a ViewController’s Class methods. If anyone wants the demo, please send me an email. Thanks.

     
     

    • Sebastian

      I get a runtime error:
      __NSPlaceholderDictionary initWithInfoFromImagePicker:]: unrecognized selector sent to instance

      And would like to see your demo code on how you avoid.

      • Sebastian

        Fixed it!
        Strangely the .m file wasn’t included in with my compiling. (weird)
        I  manually added it to Target->Build Phases->Compile Sources and then had to convert it to ARC.  But it finally compiled in then.

        • Cullen Sun

          Good to know that

    • bacho

      Hi Cullen,

      Can you please email me the Demo of the ViewController’s Class method?
      i am not able to make it work.
      thanks
      my email: bacho_666@hotmail.com

  • Faizal khan

    Great work Gustavo.

  • deepu

    i tried to save an image with my own metadata as shown:

    NSDictionary *metaDict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:@”blah blah”,@”userInfo”, nil];

    [asset writeImageDataToSavedPhotosAlbum:UIImageJPEGRepresentation(image, 1.0) metadata:metaDict completionBlock:^(NSURL *assetURL, NSError *error) {

    }

    And from that image i tried to retrive it like this:

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    [library assetForURL:url

    resultBlock:^(ALAsset *asset) {

    NSDictionary *metadata = asset.defaultRepresentation.metadata;

    imageMetadata = [[NSMutableDictionary alloc] initWithDictionary:metadata];

    }
    failureBlock:^(NSError *error) {
    }];

    But couldn’t see the metadata that I added. Can you help me?

    • http://blog.codecropper.com/ Gustavo Ambrozio

      Did you read my other article: http://blog.codecropper.com/2011/05/adding-metadata-to-ios-images-the-easy-way/

      Also, when you first run this code your app might ask for your location information. That might seem odd since you’re not trying to use the user’s location but this authorization is actually to get metadata from your images. So, if you don’t grant this you won’t get the metadata back

  • Pingback: iOS Email sending photos strips GPS Data but not the iOS Email App - Objective-C Solutions - Developers Q & A