问题设置图像的exif数据

我在iOS 4.1中使用了新的ImageIO框架。 我使用以下方法成功检索了exif元数据:

CFDictionaryRef metadataDict = CMGetAttachment(sampleBuffer, kCGImagePropertyExifDictionary , NULL); 

读出来,看起来有效。 保存图像的作品,但从来没有任何exif数据的图像。

  CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef) docurl, kUTTypeJPEG, 1, NULL); // Add the image to the destination using previously saved options. CGImageDestinationAddImage(myImageDest, iref, NULL); //add back exif NSDictionary *props = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithFloat:.1], kCGImageDestinationLossyCompressionQuality, metadataDict, kCGImagePropertyExifDictionary, //the exif metadata nil]; //kCGImagePropertyExifAuxDictionary CGImageDestinationSetProperties(myImageDest, (CFDictionaryRef) props); // Finalize the image destination. bool status = CGImageDestinationFinalize(myImageDest); 

下面的博客文章是我的答案,当我有修改和保存Exif数据咖啡因cocoa的问题。 这适用于iOS。

这是我写的Exif和GPS数据的testing代码。 它几乎是从上面的博客的代码的复制和粘贴。 我正在使用它来写入exif数据到捕获的图像。

 NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer] ; CGImageSourceRef source ; source = CGImageSourceCreateWithData((CFDataRef)jpeg, NULL); //get all the metadata in the image NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL); //make the metadata dictionary mutable so we can add properties to it NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease]; [metadata release]; NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease]; NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]autorelease]; if(!EXIFDictionary) { //if the image does not have an EXIF dictionary (not all images do), then create one for us to use EXIFDictionary = [NSMutableDictionary dictionary]; } if(!GPSDictionary) { GPSDictionary = [NSMutableDictionary dictionary]; } //Setup GPS dict [GPSDictionary setValue:[NSNumber numberWithFloat:_lat] forKey:(NSString*)kCGImagePropertyGPSLatitude]; [GPSDictionary setValue:[NSNumber numberWithFloat:_lon] forKey:(NSString*)kCGImagePropertyGPSLongitude]; [GPSDictionary setValue:lat_ref forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; [GPSDictionary setValue:lon_ref forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; [GPSDictionary setValue:[NSNumber numberWithFloat:_alt] forKey:(NSString*)kCGImagePropertyGPSAltitude]; [GPSDictionary setValue:[NSNumber numberWithShort:alt_ref] forKey:(NSString*)kCGImagePropertyGPSAltitudeRef]; [GPSDictionary setValue:[NSNumber numberWithFloat:_heading] forKey:(NSString*)kCGImagePropertyGPSImgDirection]; [GPSDictionary setValue:[NSString stringWithFormat:@"%c",_headingRef] forKey:(NSString*)kCGImagePropertyGPSImgDirectionRef]; [EXIFDictionary setValue:xml forKey:(NSString *)kCGImagePropertyExifUserComment]; //add our modified EXIF data back into the image's metadata [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (eg, public.jpeg) //this will be the data CGImageDestinationRef will write into NSMutableData *dest_data = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)dest_data,UTI,1,NULL); if(!destination) { NSLog(@"***Could not create image destination ***"); } //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) metadataAsMutable); //tell the destination to write the image data and metadata into our data object. //It will return false if something goes wrong BOOL success = NO; success = CGImageDestinationFinalize(destination); if(!success) { NSLog(@"***Could not create data from image destination ***"); } //now we have the data ready to go, so do whatever you want with it //here we just write it to disk at the same path we were passed [dest_data writeToFile:file atomically:YES]; //cleanup CFRelease(destination); CFRelease(source); 

我试过史蒂夫的答案,它的工作原理,但我认为这是大的图像缓慢,因为它是复制整个图像。

您可以使用CMSetAttachments直接在CMSampleBuffer上设置属性。 只要在调用jpegStillImageNSDataRepresentation之前做到这jpegStillImageNSDataRepresentation

 CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate); CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict); NSMutableDictionary * mutableGPS = [self getGPSDictionaryForLocation:self.myLocation]; CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, mutableGPS); // set the dictionary back to the buffer CMSetAttachments(imageSampleBuffer, mutable, kCMAttachmentMode_ShouldPropagate); 

getGPSDictionaryForLocation方法:可以在这里find:

在iOS4.1上使用照片保存地理标签信息

我创build了一个MSMutableDictionary类别来帮助将地理标签和其他元数据保存到图像中。 看看我的博客文章:

http://blog.codecropper.com/2011/05/adding-metadata-to-ios-images-the-easy-way/

一件作品涉及为EXIF数据创buildGPS元数据字典。 这是一个CLLocation类别来做到这一点:

https://gist.github.com/phildow/6043486

[由于没有解释而降低评论,重新回顾这个答案。]

苹果已经更新了他们的文章来解决这个问题(技术问答QA1622)。 如果您使用的是旧版本的Xcode,那么您可能仍然会有这样的文章,或多或less地说,运气不好,如果没有对图像数据进行低级parsing,就无法做到这一点。

更新版本在这里:

https://developer.apple.com/library/ios/#qa/qa1622/_index.html

我修改了代码,如下所示:

 - (void) saveImage:(UIImage *)imageToSave withInfo:(NSDictionary *)info { // Get the image metadata (EXIF & TIFF) NSMutableDictionary * imageMetadata = [[info objectForKey:UIImagePickerControllerMediaMetadata] mutableCopy]; // add (fake) GPS data CLLocationCoordinate2D coordSF = CLLocationCoordinate2DMake(37.732711,-122.45224); // arbitrary altitude and accuracy double altitudeSF = 15.0; double accuracyHorizontal = 1.0; double accuracyVertical = 1.0; NSDate * nowDate = [NSDate date]; // create CLLocation for image CLLocation * loc = [[CLLocation alloc] initWithCoordinate:coordSF altitude:altitudeSF horizontalAccuracy:accuracyHorizontal verticalAccuracy:accuracyVertical timestamp:nowDate]; // this is in case we try to acquire actual location instead of faking it with the code right above if ( loc ) { [imageMetadata setObject:[self gpsDictionaryForLocation:loc] forKey:(NSString*)kCGImagePropertyGPSDictionary]; } // Get the assets library ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; // create a completion block for when we process the image ALAssetsLibraryWriteImageCompletionBlock imageWriteCompletionBlock = ^(NSURL *newURL, NSError *error) { if (error) { NSLog( @"Error writing image with metadata to Photo Library: %@", error ); } else { NSLog( @"Wrote image %@ with metadata %@ to Photo Library",newURL,imageMetadata); } }; // Save the new image to the Camera Roll, using the completion block defined just above [library writeImageToSavedPhotosAlbum:[imageToSave CGImage] metadata:imageMetadata completionBlock:imageWriteCompletionBlock]; } 

我从此打电话

 imagePickerController:didFinishPickingMediaWithInfo: 

这是图像select器的代表方法。 (这是我把逻辑看看是否有图像保存等)

为了完整起见,下面是将GPS数据作为字典获取的辅助方法:

 - (NSDictionary *) gpsDictionaryForLocation:(CLLocation *)location { CLLocationDegrees exifLatitude = location.coordinate.latitude; CLLocationDegrees exifLongitude = location.coordinate.longitude; NSString * latRef; NSString * longRef; if (exifLatitude < 0.0) { exifLatitude = exifLatitude * -1.0f; latRef = @"S"; } else { latRef = @"N"; } if (exifLongitude < 0.0) { exifLongitude = exifLongitude * -1.0f; longRef = @"W"; } else { longRef = @"E"; } NSMutableDictionary *locDict = [[NSMutableDictionary alloc] init]; // requires ImageIO [locDict setObject:location.timestamp forKey:(NSString*)kCGImagePropertyGPSTimeStamp]; [locDict setObject:latRef forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; [locDict setObject:[NSNumber numberWithFloat:exifLatitude] forKey:(NSString *)kCGImagePropertyGPSLatitude]; [locDict setObject:longRef forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; [locDict setObject:[NSNumber numberWithFloat:exifLongitude] forKey:(NSString *)kCGImagePropertyGPSLongitude]; [locDict setObject:[NSNumber numberWithFloat:location.horizontalAccuracy] forKey:(NSString*)kCGImagePropertyGPSDOP]; [locDict setObject:[NSNumber numberWithFloat:location.altitude] forKey:(NSString*)kCGImagePropertyGPSAltitude]; return locDict; } 

请参阅在iPhone的照片库中将UIImage与元数据(EXIF,GPS,TIFF)一起写入

由于我还在学习,所以花了我一些时间,把史蒂夫和马蒂的答案拼凑在一起,为数据添加元数据,然后保存。 下面,我做了一个不使用ImageIO方法的答案的Swift实现。

给定一个CMSampleBuffer样本缓冲区buffer ,一些CLLocation location ,并使用Morty的build议来使用CMSetAttachments以避免重复的图像,我们可以做到以下几点。 可以在这里find扩展CLLocation的gpsMetadata方法(也是Swift)。

 if let location = location { // Get the existing metadata dictionary (if there is one) var metaDict = CMCopyDictionaryOfAttachments(nil, buffer, kCMAttachmentMode_ShouldPropagate) as? Dictionary<String, Any> ?? [:] // Append the GPS metadata to the existing metadata metaDict[kCGImagePropertyGPSDictionary as String] = location.gpsMetadata() // Save the new metadata back to the buffer without duplicating any data CMSetAttachments(buffer, metaDict as CFDictionary, kCMAttachmentMode_ShouldPropagate) } // Get JPG image Data from the buffer guard let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer) else { // There was a problem; handle it here } 

此时,您可以将图像数据写入文件或使用Photos API将图像保存到相机胶卷(使用PHAssetCreationRequest's addResource:with:data:options for iOS 9+,或者通过编写imageData到一个临时文件,然后调用PHAssetChangeRequest.creationRequestForAssetFromImage:atFileURL )。 请注意,iOS 9不推荐使用ALAssertLibrary。我在这里提供了更多的实现细节。