Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Scott, a developer at Apple, returned with my incident report and fixed the issue:</p> <blockquote> <p>The previous code above is writing <strong>string values</strong> for the GPS values - <em>this won't work</em>, they <strong>must be NS/CFNumbers</strong> (we extract a float value for EXIF).</p> </blockquote> <p>I'll be filing a bug report to Apple against their documentation.</p> <p>Although it took a week to get this response, I really do appreciate the support Apple provides to their developers. (thanks Scott!) <strong>;-)</strong></p> <p>Below is the improved code and its correct output:</p> <pre><code>[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL); //get all the metadata in the image NSDictionary *metadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imgSource, 0, NULL); //make the metadata dictionary mutable so we can add properties to it NSMutableDictionary *metadataAsMutable = [metadata mutableCopy]; NSMutableDictionary *EXIFDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]; NSMutableDictionary *GPSDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]; NSMutableDictionary *RAWDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyRawDictionary]mutableCopy]; if(!EXIFDictionary) EXIFDictionary = [[NSMutableDictionary dictionary] init]; if(!GPSDictionary) GPSDictionary = [[NSMutableDictionary dictionary] init]; if(!RAWDictionary) RAWDictionary = [[NSMutableDictionary dictionary] init]; [GPSDictionary setObject:[NSNumber numberWithFloat:37.795] forKey:(NSString*)kCGImagePropertyGPSLatitude]; [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; [GPSDictionary setObject:[NSNumber numberWithFloat:122.410] forKey:(NSString*)kCGImagePropertyGPSLongitude]; [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; [GPSDictionary setObject:@"2012:10:18" forKey:(NSString*)kCGImagePropertyGPSDateStamp]; [GPSDictionary setObject:[NSNumber numberWithFloat:300] forKey:(NSString*)kCGImagePropertyGPSImgDirection]; [GPSDictionary setObject:[NSNumber numberWithFloat:37.795] forKey:(NSString*)kCGImagePropertyGPSDestLatitude]; [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSDestLatitudeRef]; [GPSDictionary setObject:[NSNumber numberWithFloat:122.410] forKey:(NSString*)kCGImagePropertyGPSDestLongitude]; [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSDestLongitudeRef]; [EXIFDictionary setObject:@"[S.D.] kCGImagePropertyExifUserComment" forKey:(NSString *)kCGImagePropertyExifUserComment]; [EXIFDictionary setObject:[NSNumber numberWithFloat:69.999] forKey:(NSString*)kCGImagePropertyExifSubjectDistance]; //Add the modified Data back into the image’s metadata [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; [metadataAsMutable setObject:RAWDictionary forKey:(NSString *)kCGImagePropertyRawDictionary]; CFStringRef UTI = CGImageSourceGetType(imgSource); //this is the type of image (e.g., public.jpeg) //this will be the data CGImageDestinationRef will write into NSMutableData *newImageData = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1, NULL); if(!destination) NSLog(@"***Could not create image destination ***"); //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadataAsMutable); //tell the destination to write the image data and metadata into our data object. //It will return false if something goes wrong BOOL success = NO; success = CGImageDestinationFinalize(destination); if(!success) NSLog(@"***Could not create data from image destination ***"); CIImage *testImage = [CIImage imageWithData:newImageData]; NSDictionary *propDict = [testImage properties]; NSLog(@"Final properties %@", propDict); }]; </code></pre> <p>Which outputs this:</p> <pre><code>Final properties info { ColorModel = RGB; DPIHeight = 72; DPIWidth = 72; Depth = 8; Orientation = 6; PixelHeight = 2448; PixelWidth = 3264; "{Exif}" = { ApertureValue = "2.526069"; BrightnessValue = "0.547474"; ColorSpace = 1; ComponentsConfiguration = ( 0, 0, 0, 1 ); ExifVersion = ( 2, 2, 1 ); ExposureMode = 0; ExposureProgram = 2; ExposureTime = "0.05"; FNumber = "2.4"; Flash = 16; FlashPixVersion = ( 1, 0 ); FocalLenIn35mmFilm = 35; FocalLength = "4.28"; ISOSpeedRatings = ( 320 ); MeteringMode = 5; PixelXDimension = 3264; PixelYDimension = 2448; SceneCaptureType = 0; SensingMethod = 2; ShutterSpeedValue = "4.321929"; SubjectArea = ( 1631, 1223, 881, 881 ); SubjectDistance = "69.999"; UserComment = "[S.D.] kCGImagePropertyExifUserComment"; WhiteBalance = 0; }; "{GPS}" = { DateStamp = "2012:10:18"; DestLatitude = "37.795"; DestLatitudeRef = N; DestLongitude = "122.41"; DestLongitudeRef = W; ImgDirection = 300; Latitude = "37.795"; LatitudeRef = N; Longitude = "122.41"; LongitudeRef = W; }; "{JFIF}" = { DensityUnit = 1; JFIFVersion = ( 1, 1 ); XDensity = 72; YDensity = 72; }; "{TIFF}" = { Orientation = 6; ResolutionUnit = 2; XResolution = 72; YResolution = 72; "_YCbCrPositioning" = 1; }; } </code></pre> <p>As you can see, all values are now properly embedded into the EXIF header and I've tested that this is correctly written with the JPG to the camera roll.</p> <p>Enjoy! :)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload