【问题标题】:Stripping All Exif Data in Objective-C在 Objective-C 中剥离所有 Exif 数据
【发布时间】:2014-08-31 05:06:27
【问题描述】:

如何使用 Objective-c 去除 UIImage 中的所有 exif 数据?我已经能够使用以下方法获取 exif 数据:

NSData* pngData =  UIImagePNGRepresentation(image);
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);

NSDictionary* dic   =   nil;
if ( NULL == imageSource )
{
    #ifdef _DEBUG
    CGImageSourceStatus status = CGImageSourceGetStatus ( source );
    NSLog ( @"Error: file name : %@ - Status: %d", file, status );
    #endif
}
else
{
    CFDictionaryRef propertyRef = CGImageSourceCopyPropertiesAtIndex ( imageSource, 0, NULL );
    CGImageMetadataRef metadataRef = CGImageSourceCopyMetadataAtIndex ( imageSource, 0, NULL );
    // CFDictionaryRef metadataRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyExifDictionary);
    if (metadataRef) {
        NSDictionary* immutableMetadata = (NSDictionary *)metadataRef;
        if ( immutableMetadata ) {
            dic = [ NSDictionary dictionaryWithDictionary : (NSDictionary *)metadataRef ];
        }   
        CFRelease ( metadataRef );
    }
    CFRelease(imageSource);
    imageSource = nil;
}


return dic;

【问题讨论】:

    标签: objective-c uiimage exif


    【解决方案1】:

    一些想法:

    1. 通常,从NSData 或文件内容加载图像到UIImage 的过程,使用UIImagePNGRepresentation 重新提取数据并将其保存回NSData 将剥离元数据来自图像。

      不过,这种简单的技术也有其缺点。值得注意的是,将其强制为 PNG 表示的过程可能会显着影响输出 NSData 的大小。例如,如果原始图像是压缩的 JPEG(例如由相机捕获),则生成的 PNG 文件实际上可能大于原始 JPEG。

      通常我更喜欢获取原始数据(如果是 Documents 或捆绑包中的文件,则将其直接加载到 NSData;如果是从 ALAssetsLibrary 检索到的内容,我将检索 ALAsset,并且从那里,ALAssetRepresentation,然后我将使用 getBytes 来获取该原始资产的原始二进制表示)。这避免了通过UIImage 进行往返(特别是如果随后使用UIImagePNGRepresentationUIImageJEPGRepresentation)。

    2. 如果你想剥离exif数据,你可以:

      • 从原始NSData创建图像源;

      • 创建一个图像目标,使用相同的“图像数量”(几乎总是 1)和“类型”;

      • 将图像从源复制到目标时,告诉它相应的键(kCGImagePropertyExifDictionarykCGImagePropertyGPSDictionary 应设置为kCFNull),根据CGImageDestinationAddImageFromSource 文档是如何您指定如果它们碰巧出现在源中,则应在目标中删除它们)。

      因此,它可能看起来像:

      - (NSData *)dataByRemovingExif:(NSData *)data
      {
          CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
          NSMutableData *mutableData = nil;
      
          if (source) {
              CFStringRef type = CGImageSourceGetType(source);
              size_t count = CGImageSourceGetCount(source);
              mutableData = [NSMutableData data];
      
              CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)mutableData, type, count, NULL);
      
              NSDictionary *removeExifProperties = @{(id)kCGImagePropertyExifDictionary: (id)kCFNull,
                                                     (id)kCGImagePropertyGPSDictionary : (id)kCFNull};
      
              if (destination) {
                  for (size_t index = 0; index < count; index++) {
                      CGImageDestinationAddImageFromSource(destination, source, index, (__bridge CFDictionaryRef)removeExifProperties);
                  }
      
                  if (!CGImageDestinationFinalize(destination)) {
                      NSLog(@"CGImageDestinationFinalize failed");
                  }
      
                  CFRelease(destination);
              }
      
              CFRelease(source);
          }
      
          return mutableData;
      }
      

      请注意,GPS 信息在技术上不是 exif 数据,但我假设您也想删除它。如果您想保留 GPS 数据,请从我的 removeExifProperties 字典中删除 kCGImagePropertyGPSDictionary 条目。

    3. 顺便说一句,在您提取元数据的代码中,您似乎将CGImageMetadataRef 转换为NSDictionary。如果您的技术有效,那很好,但我认为CGImageMetadataRef 被认为是一种不透明的数据类型,并且确实应该使用CGImageMetadataCopyTags 来提取标签数组:

      - (NSArray *)metadataForData:(NSData *)data
      {
          NSArray *metadataArray = nil;
          CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
      
          if (source) {
              CGImageMetadataRef metadata = CGImageSourceCopyMetadataAtIndex(source, 0, NULL);
              if (metadata) {
                  metadataArray = CFBridgingRelease(CGImageMetadataCopyTags(metadata));
                  CFRelease(metadata);
              }
              CFRelease(source);
          }
      
          return metadataArray;
      }
      

      为了完整起见,在 iOS 7.0 之前的版本中,您可以从属性中提取数据:

      - (NSDictionary *)metadataForData:(NSData *)data
      {
          NSDictionary *properties = nil;
          CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
      
          if (source) {
              properties = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source, 0, NULL));
              CFRelease(source);
          }
      
          return properties;
      }
      

    【讨论】:

    • 感谢您的回答,帮助已满。但是在我使用 CGImage 删除 EXIF && GPS 后,图像数据被压缩,因为我通过“writeImageToSavedPhotosAlbum”保存 UIImage。如果我将“kCGImageDestinationLossyCompressionQuality”作为 1.0 添加到“removeExifProperties”,则输出图像会大于原始数据。您对保留图像数据为原始数据但去除 EXIF 和 GPS 等元数据有什么建议吗?
    【解决方案2】:

    根据Image I/O programming guide,可以使用CGImageDestinationSetProperties添加一个CFDictionaryRef的属性。

    他们的示例代码是:

    float compression = 1.0; // Lossless compression if available.
    int orientation = 4; // Origin is at bottom, left.
    CFStringRef myKeys[3];
    CFTypeRef   myValues[3];
    CFDictionaryRef myOptions = NULL;
    myKeys[0] = kCGImagePropertyOrientation;
    myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
    myKeys[1] = kCGImagePropertyHasAlpha;
    myValues[1] = kCFBooleanTrue;
    myKeys[2] = kCGImageDestinationLossyCompressionQuality;
    myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
    myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, 3,
                          &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    // Release the CFNumber and CFDictionary objects when you no longer need them.
    

    而且我不明白他们为什么不一直接受它。我猜接下来会发生什么:

    • 创建一个CFImageDestinationRef
    • 将您的图像复制到其中
    • 在其上设置所需的元数据

    如何做第一步在文档中不是很清楚。于是我看了CGImageDestination reference,好像可以这样搞(NOT TESTED):

    NSMutableData* mutableData = [[NSMutableData alloc] initWithCapacity:[pngData length]];
    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge_transfer CFMutableDataRef)mutableData, <# Some UTI Type #>, 1, NULL);
    CGImageDestinationAddImage(imageDestination, image, yourProperties);
    
    CGImageDestinationFinalize(imageDestination);
    

    因此,按照 Apple 在文档中显示的方式创建您的属性字典,然后创建一个图像目标并将所有内容写入其中,包括您的属性。之后您可以访问NSMutableData 对象并读取您的图像数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-24
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      • 2012-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多