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

时间:2021-05-28 21:22:19

I am aware of how to save metadata using ALAssets. But, I want to save an image, or upload it somewhere, with exif intact. I have exif data as an NSDictionary. But how can I inject it properly into a UIImage (or probably an NSData JPEG representation)?

我知道如何使用ALAssets保存元数据。但是,我想保存一张图片,或者把它上传到某个地方,exif是完整的。我有exif数据作为一个NSDictionary。但是我如何将它正确地注入UIImage(或者NSData JPEG表示)?

5 个解决方案

#1


14  

UIImage does not contain metadata information (it is stripped). So if you want to save it without using the imagepicker method (not in camera roll):

UIImage不包含元数据信息(被删除)。如果你想保存它而不使用imagepicker方法(不是在相机滚动中):

Follow the answer here to write to a file with the metadata intact:

按照下面的答案,以完整的元数据写入文件:

Problem setting exif data for an image

问题设置一个映像的exif数据

no idea why would this be downvoted but here is the method:

不知道为什么会被否决,但方法如下:

In this case im getting the image through AVFoundation and this is what goes in the

在这种情况下,我通过AVFoundation获得图像,这是进入

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

block code:

块代码:

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

After this code you will have your image in the jpeg nsdata and the correspoding dictionary for that image in the mutable cfdictionary.

在这段代码之后,您将在jpeg nsdata中得到您的图像,并在可变的cfdictionary中为该映像创建相应的字典。

All you have to do now is:

你现在要做的就是:

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

Note how I'm not saving using the ALAssets but directly into a folder of my choice.

请注意,我不是用ALAssets来保存,而是直接保存到我选择的文件夹中。

Btw most of this code can be found in the link I posted at first.

大部分的代码都可以在我刚发布的链接中找到。

#2


15  

I am using UIImagePickerController to get the image from the camera and my flow is a bit different than the one described by Chiquis. Here it is:

我使用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];
}

And using a UIImage category to put combine the image data with its metadata:

使用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;
}

And finally, here is the method I am using to generate the needed GPS dictionary:

最后,这里是我用来生成所需的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;
}

Hope it helps someone. Thanks to Gustavo Ambrozio, Chiquis and several others SO members I was able to piece it together and use it in my project.

希望它能帮助一些人。多亏了Gustavo Ambrozio, Chiquis和其他几位成员,我才得以将它整合在一起,并在我的项目中使用。

#3


4  

There is easier way. If you need to save some exif, you can use SimpleExif pod

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

First create a ExifContainer:

首先创建一个ExifContainer:

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

and populate it with all requred data:

并使用所有请求的数据填充它:

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

Then you can add this data to image:

然后你可以将这些数据添加到图像中:

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

Then you just save this data as a JPEG

然后将这些数据保存为JPEG。

#4


0  

Here's the basics of setting Make and Model metadata on a .jpg file in Swift 3 https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7 The higher level versions, like using ExifContainer pod, did not work for me.

下面是在Swift 3 https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7中设置Make和模型元数据的基本知识。

#5


0  

I faced the same problem, now I can upload files with EXIF data, also you can compress photo if need it, this solved the issue for me:

我遇到了同样的问题,现在我可以用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 method:

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

#1


14  

UIImage does not contain metadata information (it is stripped). So if you want to save it without using the imagepicker method (not in camera roll):

UIImage不包含元数据信息(被删除)。如果你想保存它而不使用imagepicker方法(不是在相机滚动中):

Follow the answer here to write to a file with the metadata intact:

按照下面的答案,以完整的元数据写入文件:

Problem setting exif data for an image

问题设置一个映像的exif数据

no idea why would this be downvoted but here is the method:

不知道为什么会被否决,但方法如下:

In this case im getting the image through AVFoundation and this is what goes in the

在这种情况下,我通过AVFoundation获得图像,这是进入

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

block code:

块代码:

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

After this code you will have your image in the jpeg nsdata and the correspoding dictionary for that image in the mutable cfdictionary.

在这段代码之后,您将在jpeg nsdata中得到您的图像,并在可变的cfdictionary中为该映像创建相应的字典。

All you have to do now is:

你现在要做的就是:

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

Note how I'm not saving using the ALAssets but directly into a folder of my choice.

请注意,我不是用ALAssets来保存,而是直接保存到我选择的文件夹中。

Btw most of this code can be found in the link I posted at first.

大部分的代码都可以在我刚发布的链接中找到。

#2


15  

I am using UIImagePickerController to get the image from the camera and my flow is a bit different than the one described by Chiquis. Here it is:

我使用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];
}

And using a UIImage category to put combine the image data with its metadata:

使用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;
}

And finally, here is the method I am using to generate the needed GPS dictionary:

最后,这里是我用来生成所需的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;
}

Hope it helps someone. Thanks to Gustavo Ambrozio, Chiquis and several others SO members I was able to piece it together and use it in my project.

希望它能帮助一些人。多亏了Gustavo Ambrozio, Chiquis和其他几位成员,我才得以将它整合在一起,并在我的项目中使用。

#3


4  

There is easier way. If you need to save some exif, you can use SimpleExif pod

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

First create a ExifContainer:

首先创建一个ExifContainer:

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

and populate it with all requred data:

并使用所有请求的数据填充它:

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

Then you can add this data to image:

然后你可以将这些数据添加到图像中:

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

Then you just save this data as a JPEG

然后将这些数据保存为JPEG。

#4


0  

Here's the basics of setting Make and Model metadata on a .jpg file in Swift 3 https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7 The higher level versions, like using ExifContainer pod, did not work for me.

下面是在Swift 3 https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7中设置Make和模型元数据的基本知识。

#5


0  

I faced the same problem, now I can upload files with EXIF data, also you can compress photo if need it, this solved the issue for me:

我遇到了同样的问题,现在我可以用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 method:

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