剥离Objective-C中的所有Exif数据

时间:2014-08-31 05:06:27

标签: objective-c uiimage exif

如何使用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;

2 个答案:

答案 0 :(得分:14)

有几点想法:

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

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

    通常我更喜欢获取原始数据(如果文档或包中的文件直接加载到NSData;如果是从ALAssetsLibrary检索的内容,我会检索{ {1}},从那里ALAsset,然后我使用ALAssetRepresentation来获取该原始资产的原始二进制表示形式。这样可以避免通过getBytes进行往返(特别是如果随后使用UIImageUIImagePNGRepresentation)。

  2. 如果要删除exif数据,可以:

    • 从原始UIImageJEPGRepresentation;

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

    • 将图像从源复制到目标时,请告诉它相应的键(NSDatakCGImagePropertyExifDictionary应设置为kCGImagePropertyGPSDictionary),根据kCFNull文档是指定如果它们恰好出现在源中,应在目标中删除的文档。

    因此,它可能看起来像:

    CGImageDestinationAddImageFromSource

    请注意,GPS信息在技术上并不是exif数据,但我认为您也想删除它。如果您想保留GPS数据,请从我的- (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; } 词典中删除kCGImagePropertyGPSDictionary条目。

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

    CGImageMetadataCopyTags

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

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

答案 1 :(得分: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,看起来可以这样做(未经测试):

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对象并读取您的图像数据。