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

时间:2010-10-28 14:09:14

标签: cocoa-touch metadata avfoundation

我正在使用AvFoundation拍摄静止图像并将gps信息添加到元数据并使用资源库保存到相册,但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) 
    {

    }

}];

3 个答案:

答案 0 :(得分:9)

我有完全相同的问题。我认为关于这个主题的文档并不是很好,所以我最终通过查看Camera应用程序拍摄的照片的元数据并尝试复制它来解决它。

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

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

如果坚持这些,你应该没事。这是一个示例:

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以保持简单。

希望这有帮助!

答案 1 :(得分:2)

梅森的回答对我很有帮助。您需要进行一些修改,例如设置经度和安全值的绝对值。纬度。这是使用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
        }
    } 
}

答案 2 :(得分:0)

上面和其他地方有几个答案可以提供你想要的大部分内容。这是我从所有这些来源生成的工作代码,它似乎工作正常。

这一切都在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