我知道如何使用ALAssets保存元数据。但是,我想保存一个图像,或者将其上传到某处,exif完好无损。我有exif数据作为NSDictionary。但是如何将其正确地注入UIImage(或者可能是NSData JPEG表示)?
答案 0 :(得分:17)
我正在使用UIImagePickerController从相机中获取图像,我的流程与Chiquis描述的流程略有不同。这是:
- (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 (e.g., 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成员,我能够把它拼凑起来并在我的项目中使用它。
答案 1 :(得分:15)
UIImage不包含元数据信息(它被剥离)。因此,如果您想在不使用imagepicker方法的情况下保存它(不在相机胶卷中):
按照此处的答案写入包含完整元数据的文件:
Problem setting exif data for an image
不知道为什么这会被贬低,但这是方法:
在这种情况下我通过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中获取该图像的对应字典。
现在你所要做的就是:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL);
CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., 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保存,而是直接进入我选择的文件夹。
顺便说一句,大部分代码都可以在我最初发布的链接中找到。
答案 2 :(得分:4)
有更简单的方法。如果您需要保存一些exif,可以使用SimpleExif pod
首先创建一个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
答案 3 :(得分:0)
以下是在Swift 3中.jpg
文件上设置制作和模型元数据的基础知识https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7更高级别的版本,如使用ExifContainer pod,对我来说不起作用。