写入元数据到图像时出现问题

3

我正在使用AvFoundation拍摄静态图像,并将GPS信息添加到元数据中,然后使用Asset库保存到照片库中,但是GPS信息根本没有保存。

这是我的代码...

[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) 
    {

    }

}];

我和你一样遇到了同样的问题。 你有没有找到写exif元数据到照片的解决方案?附件中似乎有额外的exif标签,但它们从未被写入照片。 - user573374
3个回答

9

我曾经也遇到过同样的问题。我认为关于这个主题的文档并不好,所以最终我通过查看相机应用程序拍摄的照片的元数据并尝试复制它来解决了这个问题。

以下是相机应用保存的属性:

  • kCGImagePropertyGPSLatitude (NSNumber) (以十进制格式表示的纬度)
  • kCGImagePropertyGPSLongitude (NSNumber) (以十进制格式表示的经度)
  • kCGImagePropertyGPSLatitudeRef (NSString) (N或S)
  • kCGImagePropertyGPSLongitudeRef (NSString) (E或W)
  • kCGImagePropertyGPSTimeStamp (NSString) (UTC时间戳格式的04:30:51.71)

如果你坚持遵循这些规则,那么就可以解决该问题。以下是一个示例:

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];

这一切都在AVCaptureConnection对象的captureStillImageAsynchronouslyFromConnection方法的completionHandler中完成,而self.currentLocation只是一个CLLocation。为了简单起见,我在示例中硬编码了时间戳和Lat/Lng Refs。

希望这可以帮助你!


2

Mason的回答对我非常有帮助。您需要进行一些修改,例如设置经度和纬度的绝对值。以下是使用CoreLocation + Image I/O将UIImage与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

在SO上有几个答案提供了你所需要的大部分内容。这是我从所有这些来源生成的工作代码,它似乎运行得很好。

这一切都在captureStillImageAsynchronouslyFromConnection块内完成。感谢所有不同的贡献者:

    [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);
 }];

一如既往,我担心我是否正确发布了。以下是结果的 jhead(1):

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接