2015-08-28 48 views
1

我試圖添加/修改圖像元數據時,我將一個圖像資源添加到相冊在iOS 8.4照片集合。以下代碼成功添加GPS字典,但不添加或更改EXIF數據(我已檢查過圖像標題並使用EXIF工具進行確認)。我已經嘗試了兩種不同的方法(請參閱altSetMyImageInfo),它們具有相同的非結果。我錯過了什麼?CFDictionary更新到PHPhotoLibrary kCGImagePropertyExifDictionary不採取

@property (nonatomic) CLLocation *location; 
. . . 
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { 
    if (imageDataSampleBuffer) { 
     NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; 
     CFDictionaryRef tempDict = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
     CFMutableDictionaryRef theDict = CFDictionaryCreateMutableCopy(NULL, 0, tempDict); 

     [self setMyImageInfo:theDict]; 
     [self setMyLocation:theDict]; 

     CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); 
     CFStringRef UTI = CGImageSourceGetType(source);     
     NSMutableData *newImageData = [NSMutableData data]; 
     CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData,UTI,1,NULL); 
     BOOL success = CGImageDestinationFinalize(destination); 
     CFRelease(destination); 
     CFRelease(source); 
    } 
} 
. . . 
if ([PHAssetResourceCreationOptions class]) { 
    [[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypeFullSizePhoto data:newImageData options:nil]; 
} 
else { 
    NSError *error = nil; 
    [newImageData writeToURL:temporaryFileURL options:NSDataWritingAtomic error:&error]; 
    if (error) { 
     NSLog(@"Error: %@", error); 
    } 
    else { 
      PHAssetChangeRequest *newImageRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:temporaryFileURL]; 
      PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:theAlbum]; 
      [albumChangeRequest addAssets:@[newImageRequest.placeholderForCreatedAsset]]; 
} 
. . .    
- (void) setMyImageInfo:(CFMutableDictionaryRef)theDict 
{ 
NSMutableDictionary *exif = [NSMutableDictionary dictionary]; 
[exif setObject:@"2.2.2" forKey:(NSString *)kCGImagePropertyExifVersion]; 
[exif setObject:@「MyIdentity」 forKey:(NSString *)kCGImagePropertyExifImageUniqueID]; 
[exif setObject:@"I hope this works!" forKey:(NSString *)kCGImagePropertyExifUserComment]; 

CFDictionarySetValue(theDict, kCGImagePropertyExifDictionary, (__bridge void *)exif);  
} 
- (void) setMyLocation:(CFMutableDictionaryRef)theDict 
{ 
NSMutableDictionary *gps = [NSMutableDictionary dictionary]; 
[gps setObject:@"2.2.0.0" forKey:(NSString *)kCGImagePropertyGPSVersion]; 
NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 
[formatter setDateFormat:@"HH:mm:ss.SSSSSS"]; 
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; 
[gps setObject:[formatter stringFromDate:self.location.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp]; 
[formatter setDateFormat:@"yyyy:MM:dd"]; 
[gps setObject:[formatter stringFromDate:self.location.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp]; 
CGFloat latitude = self.location.coordinate.latitude; 
if (latitude < 0) { 
    latitude = -latitude; 
    [gps setObject:@"S" forKey:(NSString *)kCGImagePropertyGPSLatitudeRef]; 
} else { 
    [gps setObject:@"N" forKey:(NSString *)kCGImagePropertyGPSLatitudeRef]; 
} 
[gps setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString *)kCGImagePropertyGPSLatitude]; 
CGFloat longitude = self.location.coordinate.longitude; 
if (longitude < 0) { 
    longitude = -longitude; 
    [gps setObject:@"W" forKey:(NSString *)kCGImagePropertyGPSLongitudeRef]; 
} else { 
    [gps setObject:@"E" forKey:(NSString *)kCGImagePropertyGPSLongitudeRef]; 
} 
[gps setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString *)kCGImagePropertyGPSLongitude]; 
CGFloat altitude = self.location.altitude; 
if (!isnan(altitude)){ 
    if (altitude < 0) { 
     altitude = -altitude; 
     [gps setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; 
    } else { 
     [gps setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; 
    } 
    [gps setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude]; 
}  
if (self.location.speed >= 0){ 
    [gps setObject:@"K" forKey:(NSString *)kCGImagePropertyGPSSpeedRef]; 
    [gps setObject:[NSNumber numberWithFloat:self.location.speed*3.6] forKey:(NSString *)kCGImagePropertyGPSSpeed]; 
} 
if (self.location.course >= 0){ 
    [gps setObject:@"T" forKey:(NSString *)kCGImagePropertyGPSTrackRef]; 
    [gps setObject:[NSNumber numberWithFloat:self.location.course] forKey:(NSString *)kCGImagePropertyGPSTrack]; 
} 
CFDictionarySetValue(theDict, kCGImagePropertyGPSDictionary, (__bridge void *)gps); 
} 

- (void) altSetMyImageInfo:(CFMutableDictionaryRef)theDict 
{ 
CFStringRef imageUniqueID = CFStringCreateWithCString(NULL, [@「MyImageID」 cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
CFDictionarySetValue(theDict, kCGImagePropertyExifImageUniqueID, imageUniqueID); 
CFRelease (imageUniqueID);  
CFStringRef userComment = CFStringCreateWithCString(NULL, [@"I hope this works!" cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
CFDictionarySetValue(theDict, kCGImagePropertyExifUserComment, userComment); 
CFRelease (userComment); 
} 

回答

1

這個問題的答案涉及到很多東西。一個小例子,「CGImageSourceRef源」缺失。最重要的是,由於NDA限制「你們都知道誰」(你們知道,因爲我不能說),所以我不能把問題放到項目的背景中。我沒有解析這個問題,而是在代碼中發佈了答案。如果您將此代碼插入某個示例程序「您知道誰」(我不能說出示例名稱,但maCVA可能會爲您提供搜索其開發人員庫的線索)。我已經做到這樣,大部分代碼都會放在一個特定的大型源文件的末尾,而其他行則有參考行號(從頂部開始插入)。唯一的其他變化是頭文件獲取CLLocationManagerDelegate添加到@interface。您可能還想爲位置服務添加位置服務。

我已經包含調試代碼來查看日誌文件中的元數據。剩下的唯一問題是使用CFDictionarySetValue還是「setObject:forKey:」來分配字典值。我使用前者爲EXIF/TIFF數據,後者爲GPS數據,但在EXIF/TIFF方法中包含「setObject:forKey:」代碼作爲註釋。兩者都有效。 「setObject:forKey:」更簡潔。有什麼意見?

@import CoreLocation; // MAS: for GPS (line 11) 
@property (strong, nonatomic) CLLocationManager *locationManager; // MAS: for image metadata (line 42) 
@property (nonatomic) CLLocation *location; // MAS: get it early and save it here (line 43) 
@property (assign, nonatomic) id<CLLocationManagerDelegate> delegate; // MAS: do we really need this? (line 44) 
/* MAS: Instantiate location manager (in viewDidLoad, about line 70) 
*/ 
self.locationManager = [[CLLocationManager alloc] init]; 
/* MAS: end 
*/ 
    [self startStandardUpdates]; // MAS: get our location (in viewDidLoad about line 201, not quite at the end) 
/* MAS: (still in veiwDidLoad about line 206 after dispatch) 
*/ 
if ([CLLocationManager locationServicesEnabled]) { 
    NSLog(@"location services enabled"); 
} 
else { 
    NSLog(@"Could not add location services to the session"); 
    // self.setupResult = PikBitSetupResultSessionConfigurationFailed; 
    // Display alert to the user. 
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Location services" 
                    message:@"Location services are not enabled on this device. Please enable location services in settings." 
                  preferredStyle:UIAlertControllerStyleAlert]; 
    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleDefault 
                  handler:^(UIAlertAction * action) {}]; // NULL action to dismiss the alert. 
    [alert addAction:defaultAction]; 
    [self presentViewController:alert animated:YES completion:nil]; 
} 

if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) 
{ 
    [self.locationManager requestWhenInUseAuthorization]; 
    NSLog(@"location authorization status not determined"); 

} else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) { 
    NSLog(@"location services authorization was previously denied by the user."); 
    // Display alert to the user. 
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Location services" 
                    message:@"Location services were previously denied by the user. Please enable location services for this app in settings." 
                  preferredStyle:UIAlertControllerStyleAlert]; 
    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleDefault 
                  handler:^(UIAlertAction * action) {}]; // Do nothing action to dismiss the alert. 
    [alert addAction:defaultAction]; 
    [self presentViewController:alert animated:YES completion:nil]; 
} 

/* MAS: end 
*/ 
      /* MAS: about line 616, once you have image NSData 
      */ 
      CFDictionaryRef tempDict = CMCopyDictionaryOfAttachments(NULL, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
      CFMutableDictionaryRef theDict = CFDictionaryCreateMutableCopy(NULL, 0, tempDict); 

      [self setMyImageInfo:theDict]; 
      [self setMyLocation:theDict]; 

      CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); 
      CFStringRef UTI = CGImageSourceGetType(source); 
      NSMutableData *newImageData = [NSMutableData data]; 
      CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData,UTI,1,NULL); 
      if(!destination) { 
       NSLog(@"***Could not create image destination ***"); 
      } 
      CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) theDict); 

      BOOL success = CGImageDestinationFinalize(destination); 
      CFRelease(destination); 
      CFRelease(source); 
      if(success) { 
       NSLog(@"it worked, let's see the metadata"); 
       [self checkEXIF:newImageData]; 
      } 
      else { 
       NSLog(@"***Could not create data from image destination ***"); 
      } 
      /* MAS: end 
      */ 
[[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypePhoto data:newImageData options:nil]; // MAS: change data to 'NewImageData' (about line 654) 
          [newImageData writeToURL:temporaryFileURL options:NSDataWritingAtomic error:&error]; // MAS: change source to newImageData (about line 668) 
/* MAS: start (immediately before @end) 
*/ 
#pragma mark - location delegate methods 

// Start the standard location service 

- (void)startStandardUpdates 
{ 
// set the delegate and the parameters 
self.locationManager.delegate = self; 
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; 

// Set a movement threshold for new events. 
self.locationManager.distanceFilter = 500; // meters 

[self.locationManager startUpdatingLocation]; 
NSLog(@"location manager started"); 
} 

// Delegate methods 

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { 
// If it's a relatively recent event, turn off updates to save power. 
self.location = [locations lastObject]; 
NSLog(@"location manager delegate used"); 
NSDate* eventDate = self.location.timestamp; 
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow]; 
if (fabs(howRecent) < 15.0) { 
    // If the event is recent, do something with it. 
    NSLog(@"latitude %+.6f, longitude %+.6f\n", 
      self.location.coordinate.latitude, 
      self.location.coordinate.longitude); 
} 
else { 
    NSLog(@"latitude %+.6f, longitude %+.6f\n", 
      self.location.coordinate.latitude, 
      self.location.coordinate.longitude); 

} 
/* MAS: for optimizing usage (later) 

[manager stopUpdatingLocation]; // If we only want one update. 

manager.delegate = nil;   // We might be called again here, even though we 
// called "stopUpdatingLocation", so remove us as the delegate to be sure. 
*/ 
} 
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { 
// report any errors returned back from Location Services 
NSLog(@"location manager error - %@", error.description); 
} 

- (void) setMyImageInfo:(CFMutableDictionaryRef)theDict 
{ 
NSString *temp = @"This is a very long string, used to test the loading of the image description field"; 
NSString *tempID = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // used to test imageUniqueID field, should the 33rd character be a nul? 

// 
if (theDict) { 
    // save a dictionary of the TIFF image properties 
    CFDictionaryRef tiffData = CFDictionaryGetValue(theDict, kCGImagePropertyTIFFDictionary); 

    CFMutableDictionaryRef tiffDataMut; 
    if (tiffData) { 
     tiffDataMut = CFDictionaryCreateMutableCopy(nil, 0, tiffData); 
    } 
    else { 
     tiffDataMut = CFDictionaryCreateMutable(nil, 0, 
               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 
    } 
    // The best place to store a long string -- 
    CFStringRef imageDescription = CFStringCreateWithCString(NULL, [temp cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
    CFDictionarySetValue(tiffDataMut, kCGImagePropertyTIFFImageDescription, imageDescription); 
    CFRelease (imageDescription); 
    // Carry the brand into the image -- 
    CFStringRef software = CFStringCreateWithCString(NULL, [@"MyApp" cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
    CFDictionarySetValue(tiffDataMut, kCGImagePropertyTIFFSoftware, software); 
    CFRelease (software); 
    // Carry the manufacturer into the image -- 
    CFStringRef make = CFStringCreateWithCString(NULL, [@"Apple" cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
    CFDictionarySetValue(tiffDataMut, kCGImagePropertyTIFFMake, make); 
    CFRelease (make); 
    // Carry the device name into the image -- 
    CFStringRef model = CFStringCreateWithCString(NULL, [@"iPhone" cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
    CFDictionarySetValue(tiffDataMut, kCGImagePropertyTIFFModel, model); 
    CFRelease (model); 

    CFDictionarySetValue(theDict, kCGImagePropertyTIFFDictionary, tiffDataMut); 

    CFRelease(tiffDataMut); 

    // Now do the same for EXIF 
    // save a dictionary of the EXIF image properties 
    CFDictionaryRef exifData = CFDictionaryGetValue(theDict, kCGImagePropertyExifDictionary); 

    CFMutableDictionaryRef exifDataMut; 
    if (exifData) { 
     exifDataMut = CFDictionaryCreateMutableCopy(nil, 0, exifData); 
    } 
    else { 
     exifDataMut = CFDictionaryCreateMutable(nil, 0, 
               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 
    } 
    // This is a good place to store an ID, but it only holds 33 characters -- 
    CFStringRef imageUniqueID = CFStringCreateWithCString(NULL, [tempID cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
    CFDictionarySetValue(exifDataMut, kCGImagePropertyExifImageUniqueID, imageUniqueID); 
    CFRelease (imageUniqueID); 
    // Just to see that it works -- 
    CFStringRef userComment = CFStringCreateWithCString(NULL, [@"I hope this works! And it does!!" cStringUsingEncoding:NSASCIIStringEncoding], kCFStringEncodingASCII); 
    CFDictionarySetValue(exifDataMut, kCGImagePropertyExifUserComment, userComment); 
    CFRelease (userComment); 

    CFDictionarySetValue(theDict, kCGImagePropertyExifDictionary, exifDataMut); 

    CFRelease(exifDataMut); 

} 
else { 
    NSLog(@"Metadata Dictionairy for image empty"); 
} 

/* alternative version (this has been tested and works) -- 
// Create dictionaries - 
NSMutableDictionary *exif = [NSMutableDictionary dictionary]; 
NSMutableDictionary *tiff = [NSMutableDictionary dictionary]; 
// load some TIFF and EXIF data -- 
[tiff setObject:temp forKey:(NSString *)kCGImagePropertyTIFFImageDescription]; 
[tiff setObject:@"Apple" forKey:(NSString *)kCGImagePropertyTIFFMake]; 
[tiff setObject:@"iPhone" forKey:(NSString *)kCGImagePropertyTIFFModel]; 
[tiff setObject:@"PikLips" forKey:(NSString *)kCGImagePropertyTIFFSoftware]; 
[exif setObject:@"I hope this works! And it does, too!!" forKey:(NSString *)kCGImagePropertyExifUserComment]; 
[exif setObject:tempID forKey:(NSString *)kCGImagePropertyExifImageUniqueID]; 

// load the info into the passed CFMutableDictionaryRef -- 
CFDictionarySetValue(theDict, kCGImagePropertyExifDictionary, (__bridge void *)exif); 
CFDictionarySetValue(theDict, kCGImagePropertyTIFFDictionary, (__bridge void *)tiff); 

* end 
*/ 
} 
- (void) setMyLocation:(CFMutableDictionaryRef)theDict 
{ 
NSMutableDictionary *gps = [NSMutableDictionary dictionary]; 
[gps setObject:@"2.2.0.0" forKey:(NSString *)kCGImagePropertyGPSVersion]; 
NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 
[formatter setDateFormat:@"HH:mm:ss.SSSSSS"]; 
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; 
[gps setObject:[formatter stringFromDate:self.location.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp]; 
[formatter setDateFormat:@"yyyy:MM:dd"]; 
[gps setObject:[formatter stringFromDate:self.location.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp]; 
CGFloat latitude = self.location.coordinate.latitude; 
if (latitude < 0) { 
    latitude = -latitude; 
    [gps setObject:@"S" forKey:(NSString *)kCGImagePropertyGPSLatitudeRef]; 
} else { 
    [gps setObject:@"N" forKey:(NSString *)kCGImagePropertyGPSLatitudeRef]; 
} 
[gps setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString *)kCGImagePropertyGPSLatitude]; 
CGFloat longitude = self.location.coordinate.longitude; 
if (longitude < 0) { 
    longitude = -longitude; 
    [gps setObject:@"W" forKey:(NSString *)kCGImagePropertyGPSLongitudeRef]; 
} else { 
    [gps setObject:@"E" forKey:(NSString *)kCGImagePropertyGPSLongitudeRef]; 
} 
[gps setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString *)kCGImagePropertyGPSLongitude]; 
CGFloat altitude = self.location.altitude; 
if (!isnan(altitude)){ 
    if (altitude < 0) { 
     altitude = -altitude; 
     [gps setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; 
    } else { 
     [gps setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; 
    } 
    [gps setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude]; 
} 
if (self.location.speed >= 0){ 
    [gps setObject:@"K" forKey:(NSString *)kCGImagePropertyGPSSpeedRef]; 
    [gps setObject:[NSNumber numberWithFloat:self.location.speed*3.6] forKey:(NSString *)kCGImagePropertyGPSSpeed]; 
} 
if (self.location.course >= 0){ 
    [gps setObject:@"T" forKey:(NSString *)kCGImagePropertyGPSTrackRef]; 
    [gps setObject:[NSNumber numberWithFloat:self.location.course] forKey:(NSString *)kCGImagePropertyGPSTrack]; 
} 
CFDictionarySetValue(theDict, kCGImagePropertyGPSDictionary, (__bridge void *)gps); 
} 
#pragma mark debug 

// This is used to verify the metadata loading 

- (void)checkEXIF:(NSData *) imageData { 
CGImageSourceRef myImageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); 

CFDictionaryRef imagePropertiesDictionary; 
CFDictionaryRef tiffPropertiesDictionary; 
CFDictionaryRef exifPropertiesDictionary; 
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
         [NSNumber numberWithBool:NO], (NSString *)kCGImageSourceShouldCache, 
         nil]; 

imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(myImageSource,0, (CFDictionaryRef)options); 

/* these value are produced by the system 
*/ 
CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth); 
CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight); 

int w = 0; 
int h = 0; 

CFNumberGetValue(imageWidth, kCFNumberIntType, &w); 
CFNumberGetValue(imageHeight, kCFNumberIntType, &h); 
// NSDictionary *propertiesDict = (NSDictionary *)CFBridgingRelease(imagePropertiesDictionary); // this assignment throws an exception later in the code 

NSLog(@"Image Width: %d",w); 
NSLog(@"Image Height: %d",h); 
// NSLog(@"Image Properties: %@", propertiesDict); 

/* These values are what we added -- 
*/ 
tiffPropertiesDictionary = CFDictionaryGetValue(imagePropertiesDictionary,kCGImagePropertyTIFFDictionary); 

if (tiffPropertiesDictionary) { 
    NSString *make = (NSString *)CFDictionaryGetValue(tiffPropertiesDictionary, kCGImagePropertyTIFFMake); 
    NSString *model = (NSString *)CFDictionaryGetValue(tiffPropertiesDictionary, kCGImagePropertyTIFFModel); 
    NSString *imageDescription = (NSString *)CFDictionaryGetValue(tiffPropertiesDictionary, kCGImagePropertyTIFFImageDescription); 
    NSString *software = (NSString *)CFDictionaryGetValue(tiffPropertiesDictionary, kCGImagePropertyTIFFSoftware); 

    NSLog(@"Image Description: %@", imageDescription); 
    NSLog(@"Make: %@", make); 
    NSLog(@"Model: %@", model); 
    NSLog(@"Software: %@", software); 

} 
else { 
    NSLog(@"No TIFF dictionary"); 
} 

exifPropertiesDictionary = CFDictionaryGetValue(imagePropertiesDictionary,kCGImagePropertyExifDictionary); 

if (exifPropertiesDictionary) { 
    NSString *uniqueID = (__bridge NSString *)CFDictionaryGetValue(exifPropertiesDictionary, kCGImagePropertyExifImageUniqueID); 
    NSString *userComment = (__bridge NSString *)CFDictionaryGetValue(exifPropertiesDictionary, kCGImagePropertyExifUserComment); 

    NSLog(@"Unique ID: %@", uniqueID); 
    NSLog(@"User comment: %@", userComment); 
} 
else { 
    NSLog(@"No EXIF dictionary"); 
} 

CFRelease(imagePropertiesDictionary); 

} 

/* MAS: end 
*/