2010-10-28 20 views
3

J'utilise AvFoundation pour prendre des images fixes et ajouter des informations GPS aux métadonnées et les enregistrer dans un album photo à l'aide de la bibliothèque de biens, mais les informations GPS ne sont pas sauvegardées du tout.Problème lors de l'écriture des métadonnées sur l'image

voici mon code ...

[self.stillImageTaker captureStillImageAsynchronouslyFromConnection:videoConnection 
        completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) 
    { 

if (imageDataSampleBuffer != NULL) 

    { 

      CFDictionaryRef exifAttachments = CMGetAttachment(imageDataSampleBuffer,kCGImagePropertyExifDictionary, NULL); 
      CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 


     NSDictionary *gpsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"1",kCGImagePropertyGPSVersion, 
           @"78.4852",kCGImagePropertyGPSLatitude,@"32.1456",kCGImagePropertyGPSLongitude, nil]; 

     CMSetAttachment(imageDataSampleBuffer,kCGImagePropertyGPSDictionary,gpsDict,kCMAttachmentMode_ShouldPropagate); 

     CFDictionaryRef newMetadata = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
     CFDictionaryRef gpsAttachments = CMGetAttachment(imageDataSampleBuffer,kCGImagePropertyGPSDictionary, NULL); 

     if (exifAttachments) 
     { // Attachments may be read or additional ones written 

     } 

     NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; 
     UIImage *image = [[UIImage alloc] initWithData:imageData]; 

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


     NSDictionary *newDict = (NSDictionary *)newMetadata; 

     [library writeImageToSavedPhotosAlbum:[image CGImage] 
            metadata:newDict completionBlock:^(NSURL *assetURL, NSError *error) 
     { 
      if (error) 
      { 

      }                        
     }]; 

     [library release]; 
     [image release]; 
     CFRelease(metadataDict); 
     CFRelease(newMetadata); 

    } 
    else if (error) 
    { 

    } 

}]; 
+0

J'ai le même problème que vous. Avez-vous déjà trouvé une solution pour écrire des métadonnées exif sur une photo? Il semble qu'il y ait des étiquettes d'exigence supplémentaires dans la pièce jointe, mais elles ne sont jamais écrites sur la photo. –

Répondre

9

J'ai eu exactement le même problème. Je pense que la documentation sur ce sujet n'est pas géniale, donc je l'ai résolu à la fin en regardant les métadonnées d'une photo prise par l'application Camera et en essayant de la répliquer.

Voici une course vers le bas des propriétés l'application Appareil photo Saves:

  • kCGImagePropertyGPSLatitude (NSNumber)(Latitude au format décimal)
  • kCGImagePropertyGPSLongitude (NSNumber)(Longitude au format décimal)
  • kCGImagePropertyGPSLatitu DEREF (NSString)(N ou S)
  • kCGImagePropertyGPSLongitudeRef (NSString)(E ou W)
  • kCGImagePropertyGPSTimeStamp (NSString)(du format 04: 30: 51.71 (horodateur UTC))

Si vous y tenez, ça devrait aller. Voici un exemple:

CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict); 

NSDictionary *gpsDict = [NSDictionary 
    dictionaryWithObjectsAndKeys: 
    [NSNumber numberWithFloat:self.currentLocation.coordinate.latitude], kCGImagePropertyGPSLatitude, 
    @"N", kCGImagePropertyGPSLatitudeRef, 
    [NSNumber numberWithFloat:self.currentLocation.coordinate.longitude], kCGImagePropertyGPSLongitude, 
    @"E", kCGImagePropertyGPSLongitudeRef, 
    @"04:30:51.71", kCGImagePropertyGPSTimeStamp, 
    nil]; 

CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, gpsDict); 

// Get the image 
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; 
UIImage *image = [[UIImage alloc] initWithData:imageData]; 

// Get the assets library 
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 
[library writeImageToSavedPhotosAlbum:[image CGImage] metadata:mutable completionBlock:captureComplete]; 

Tout cela est dans le completionHandler de la méthode captureStillImageAsynchronouslyFromConnection de votre objet AVCaptureConnection et self.currentLocation est juste un CLLocation. J'ai codé en dur le timestamp et Lat/Lng Refs pour l'exemple afin de garder les choses simples.

Espérons que cela aide!

2

La réponse de Mason m'a vraiment aidé. Vous aurez besoin de quelques modifications telles que la définition de la valeur absolue de la longitude & latitude. Voici un extrait de code d'utilisation CoreLocation + image d'E/S pour écrire un UIImage sur le disque avec des informations GPS:

- (BOOL)writeCGImage:(CGImageRef)theImage toURL:(NSURL*)url withType:(CFStringRef)imageType andOptions:(CFDictionaryRef)options { 
    CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef)url, imageType, 1, nil); 
    CGImageDestinationAddImage(myImageDest, theImage, options); 
    BOOL success      = CGImageDestinationFinalize(myImageDest); 

    // Memory Mangement 
    CFRelease(myImageDest); 
    if (options) 
     CFRelease(options); 

    return success; 
} 

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { 
    if (newLocation) { 
     [manager stopUpdatingLocation]; 

     // Create formatted date 
     NSTimeZone  *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; 
     NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 
     [formatter setTimeZone:timeZone]; 
     [formatter setDateFormat:@"HH:mm:ss.SS"]; 

     // Create GPS Dictionary 
     NSDictionary *gpsDict = [NSDictionary dictionaryWithObjectsAndKeys: 
            [NSNumber numberWithFloat:fabs(newLocation.coordinate.latitude)], kCGImagePropertyGPSLatitude 
            , ((newLocation.coordinate.latitude >= 0) ? @"N" : @"S"), kCGImagePropertyGPSLatitudeRef 
            , [NSNumber numberWithFloat:fabs(newLocation.coordinate.longitude)], kCGImagePropertyGPSLongitude 
            , ((newLocation.coordinate.longitude >= 0) ? @"E" : @"W"), kCGImagePropertyGPSLongitudeRef 
            , [formatter stringFromDate:[newLocation timestamp]], kCGImagePropertyGPSTimeStamp 
            , nil]; 

     // Memory Management 
     [formatter release]; 

     // Set GPS Dictionary to be part of media Metadata 
     // NOTE: mediaInfo in this sample is dictionary object returned in UIImagePickerController delegate: 
     // imagePickerController:didFinishPickingMediaWithInfo 
     if (mediaInfo && [mediaInfo objectForKey:UIImagePickerControllerMediaMetadata] && gpsDict) { 
      [[mediaInfo objectForKey:UIImagePickerControllerMediaMetadata] setValue:gpsDict forKey:@"{GPS}"]; 
     } 

     // Save Image 
     if([self writeCGImage:[image CGImage] toURL:imageSaveURL withType:kUTTypeJPEG andOptions:(CFDictionaryRef)[mediaInfo objectForKey:UIImagePickerControllerMediaMetadata]]) { 
      // Image is written to device 
     } 
    } 
} 
0

Il y a plusieurs réponses ci-dessus et ailleurs sur SO qui donnent le plus de ce que vous voulez. Voici le code de travail que j'ai généré à partir de toutes ces sources, et cela semble fonctionner correctement.

Tout cela est effectué dans le bloc captureStillImageAsynchronouslyFromConnection. Mes remerciements à tous les différents contributeurs:

[stillCapture 
captureStillImageAsynchronouslyFromConnection: stillConnection 
completionHandler: 
^(CMSampleBufferRef imageSampleBuffer, NSError *error) { 
    if (error) { 
     NSLog(@"snap capture error %@", [error localizedDescription]); 
     return; 
    } 

    NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 

    CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
    CFMutableDictionaryRef mutableAttachments = CFDictionaryCreateMutableCopy(NULL, 0, attachments); 

    // Create GPS Dictionary 
    NSMutableDictionary *gps = [NSMutableDictionary dictionary]; 

    CLLocation *location = <your location source here>; 

    // GPS tag version 
    [gps setObject:@"2.2.0.0" forKey:(NSString *)kCGImagePropertyGPSVersion]; 

    // Time and date must be provided as strings, not as an NSDate object 
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 
    [formatter setDateFormat:@"HH:mm:ss.SSSSSS"]; 
    [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; 
    [gps setObject:[formatter stringFromDate:location.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp]; 

    // Latitude 
    [gps setObject: (location.coordinate.latitude < 0) ? @"S" : @"N" 
      forKey:(NSString *)kCGImagePropertyGPSLatitudeRef]; 
    [gps setObject:[NSNumber numberWithDouble:fabs(location.coordinate.latitude)] 
      forKey:(NSString *)kCGImagePropertyGPSLatitude]; 

    // Longitude 
    [gps setObject: (location.coordinate.longitude < 0) ? @"W" : @"E" 
      forKey:(NSString *)kCGImagePropertyGPSLongitudeRef]; 
    [gps setObject:[NSNumber numberWithDouble:fabs(location.coordinate.longitude)] 
      forKey:(NSString *)kCGImagePropertyGPSLongitude]; 

    // Altitude 
    if (!isnan(location.altitude)){ 
     // NB: many get this wrong, it is an int, not a string: 
     [gps setObject:[NSNumber numberWithInt: location.altitude >= 0 ? 0 : 1] 
       forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; 
     [gps setObject:[NSNumber numberWithDouble:fabs(location.altitude)] 
       forKey:(NSString *)kCGImagePropertyGPSAltitude]; 
    } 

    // Speed, must be converted from m/s to km/h 
    if (location.speed >= 0){ 
     [gps setObject:@"K" forKey:(NSString *)kCGImagePropertyGPSSpeedRef]; 
     [gps setObject:[NSNumber numberWithDouble:location.speed*3.6] forKey:(NSString *)kCGImagePropertyGPSSpeed]; 
    } 

    // Heading 
    if (location.course >= 0){ 
     [gps setObject:@"T" forKey:(NSString *)kCGImagePropertyGPSTrackRef]; 
     [gps setObject:[NSNumber numberWithDouble:location.course] forKey:(NSString *)kCGImagePropertyGPSTrack]; 
    } 

    CFDictionarySetValue(mutableAttachments, kCGImagePropertyGPSDictionary, CFBridgingRetain(gps)); 

//   NSDictionary *ma = (__bridge NSDictionary *)(mutableAttachments); 
//   NSLog(@"photo attachments: %@", ma); 

    [ROOTVC.library 
     writeImageDataToSavedPhotosAlbum:jpegData 
     metadata:(__bridge id)mutableAttachments 
     completionBlock:^(NSURL *assetURL, NSError *error) { 
      if (error) { 
       NSLog(@"XXX save to assets failed: %@", [error localizedDescription]); 
      } else { 
       [self processAsset:assetURL inGroup:ROOTVC.venue forMessage:savingMessage]; 
      } 
     }]; 

    if (mutableAttachments) 
     CFRelease(mutableAttachments); 
    if (attachments) 
     CFRelease(attachments); 
}]; 

Comme toujours, je m'inquiète si je reçois les bonnes versions.Voici un jhead (1) du résultat:

File name : 20131001_082119/photo.jpeg 
File size : 82876 bytes 
File date : 2013:10:01 08:21:19 
Resolution : 480 x 360 
Orientation : rotate 90 
Flash used : No 
Focal length : 4.1mm (35mm equivalent: 30mm) 
Exposure time: 0.0083 s (1/120) 
Aperture  : f/2.2 
ISO equiv. : 6400 
Whitebalance : Auto 
Metering Mode: pattern 
Exposure  : program (auto) 
GPS Latitude : N 40d 43m 22.32s 
GPS Longitude: W 74d 34m 39.15s 
GPS Altitude : 117.92m