【问题标题】:Save CUSTOM metadata in an image taken from AVFoundation in iOS将 CUSTOM 元数据保存在从 iOS 中的 AVFoundation 获取的图像中
【发布时间】:2012-01-20 07:14:33
【问题描述】:

当我使用AVFoundation framework 获取图像时,如何在图像中保存自定义元数据?

我知道我可以访问属性天气,我的图像为UIImageCIImage,但这些属性似乎彼此不同(即使是相同的图像)。

到目前为止,我是这样访问字典的:(代码取自 Caffeinated Cocoa 博客)

NSLog(@"about to request a capture from: %@", [self stillImageOutput]);
    [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) 
    {

NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer] ;

        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge_retained  CFDataRef)jpeg, NULL);

        //get all the metadata in the image
        NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);

        //make the metadata dictionary mutable so we can add properties to it
        NSMutableDictionary *metadataAsMutable = [metadata mutableCopy];

        NSMutableDictionary *EXIFDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy];
        NSMutableDictionary *GPSDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy];
        NSMutableDictionary *RAWDictionary = [[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyRawDictionary]mutableCopy];

        if(!EXIFDictionary) {
            EXIFDictionary = [NSMutableDictionary dictionary];
        }
        if(!GPSDictionary) {
            GPSDictionary = [NSMutableDictionary dictionary];
        }
        if(!RAWDictionary) {
            RAWDictionary = [NSMutableDictionary dictionary];
        }

        float _lat = gpsInfo.coordinate.latitude;
        float _lon = gpsInfo.coordinate.longitude;
        float _alt = gpsInfo.altitude;
        NSDate *_date = gpsInfo.timestamp;
        float _heading = gpsInfo.course;;

        [GPSDictionary setValue:[NSNumber numberWithFloat:_lat] 
                         forKey:(NSString*)kCGImagePropertyGPSLatitude];
        [GPSDictionary setValue:[NSNumber numberWithFloat:_lon] 
                         forKey:(NSString*)kCGImagePropertyGPSLongitude];
        [GPSDictionary setValue:[NSNumber numberWithFloat:_alt] 
                         forKey:(NSString*)kCGImagePropertyGPSAltitude];
        [GPSDictionary setValue:_date 
                         forKey:(NSString*)kCGImagePropertyGPSDateStamp];
        [GPSDictionary setValue:[NSNumber numberWithFloat:_heading] 
                         forKey:(NSString*)kCGImagePropertyGPSImgDirection];


        [EXIFDictionary setValue:@"Wasup" forKey:(NSString *)kCGImagePropertyExifUserComment];

        [RAWDictionary setValue:attitude forKey:@"Attitude"];

        //Add the modified Data back into the image’s metadata
        [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
        [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
        [metadataAsMutable setObject:RAWDictionary forKey:(NSString *)kCGImagePropertyRawDictionary];

        NSLog(@"Info: %@",metadataAsMutable);

        CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)

        //this will be the data CGImageDestinationRef will write into
        NSMutableData *data = [NSMutableData data];

        CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)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, (__bridge CFDictionaryRef) metadataAsMutable);

        //tell the destination to write the image data and metadata into our data object.
        //It will return false if something goes wrong
        BOOL success = NO;
        success = CGImageDestinationFinalize(destination);

        if(!success) {
            NSLog(@"***Could not create data from image destination ***");
        }

我可以像这样保存我的图像:

CIImage *testImage = [CIImage imageWithData:data];

        NSDictionary *propDict = [testImage properties];


        NSLog(@"Properties %@",propDict);

或者像这样

UIImage *imageMod = [[UIImage alloc] initWithData:data];


CGImageSourceRef source2 = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);

        //get all the metadata in the image
NSDictionary *metadata2 = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source2,0,NULL);

NSLog(@"WASD: %@",metadata2);

但是这两个选项都不会返回我的自定义原始数据字典。

总而言之,我想做的是将自己的对象保存在我自己创建的字段中(在本例中为 cmattitude 对象)

PD:当我在将字典与图像合并之前NSLOG 时,它确实拥有我想要的所有文件。

Info: {
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 1080;
    PixelWidth = 1920;
    "{Exif}" =     {
        ApertureValue = "2.526069";
        BrightnessValue = "4.013361";
        ColorSpace = 1;
        ComponentsConfiguration =         (
            1,
            2,
            3,
            0
        );
        ExifVersion =         (
            2,
            2,
            1
        );
        ExposureMode = 0;
        ExposureProgram = 2;
        ExposureTime = "0.03333334";
        FNumber = "2.4";
        Flash = 16;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLenIn35mmFilm = 35;
        FocalLength = "4.28";
        ISOSpeedRatings =         (
            80
        );
        MeteringMode = 5;
        PixelXDimension = 1920;
        PixelYDimension = 1080;
        SceneCaptureType = 0;
        SensingMethod = 2;
        Sharpness = 0;
        ShutterSpeedValue = "4.906905";
        SubjectArea =         (
            959,
            539,
            518,
            388
        );
        UserComment = "Wazup";
        WhiteBalance = 0;
    };
    "{GPS}" =     {
        Altitude = 102;
        DateStamp = "2012-01-20 06:55:48 +0000";
        ImgDirection = "-1";
        Latitude = "35.02496";
        Longitude = "135.754";
    };
    "{Raw}" =     {
        Attitude = "Pitch: 50.913760, Roll: 36.342350, Yaw: -164.272361 @ 0.048291\n";
    };
    "{TIFF}" =     {
        Orientation = 6;
        ResolutionUnit = 2;
        XResolution = 72;
        YResolution = 72;
        "_YCbCrPositioning" = 1;
    };

但是当我从新图像中对字典进行 NSLOG 时,它会为我提供 CIIMage 的这个:

Properties {
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 1080;
    PixelWidth = 1920;
    "{Exif}" =     {
        ApertureValue = "2.526069";
        BrightnessValue = "4.013361";
        ColorSpace = 1;
        ComponentsConfiguration =         (
            0,
            0,
            0,
            1
        );
        ExifVersion =         (
            2,
            2,
            1
        );
        ExposureMode = 0;
        ExposureProgram = 2;
        ExposureTime = "0.03333334";
        FNumber = "2.4";
        Flash = 16;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLenIn35mmFilm = 35;
        FocalLength = "4.28";
        ISOSpeedRatings =         (
            80
        );
        MeteringMode = 5;
        PixelXDimension = 1920;
        PixelYDimension = 1080;
        SceneCaptureType = 0;
        SensingMethod = 2;
        Sharpness = 0;
        ShutterSpeedValue = "4.906905";
        SubjectArea =         (
            959,
            539,
            518,
            388
        );
        UserComment = "Wazup";
        WhiteBalance = 0;
    };
    "{GPS}" =     {
        Altitude = 102;
        ImgDirection = 0;
        Latitude = "35.025";
        Longitude = "135.754";
    };
    "{JFIF}" =     {
        DensityUnit = 1;
        JFIFVersion =         (
            1,
            1
        );
        XDensity = 72;
        YDensity = 72;
    };
    "{TIFF}" =     {
        Orientation = 6;
        ResolutionUnit = 2;
        XResolution = 72;
        YResolution = 72;
        "_YCbCrPositioning" = 1;
    };

如果我 NSLog UIImage 它给我的信息更少。

谢谢!

【问题讨论】:

  • 首先,您的代码在变量“destination”保存的位置生成富含元数据的图像数据,但随后您尝试使用变量“data”保存它。难怪它不起作用!
  • 最近我正在处理图像,花了很多时间尝试使用 Apple 的框架获得好的结果。但是,即使您设法更改元数据,您也需要重新处理图像本身,我的意思是您必须创建您正在处理的图像的副本。即使它看起来相同的文件大小变化等。经过一点点搜索替代原生框架后,我找到了 Adob​​e 的 XMP 工具包。不仅是图像文件,您还可以使用 XMP 工具包更改几乎所有媒体文件中的元数据。它可以读取/写入/更改所有元数据名称空间。
  • @BenjaminWheeler no... CGImageDestinationCreateWithData “创建一个写入 Core Foundation 可变数据对象的图像目标”,因此通过将 NSMutableData 引用传递给它,我将直接写入“数据”。

标签: ios iphone objective-c xcode4.2


【解决方案1】:

UIImageCIImage 不包含所有元数据。事实上,将它从NSData 转换为这两种格式中的任何一种都会去掉很多元数据,所以恕我直言,UIImageCIImage 应该只在您计划在 UI 中显示或传递时使用CoreImage.

您可以像这样读取图像属性:

- (NSDictionary *)getImageProperties:(NSData *)imageData {
    // get the original image metadata
    CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
    CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL);
    NSDictionary *props = (__bridge_transfer NSDictionary *) properties;
    CFRelease(imageSourceRef);

    return props;
}

然后您可以将您的NSDictionary 转换为NSMutableDictionary

NSDictionary *props = [self getImageProperties:imageData];
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:props];

之后,添加您想要的任何字段。注意EXIF数据是通过kCGImagePropertyExifDictionary抓取得到的,GPS数据是通过kCGImagePropertyGPSDictionary抓取的,以此类推:

NSMutableDictionary *exifDictionary = properties[(__bridge NSString *) kCGImagePropertyExifDictionary];

查看CGImageProperties 头文件中所有可用的键。

好的,现在您已经对元数据进行了所需的任何更改,将其添加回来需要几个步骤:

// incoming image data
NSData *imageData;

// create the image ref
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef) imageData);
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);

// the updated properties from above
NSMutableDictionary *mutableDictionary;

// create the new output data
CFMutableDataRef newImageData = CFDataCreateMutable(NULL, 0);
// my code assumes JPEG type since the input is from the iOS device camera
CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef) @"image/jpg", kUTTypeImage);
// create the destination
CGImageDestinationRef destination = CGImageDestinationCreateWithData(newImageData, type, 1, NULL);
// add the image to the destination
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) mutableDictionary);
// finalize the write
CGImageDestinationFinalize(destination);

// memory cleanup
CGDataProviderRelease(imgDataProvider);
CGImageRelease(imageRef);
CFRelease(type);
CFRelease(destination);

// your new image data
NSData *newImage = (__bridge_transfer NSData *)newImageData;

你有它。

【讨论】:

  • 嘿!不错的代码在这里!但是,@mikeho,我有一个问题: newImage 存储在哪里?我相信我必须自己用[newImage writeToFile:@"/Users/nikola/Desktop/newImage.jpg" atomically:YES]; 存储它,但如果我通过记录该文件的属性来检查它,它没有我写过的新密钥......
  • @NikolaMarkovic 嘿,对不起。还没来得及回应。是的,您必须按照您的方式编写它。但是你在看哪些属性?您需要一个 EXIF 查看器来查看数据(或将数据加载到 Photoshop 中,它应该会显示给您)。
  • 对不起,这个答案迟到了。我将新图像保存在桌面上,但它的 EXIF 保持不变。我不明白为什么。它的行为就像是受保护/不可变的,但是我不知道为什么。
  • @NikolaMarkovic 老实说,我不确定 :( 我在手机上试过这个,下载图像或在 Image Capture 中查看它可以正确显示数据。可能是 OSX 的行为不同。跨度>
  • 必须是..我目前没有从事那个项目,但一旦我回到它并找到答案,我会告诉你的。还是谢谢!
猜你喜欢
  • 2013-04-05
  • 1970-01-01
  • 2012-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多