如何将exif元数据写入图像(不是相机胶卷,只是UIImage或JPEG)

我知道如何使用ALAssets保存元数据。 但是,我想保存一个图像,或者将其上传到exif,并保持原样。 我有EXIF数据作为NSDictionary。 但是,我怎样才能正确地注入到UIImage(或可能是一个NSData JPEG表示)?

UIImage不包含元数据信息(被剥离)。 所以,如果你想保存它,而不使用imagepicker方法(不在相机胶卷):

按照这里的答案写入元数据完整的文件:

问题设置图像的exif数据

不知道为什么会这样downvoted,但这里是方法:

在这种情况下即时通过AVFoundation得到的形象,这是怎么回事

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { // code here } 

块代码:

  CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate); CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict); // 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(loc.coordinate.latitude)], kCGImagePropertyGPSLatitude , ((loc.coordinate.latitude >= 0) ? @"N" : @"S"), kCGImagePropertyGPSLatitudeRef , [NSNumber numberWithFloat:fabs(loc.coordinate.longitude)], kCGImagePropertyGPSLongitude , ((loc.coordinate.longitude >= 0) ? @"E" : @"W"), kCGImagePropertyGPSLongitudeRef , [formatter stringFromDate:[loc timestamp]], kCGImagePropertyGPSTimeStamp , [NSNumber numberWithFloat:fabs(loc.altitude)], kCGImagePropertyGPSAltitude , nil]; // The gps info goes into the gps metadata part CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, (__bridge void *)gpsDict); // Here just as an example im adding the attitude matrix in the exif comment metadata CMRotationMatrix m = att.rotationMatrix; GLKMatrix4 attMat = GLKMatrix4Make(m.m11, m.m12, m.m13, 0, m.m21, m.m22, m.m23, 0, m.m31, m.m32, m.m33, 0, 0, 0, 0, 1); NSMutableDictionary *EXIFDictionary = (__bridge NSMutableDictionary*)CFDictionaryGetValue(mutable, kCGImagePropertyExifDictionary); [EXIFDictionary setValue:NSStringFromGLKMatrix4(attMat) forKey:(NSString *)kCGImagePropertyExifUserComment]; CFDictionarySetValue(mutable, kCGImagePropertyExifDictionary, (__bridge void *)EXIFDictionary); NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer] ; 

在此代码之后,您将在jpeg nsdata中拥有您的图像,并在可变的cfdictionary中为该图像创build相应的字典。

你现在要做的是:

  CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL); CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (eg, public.jpeg) NSMutableData *dest_data = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge 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) mutable); //tell the destination to write the image data and metadata into our data object. //It will return false if something goes wrong BOOL 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 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"ImagesFolder"]; NSError *error; if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath]) [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder // NSString *imageName = @"ImageName"; NSString *fullPath = [dataPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.jpg", name]]; //add our image to the path [dest_data writeToFile:fullPath atomically:YES]; //cleanup CFRelease(destination); CFRelease(source); 

请注意,我如何不使用ALAssets进行保存,而是直接保存到我select的文件夹中。

顺便说一句,这个代码大部分可以在我发布的链接中find。

我正在使用UIImagePickerController从相机获取图像,而且我的stream程与Chiquis描述的stream程略有不同。 这里是:

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *image = info[@"UIImagePickerControllerOriginalImage"]; NSString *fullPhotoFilename = ...; // generate the photo name and path here NSData *photoData = [UIImage taggedImageData:image.jpegData metadata:info[@"UIImagePickerControllerMediaMetadata"] orientation:image.imageOrientation]; [photoData writeToFile:fullPhotoFilename atomically:YES]; } 

使用UIImage类别将图像数据与其元数据结合起来:

 #import <ImageIO/ImageIO.h> #import "UIImage+Tagging.h" #import "LocationHelper.h" @implementation UIImage (Tagging) + (NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSMutableDictionary *)metadata { // create an imagesourceref CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL); // this is the type of image (eg, public.jpeg) CFStringRef UTI = CGImageSourceGetType(source); // create a new data object and write the new image into it NSMutableData *dest_data = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data, UTI, 1, NULL); if (!destination) { NSLog(@"Error: 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, (__bridge CFDictionaryRef) metadata); BOOL success = NO; success = CGImageDestinationFinalize(destination); if (!success) { NSLog(@"Error: Could not create data from image destination"); } CFRelease(destination); CFRelease(source); return dest_data; } + (NSData *)taggedImageData:(NSData *)imageData metadata:(NSDictionary *)metadata orientation:(UIImageOrientation)orientation { CLLocationManager *locationManager = [CLLocationManager new]; CLLocation *location = [locationManager location]; NSMutableDictionary *newMetadata = [NSMutableDictionary dictionaryWithDictionary:metadata]; if (!newMetadata[(NSString *)kCGImagePropertyGPSDictionary] && location) { newMetadata[(NSString *)kCGImagePropertyGPSDictionary] = [LocationHelper gpsDictionaryForLocation:location]; } // Reference: http://sylvana.net/jpegcrop/exif_orientation.html int newOrientation; switch (orientation) { case UIImageOrientationUp: newOrientation = 1; break; case UIImageOrientationDown: newOrientation = 3; break; case UIImageOrientationLeft: newOrientation = 8; break; case UIImageOrientationRight: newOrientation = 6; break; case UIImageOrientationUpMirrored: newOrientation = 2; break; case UIImageOrientationDownMirrored: newOrientation = 4; break; case UIImageOrientationLeftMirrored: newOrientation = 5; break; case UIImageOrientationRightMirrored: newOrientation = 7; break; default: newOrientation = -1; } if (newOrientation != -1) { newMetadata[(NSString *)kCGImagePropertyOrientation] = @(newOrientation); } NSData *newImageData = [self writeMetadataIntoImageData:imageData metadata:newMetadata]; return newImageData; } 

最后,这里是我用来生成所需的GPS字典的方法:

 + (NSDictionary *)gpsDictionaryForLocation:(CLLocation *)location { NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setTimeZone:timeZone]; [formatter setDateFormat:@"HH:mm:ss.SS"]; NSDictionary *gpsDict = @{(NSString *)kCGImagePropertyGPSLatitude: @(fabs(location.coordinate.latitude)), (NSString *)kCGImagePropertyGPSLatitudeRef: ((location.coordinate.latitude >= 0) ? @"N" : @"S"), (NSString *)kCGImagePropertyGPSLongitude: @(fabs(location.coordinate.longitude)), (NSString *)kCGImagePropertyGPSLongitudeRef: ((location.coordinate.longitude >= 0) ? @"E" : @"W"), (NSString *)kCGImagePropertyGPSTimeStamp: [formatter stringFromDate:[location timestamp]], (NSString *)kCGImagePropertyGPSAltitude: @(fabs(location.altitude)), }; return gpsDict; } 

希望它可以帮助别人。 感谢Gustavo Ambrozio,Chiquis和其他几位SO成员,我能够将它们拼凑在一起并用于我的项目。

有更简单的方法。 如果您需要保存一些exif,可以使用SimpleExif窗格

首先创build一个ExifContainer:

 ExifContainer *container = [[ExifContainer alloc] init]; 

并填写所有需要的数据:

 [container addUserComment:@"A long time ago, in a galaxy far, far away"]; [container addCreationDate:[NSDate dateWithTimeIntervalSinceNow:-10000000]]; [container addLocation:locations[0]]; 

那么你可以将这个数据添加到图像:

 NSData *imageData = [[UIImage imageNamed:@"DemoImage"] addExif:container]; 

然后,您只需将这些数据保存为JPEG

以下是在Swift 3中设置.jpg.jpg文件的基础知识。

我面临同样的问题,现在我可以上传EXIF数据文件,也可以压缩照片,如果需要的话,这解决了我的问题:

 // Get your image. UIImage *loImgPhoto = [self getImageFromAsset:loPHAsset]; // Get your metadata (includes the EXIF data). CGImageSourceRef loImageOriginalSource = CGImageSourceCreateWithData(( CFDataRef) loDataFotoOriginal, NULL); NSDictionary *loDicMetadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(loImageOriginalSource, 0, NULL); // Set your compression quality (0.0 to 1.0). NSMutableDictionary *loDicMutableMetadata = [loDicMetadata mutableCopy]; [loDicMutableMetadata setObject:@(lfCompressionQualityValue) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality]; // Create an image destination. NSMutableData *loNewImageDataWithExif = [NSMutableData data]; CGImageDestinationRef loImgDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)loNewImageDataWithExif, CGImageSourceGetType(loImageOriginalSource), 1, NULL); // Add your image to the destination. CGImageDestinationAddImage(loImgDestination, loImgPhoto.CGImage, (__bridge CFDictionaryRef) loDicMutableMetadata); // Finalize the destination. if (CGImageDestinationFinalize(loImgDestination)) { NSLog(@"Successful image creation."); // process the image rendering, adjustment data creation and finalize the asset edit. //Upload photo with EXIF metadata [self myUploadMethod:loNewImageDataWithExif]; } else { NSLog(@"Error -> failed to finalize the image."); } CFRelease(loImageOriginalSource); CFRelease(loImgDestination); 

getImageFromAsset方法:

 -(UIImage *)getImageFromAsset:(PHAsset *)aPHAsset { __block UIImage *limgImageResult; PHImageRequestOptions *lPHImageRequestOptions = [PHImageRequestOptions new]; lPHImageRequestOptions.synchronous = YES; [self.imageManager requestImageForAsset:aPHAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault//PHImageContentModeAspectFit options:lPHImageRequestOptions resultHandler:^(UIImage *limgImage, NSDictionary *info) { limgImageResult = limgImage; }]; return limgImageResult; }