Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write UIImage along with metadata (EXIF, GPS, TIFF) in iPhone's Photo library

I am developing a project, where the requirements are: - User will open the camera through the application - Upon capturing an Image, some data will be appended to the captured image's metadata. I have gone through some of the forums. I tried to code this logic. I guess, I have reached to the point, but something is missing as I am not able to see the metadata that I am appending to the image. My code is:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)dictionary 
{

    [picker dismissModalViewControllerAnimated:YES];

    NSData *dataOfImageFromGallery = UIImageJPEGRepresentation (image,0.5);
    NSLog(@"Image length:  %d", [dataOfImageFromGallery length]);


    CGImageSourceRef source;
    source = CGImageSourceCreateWithData((CFDataRef)dataOfImageFromGallery, NULL);

    NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);

    NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease];
    [metadata release];

    NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease];
    NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]autorelease];


    if(!EXIFDictionary) 
    {
        //if the image does not have an EXIF dictionary (not all images do), then create one for us to use
        EXIFDictionary = [NSMutableDictionary dictionary];
    }

    if(!GPSDictionary) 
    {
        GPSDictionary = [NSMutableDictionary dictionary];
    }

    //Setup GPS dict - 
    //I am appending my custom data just to test the logic……..

    [GPSDictionary setValue:[NSNumber numberWithFloat:1.1] forKey:(NSString*)kCGImagePropertyGPSLatitude];
    [GPSDictionary setValue:[NSNumber numberWithFloat:2.2] forKey:(NSString*)kCGImagePropertyGPSLongitude];
    [GPSDictionary setValue:@"lat_ref" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
    [GPSDictionary setValue:@"lon_ref" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
    [GPSDictionary setValue:[NSNumber numberWithFloat:3.3] forKey:(NSString*)kCGImagePropertyGPSAltitude];
    [GPSDictionary setValue:[NSNumber numberWithShort:4.4] forKey:(NSString*)kCGImagePropertyGPSAltitudeRef]; 
    [GPSDictionary setValue:[NSNumber numberWithFloat:5.5] forKey:(NSString*)kCGImagePropertyGPSImgDirection];
    [GPSDictionary setValue:@"_headingRef" forKey:(NSString*)kCGImagePropertyGPSImgDirectionRef];

    [EXIFDictionary setValue:@"xml_user_comment" forKey:(NSString *)kCGImagePropertyExifUserComment];
    //add our modified EXIF data back into the image’s metadata
    [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
    [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];

    CFStringRef UTI = CGImageSourceGetType(source);
    NSMutableData *dest_data = [NSMutableData data];

    CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef) dest_data, UTI, 1, NULL);

    if(!destination)
    {
        NSLog(@"--------- Could not create image destination---------");
    }


    CGImageDestinationAddImageFromSource(destination, source, 0, (CFDictionaryRef) metadataAsMutable);

    BOOL success = NO;
    success = CGImageDestinationFinalize(destination);

    if(!success)
    {
        NSLog(@"-------- could not create data from image destination----------");
    }

    UIImage * image1 = [[UIImage alloc] initWithData:dest_data];
    UIImageWriteToSavedPhotosAlbum (image1, self, nil, nil);    
}

Kindly, help me to do this and get something positive. Look at the last line, am I saving the image with my metadata in it? The image is getting saved at that point, but the metadata that I am appending to it, is not getting saved.

Thanks in advance.

like image 802
Sid Avatar asked Nov 01 '11 10:11

Sid


People also ask

Do iPhone photos have EXIF data?

Nearly every photo you take on your iPhone has a batch of hidden information stored within: metadata. This metadata, known more specifically as EXIF data for images, contains descriptive information that makes each image unique. That includes the creation date, camera information and settings and your location.

How do I see EXIF data on iPhone?

Here's how to get to the new information pane in the ‌Photos‌ app on iPhones and iPads running ‌iOS 15‌/iPadOS 15. Open the Photos app and tap an image in your Library. Tap the info button (the encircled "i" icon) below the image. Look for the EXIF data in the box below the date and time.

How do I find EXIF data on a photo?

Viewing EXIF data in Windows is easy. 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.

How do I get metadata from a picture on my phone?

Swipe up on the photo or tap the three-dot menu icon in the top right corner. You'll see the photo's EXIF data displayed in a nice, readable format that includes the following data: Date and time taken. Image name, size, and resolution.


2 Answers

Apple has updated their article addressing this issue (Technical Q&A QA1622). If you're using an older version of Xcode, you may still have the article that says, more or less, tough luck, you can't do this without low-level parsing of the image data.

https://developer.apple.com/library/ios/#qa/qa1622/_index.html

I adapted the code there as follows:

- (void) saveImage:(UIImage *)imageToSave withInfo:(NSDictionary *)info
{
    // Get the assets library
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    // Get the image metadata (EXIF & TIFF)
    NSMutableDictionary * imageMetadata = [[info objectForKey:UIImagePickerControllerMediaMetadata] mutableCopy];

    // add GPS data
    CLLocation * loc = <•••>; // need a location here
    if ( loc ) {
        [imageMetadata setObject:[self gpsDictionaryForLocation:loc] forKey:(NSString*)kCGImagePropertyGPSDictionary];
    }

    ALAssetsLibraryWriteImageCompletionBlock imageWriteCompletionBlock =
    ^(NSURL *newURL, NSError *error) {
        if (error) {
            NSLog( @"Error writing image with metadata to Photo Library: %@", error );
        } else {
            NSLog( @"Wrote image %@ with metadata %@ to Photo Library",newURL,imageMetadata);
        }
    };

    // Save the new image to the Camera Roll
    [library writeImageToSavedPhotosAlbum:[imageToSave CGImage] 
                                 metadata:imageMetadata 
                          completionBlock:imageWriteCompletionBlock];
    [imageMetadata release];
    [library release];
}

and I call this from

imagePickerController:didFinishPickingMediaWithInfo:

which is the delegate method for the image picker.

I use a helper method (adapted from GusUtils) to build a GPS metadata dictionary from a location:

- (NSDictionary *) gpsDictionaryForLocation:(CLLocation *)location
{
    CLLocationDegrees exifLatitude  = location.coordinate.latitude;
    CLLocationDegrees exifLongitude = location.coordinate.longitude;

    NSString * latRef;
    NSString * longRef;
    if (exifLatitude < 0.0) {
        exifLatitude = exifLatitude * -1.0f;
        latRef = @"S";
    } else {
        latRef = @"N";
    }

    if (exifLongitude < 0.0) {
        exifLongitude = exifLongitude * -1.0f;
        longRef = @"W";
    } else {
        longRef = @"E";
    }

    NSMutableDictionary *locDict = [[NSMutableDictionary alloc] init];

    [locDict setObject:location.timestamp forKey:(NSString*)kCGImagePropertyGPSTimeStamp];
    [locDict setObject:latRef forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
    [locDict setObject:[NSNumber numberWithFloat:exifLatitude] forKey:(NSString *)kCGImagePropertyGPSLatitude];
    [locDict setObject:longRef forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
    [locDict setObject:[NSNumber numberWithFloat:exifLongitude] forKey:(NSString *)kCGImagePropertyGPSLongitude];
    [locDict setObject:[NSNumber numberWithFloat:location.horizontalAccuracy] forKey:(NSString*)kCGImagePropertyGPSDOP];
    [locDict setObject:[NSNumber numberWithFloat:location.altitude] forKey:(NSString*)kCGImagePropertyGPSAltitude];

    return [locDict autorelease];

}

So far this is working well for me on iOS4 and iOS5 devices.

Update: and iOS6/iOS7 devices. I built a simple project using this code:

https://github.com/5teev/MetaPhotoSave

like image 112
Code Roadie Avatar answered Oct 24 '22 12:10

Code Roadie


The function: UIImageWriteToSavePhotosAlbum only writes the image data.

You need to read up on the ALAssetsLibrary

The method you ultimately want to call is:

 ALAssetsLibrary *library = [[ALAssetsLibrary alloc]
 [library writeImageToSavedPhotosAlbum:metadata:completionBlock];
like image 34
Rayfleck Avatar answered Oct 24 '22 12:10

Rayfleck