Problem in writing metadata to image

后端 未结 3 1625
灰色年华
灰色年华 2021-01-01 06:19

I am using AvFoundation to take still image and adding gps info to metadata and saving to a photo album using Asset library but gps info is not saving at all.

here

相关标签:
3条回答
  • 2021-01-01 06:42

    There are several answers above and elsewhere on SO that give most of what you want. Here is the working code I generated from all of these sources, and it seems to work just fine.

    This is all done within the captureStillImageAsynchronouslyFromConnection block. My thanks to all the various contributors:

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

    As always, I worry if I am getting the releases right. Here's a jhead(1) of the result:

    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
    
    0 讨论(0)
  • 2021-01-01 06:56

    I had exactly the same problem. I think the documentation on this topic isn't great, so I solved it in the end by looking at the metadata of a photo taken by the Camera app and trying to replicate it.

    Here's a run down of the properties the Camera app saves:

    • kCGImagePropertyGPSLatitude (NSNumber) (Latitude in decimal format)
    • kCGImagePropertyGPSLongitude (NSNumber) (Longitude in decimal format)
    • kCGImagePropertyGPSLatitudeRef (NSString) (Either N or S)
    • kCGImagePropertyGPSLongitudeRef (NSString) (Either E or W)
    • kCGImagePropertyGPSTimeStamp (NSString) (Of the format 04:30:51.71 (UTC timestamp))

    If you stick to these you should be fine. Here's a sample:

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

    This is all in the completionHandler of the captureStillImageAsynchronouslyFromConnection method of your AVCaptureConnection object, and self.currentLocation is just a CLLocation. I hardcoded the timestamp and Lat/Lng Refs for the example to keep things simple.

    Hope this helps!

    0 讨论(0)
  • 2021-01-01 06:56

    Mason's answer really helped me. You'll need some modifications such as setting the absolute value of longitude & latitude. Here's a code snippet of using CoreLocation + Image I/O to write an UIImage to disk with GPS information:

    - (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 讨论(0)
提交回复
热议问题