【问题标题】:Get Exif data from UIImage - UIImagePickerController [duplicate]从 UIImage 获取 Exif 数据 - UIImagePickerController [重复]
【发布时间】:2012-04-03 17:10:17
【问题描述】:

我们如何从 UIImagePickerController 中选择的 UIImage 中获取 Exif 信息?

我为此做了很多研发,得到了很多回复,但还是没能实现。

我浏览过这个thisthis 链接

请帮我解决这个问题。

提前谢谢..

【问题讨论】:

标签: iphone objective-c ipad exif


【解决方案1】:

有趣的问题!我想出了以下解决方案,适用于从您的照片库中挑选的图像(注意我的代码使用 ARC):

导入AssetsLibrary.frameworkImageIO.framework

然后在 .h 文件中包含所需的类:

#import <AssetsLibrary/ALAsset.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <ImageIO/CGImageSource.h>
#import <ImageIO/CGImageProperties.h>

并将其放入您的 imagePickerController:didFinishPickingMediaWithInfo: 委托方法中:

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL]
    resultBlock:^(ALAsset *asset) {

        ALAssetRepresentation *image_representation = [asset defaultRepresentation];

        // create a buffer to hold image data 
        uint8_t *buffer = (Byte*)malloc(image_representation.size);
        NSUInteger length = [image_representation getBytes:buffer fromOffset: 0.0  length:image_representation.size error:nil];

        if (length != 0)  {

            // buffer -> NSData object; free buffer afterwards
            NSData *adata = [[NSData alloc] initWithBytesNoCopy:buffer length:image_representation.size freeWhenDone:YES];

            // identify image type (jpeg, png, RAW file, ...) using UTI hint
            NSDictionary* sourceOptionsDict = [NSDictionary dictionaryWithObjectsAndKeys:(id)[image_representation UTI] ,kCGImageSourceTypeIdentifierHint,nil];

            // create CGImageSource with NSData
            CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef) adata,  (__bridge CFDictionaryRef) sourceOptionsDict);

            // get imagePropertiesDictionary
            CFDictionaryRef imagePropertiesDictionary;
            imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(sourceRef,0, NULL);

            // get exif data
            CFDictionaryRef exif = (CFDictionaryRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyExifDictionary);
            NSDictionary *exif_dict = (__bridge NSDictionary*)exif;
            NSLog(@"exif_dict: %@",exif_dict);

            // save image WITH meta data
            NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
            NSURL *fileURL = nil;
            CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, imagePropertiesDictionary);

            if (![[sourceOptionsDict objectForKey:@"kCGImageSourceTypeIdentifierHint"] isEqualToString:@"public.tiff"])
                     {
                         fileURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@.%@",
                                                           documentsDirectory,
                                                           @"myimage",
                                                           [[[sourceOptionsDict objectForKey:@"kCGImageSourceTypeIdentifierHint"] componentsSeparatedByString:@"."] objectAtIndex:1]
                                                           ]];

                         CGImageDestinationRef dr = CGImageDestinationCreateWithURL ((__bridge CFURLRef)fileURL,
                                                                                     (__bridge CFStringRef)[sourceOptionsDict objectForKey:@"kCGImageSourceTypeIdentifierHint"],
                                                                                     1,
                                                                                     NULL
                                                                                    );
              CGImageDestinationAddImage(dr, imageRef, imagePropertiesDictionary);
              CGImageDestinationFinalize(dr);
              CFRelease(dr);
            }
            else
            {
              NSLog(@"no valid kCGImageSourceTypeIdentifierHint found …");
            }

            // clean up
            CFRelease(imageRef);
            CFRelease(imagePropertiesDictionary);
            CFRelease(sourceRef);
        }
        else {
            NSLog(@"image_representation buffer length == 0");
        }
    }
    failureBlock:^(NSError *error) {
        NSLog(@"couldn't get asset: %@", error);
    }
];

我注意到的一件事是,iOS 会要求用户允许定位服务——如果他拒绝,您将无法获取图像数据……

编辑

添加了保存图像的代码,包括其元数据。这是一种快速的方法,所以也许有更好的方法,但它确实有效!

【讨论】:

  • 你能告诉我在获取exif数据后如何保存图像吗?
  • 请告诉我你的代码中的“__bridge”是什么? :-)
  • @Marvin 我正在使用 ARC。 __bridge 是一个充满关键字的手,它告诉 ARC 对象的所有权,以便它可以正确地清理它们。最简单的情况是__bridge 强制转换,ARC 不会为此做任何额外的工作(它假设您自己处理对象的内存)。
  • buffer 的内存是否已释放?怀疑没有……?
  • 注意:你还需要包含#import
【解决方案2】:

这些答案似乎都极其复杂。如果图像已保存到相机胶卷,并且您拥有 ALAsset(来自 UIImagePicker 或 ALAssetLibrary),则可以像这样获取元数据:

asset.defaultRepresentation.metadata;

如果您想将该图像从相机胶卷保存到另一个位置(例如在沙盒/文档中),只需执行以下操作:

CGImageDestinationRef imageDestinationRef   = CGImageDestinationCreateWithURL((__bridge CFURLRef)urlToSaveTo, kUTTypeJPEG, 1, NULL);
CFDictionaryRef imagePropertiesRef          = (__bridge CFDictionaryRef)asset.defaultRepresentation.metadata;

CGImageDestinationAddImage(imageDestinationRef, asset.defaultRepresentation.fullResolutionImage, imagePropertiesRef);
if (!CGImageDestinationFinalize(imageDestinationRef)) NSLog(@"Failed to copy photo on save to %@", urlToSaveTo);

CFRelease(imageDestinationRef);

【讨论】:

  • 这比 2012 年的答案要好得多。
  • 它也比其他方式返回更多信息:它们不会返回存储在 {TIFF} 中的信息,如型号、品牌、版权、艺术家等。这应该被标记为答案!
【解决方案3】:

我已经找到解决方案并从here得到答复

从这里我们也可以获取 GPS 信息..

太棒了,感谢大家帮助我解决这个问题。

更新

这是我自己创建的另一个函数,也返回 Exif 数据和 GPS 数据,在这个函数中我们不需要任何第三方库..但是你必须为此打开位置服务。并为此使用当前的纬度和经度。所以必须使用CoreLocation.framework

//FOR CAMERA IMAGE

-(NSMutableData *)getImageWithMetaData:(UIImage *)pImage
{
    NSData* pngData =  UIImagePNGRepresentation(pImage);

    CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);
    NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);

    NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease];
    [metadata release];

    //For GPS Dictionary
    NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]autorelease];
    if(!GPSDictionary) 
        GPSDictionary = [NSMutableDictionary dictionary];

    [GPSDictionary setValue:[NSNumber numberWithDouble:currentLatitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
    [GPSDictionary setValue:[NSNumber numberWithDouble:currentLongitude] forKey:(NSString*)kCGImagePropertyGPSLongitude];

    NSString* ref;
    if (currentLatitude <0.0)
        ref = @"S"; 
    else
        ref =@"N";  
    [GPSDictionary setValue:ref forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];

    if (currentLongitude <0.0)
        ref = @"W"; 
    else
        ref =@"E";  
    [GPSDictionary setValue:ref forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];

    [GPSDictionary setValue:[NSNumber numberWithFloat:location.altitude] forKey:(NSString*)kCGImagePropertyGPSAltitude];

    //For EXIF Dictionary
    NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease];
    if(!EXIFDictionary) 
        EXIFDictionary = [NSMutableDictionary dictionary];

    [EXIFDictionary setObject:[NSDate date] forKey:(NSString*)kCGImagePropertyExifDateTimeOriginal];
    [EXIFDictionary setObject:[NSDate date] forKey:(NSString*)kCGImagePropertyExifDateTimeDigitized];

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

    NSMutableData *dest_data = [NSMutableData data];
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)dest_data, UTI, 1, NULL);

    if(!destination)
        dest_data = [[pngData mutableCopy] autorelease];
    else 
    {
        CGImageDestinationAddImageFromSource(destination, source, 0, (CFDictionaryRef) metadataAsMutable);
        BOOL success = CGImageDestinationFinalize(destination);
        if(!success)
            dest_data = [[pngData mutableCopy] autorelease];
    }

    if(destination)
        CFRelease(destination);

    CFRelease(source);

    return dest_data;
}

//FOR PHOTO LIBRARY IMAGE

-(NSMutableData *)getImagedataPhotoLibrary:(NSDictionary *)pImgDictionary andImage:(UIImage *)pImage
{
    NSData* data = UIImagePNGRepresentation(pImage);

    CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
    NSMutableDictionary *metadataAsMutable = [[pImgDictionary mutableCopy]autorelease];

    CFStringRef UTI = CGImageSourceGetType(source);

    NSMutableData *dest_data = [NSMutableData data];

    //For Mutabledata
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)dest_data, UTI, 1, NULL);

    if(!destination)
        dest_data = [[data mutableCopy] autorelease];
    else 
    {
        CGImageDestinationAddImageFromSource(destination, source, 0, (CFDictionaryRef) metadataAsMutable);
        BOOL success = CGImageDestinationFinalize(destination);
        if(!success)
            dest_data = [[data mutableCopy] autorelease];
    }
    if(destination)
        CFRelease(destination);

    CFRelease(source);

    return dest_data;
}

我们会像这样检索这些数据

//FOR CAMERA IMAGE
NSData *originalImgData = [self getImageWithMetaData:imgOriginal];

//FOR PHOTO LIBRARY IMAGE
[self getImagedataPhotoLibrary:[[myasset defaultRepresentation] metadata] andImage:imgOriginal];

对于所有这些,您必须导入 AssetsLibrary.frameworkImageIO.framework

【讨论】:

  • currentLatitude 来自哪里?您是在调用 UIImagePicker 之前从标准 CoreLocation 调用中设置它还是直接从返回的图像中读取它?
  • CoreLoation 在调用 UIImagePickerController 之前调用
  • 啊,那可能不准确。您调用 CoreLocation,调用 UIImagePicker,然后在拍摄图像之前将设备物理移动一段距离。您现在将获得不准确的纬度和经度。德拉特...
  • 但是您也可以将距离过滤器设置为 5 米,因此如果使用移动,它将自动获取其当前位置..
  • 您实际上并没有从图像中获取 EXIF 数据,而是在创建 EXIF 并将其添加到图像中。这实际上并不能回答问题。
【解决方案4】:

我已经使用这种方法从图像中获取 exifdata 字典,我希望这也对你有用

-(void)getExifDataFromImage:(UIImage *)currentImage
{

    NSData* pngData =  UIImageJPEGRepresentation(currentImage, 1.0);

    CGImageSourceRef mySourceRef = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);

    //CGImageSourceRef mySourceRef = CGImageSourceCreateWithURL((__bridge CFURLRef)myURL, NULL);
    if (mySourceRef != NULL)
    {
        NSDictionary *myMetadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(mySourceRef,0,NULL);
        NSDictionary *exifDic = [myMetadata objectForKey:(NSString *)kCGImagePropertyExifDictionary];
        NSDictionary *tiffDic = [myMetadata objectForKey:(NSString *)kCGImagePropertyTIFFDictionary];
        NSLog(@"exifDic properties: %@", myMetadata); //all data
        float rawShutterSpeed = [[exifDic objectForKey:(NSString *)kCGImagePropertyExifExposureTime] floatValue];
        int decShutterSpeed = (1 / rawShutterSpeed);
        NSLog(@"Camera %@",[tiffDic objectForKey:(NSString *)kCGImagePropertyTIFFModel]);
        NSLog(@"Focal Length %@mm",[exifDic objectForKey:(NSString *)kCGImagePropertyExifFocalLength]);
        NSLog(@"Shutter Speed %@", [NSString stringWithFormat:@"1/%d", decShutterSpeed]);
        NSLog(@"Aperture f/%@",[exifDic objectForKey:(NSString *)kCGImagePropertyExifFNumber]);


        NSNumber *ExifISOSpeed  = [[exifDic objectForKey:(NSString*)kCGImagePropertyExifISOSpeedRatings] objectAtIndex:0];
        NSLog(@"ISO %ld",[ExifISOSpeed integerValue]);
        NSLog(@"Taken %@",[exifDic objectForKey:(NSString*)kCGImagePropertyExifDateTimeDigitized]);


    }

}

【讨论】:

    【解决方案5】:

    您需要 ALAssetsLibrary 才能从图像中实际检索 EXIF 信息。仅当将 EXIF 保存到照片库时,才会将 EXIF 添加到图像中。即使您使用 ALAssetLibrary 从库中获取图像资源,如果您将其设置为 UIImage,它也会丢失所有 EXIF 信息。

    【讨论】:

      【解决方案6】:

      我已尝试按照 Mehul 的建议将 GPS 坐标插入 iPad 相机拾取的图像元数据中。 成功了,谢谢你的帖子。

      附: 谁打算使用该代码,只需替换函数顶部的两个地理位置 -(NSMutableData *)getImageWithMetaData:(UIImage *)pImage {

      double currentLatitude = [locationManager location].coordinate.latitude;
      double currentLongitude = [locationManager location].coordinate.longitude;
      

      ...

      假设您已经在代码中的某个位置初始化了 locationManager,如下所示:

          locationManager = [[CLLocationManager alloc] init];
          [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
          [locationManager setDelegate:self]; // Not necessary in this case
          [locationManager startUpdatingLocation]; // Not neccessary in this case
      

      并通过将 CoreLocation/CoreLocation.h 和 ImageIO/ImageIO.h 标头与相关框架一起导入。

      【讨论】:

      • 这不是回答问题,它是题外话
      • 跑题了。他没有问如何获取位置而是他需要将位置作为 UIImage 的元数据
      猜你喜欢
      • 2016-01-10
      • 1970-01-01
      • 2010-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多