修改的EXIF数据不能正确保存 [英] Modified EXIF data doesn't save properly

查看:621
本文介绍了修改的EXIF数据不能正确保存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在无数尝试和筛选每一个SO答案+谷歌的结果后,它阻碍我在iOS上使用EXIF是如此令人沮丧。

After countless attempts and sieving through every SO answer + google result, it baffles me that working with EXIF on iOS is so frustrating.

下面是工作代码

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection
    completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
    {
        NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];

        CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL);

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

        NSLog(@"original metadata Info: %@",metadata);

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

        if(!GPSDictionary)
            GPSDictionary = [[NSMutableDictionary dictionary] init];

        if(!RAWDictionary)
            RAWDictionary = [[NSMutableDictionary dictionary] init];


        [GPSDictionary setObject:@"camera coord Latitude"
                         forKey:(NSString*)kCGImagePropertyGPSLatitude];
        [GPSDictionary setObject:@"camera coord Longitude"
                         forKey:(NSString*)kCGImagePropertyGPSLongitude];
        [GPSDictionary setObject:@"camera GPS Date Stamp"
                         forKey:(NSString*)kCGImagePropertyGPSDateStamp];
        [GPSDictionary setObject:@"camera direction (heading) in degrees"
                         forKey:(NSString*)kCGImagePropertyGPSImgDirection];

        [GPSDictionary setObject:@"subject coord Latitude"
                         forKey:(NSString*)kCGImagePropertyGPSDestLatitude];
        [GPSDictionary setObject:@"subject coord Longitude"
                         forKey:(NSString*)kCGImagePropertyGPSDestLongitude];

        [EXIFDictionary setObject:@"[S.D.] kCGImagePropertyExifUserComment"
                           forKey:(NSString *)kCGImagePropertyExifUserComment];

        [EXIFDictionary setValue:@"69 m" forKey:(NSString *)kCGImagePropertyExifSubjectDistance];


        //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(@"metadataAsMutable Info: %@",metadataAsMutable);

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

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

        CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, 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, imgSource, 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:newImageData];
        NSDictionary *propDict = [testImage properties];
        NSLog(@"Properties %@", propDict);

    }];

其输出结果如下:

2012-10-12 23:17:45.415 Waypointer[3120:907] original metadata Info: {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 2448;
PixelWidth = 3264;
"{Exif}" =     {
    ApertureValue = "2.526069";
    BrightnessValue = "-4.410617";
    ColorSpace = 1;
    ComponentsConfiguration =         (
        1,
        2,
        3,
        0
    );
    ExifVersion =         (
        2,
        2,
        1
    );
    ExposureMode = 0;
    ExposureProgram = 2;
    ExposureTime = "0.06666667";
    FNumber = "2.4";
    Flash = 16;
    FlashPixVersion =         (
        1,
        0
    );
    FocalLenIn35mmFilm = 35;
    FocalLength = "4.28";
    ISOSpeedRatings =         (
        800
    );
    MeteringMode = 5;
    PixelXDimension = 3264;
    PixelYDimension = 2448;
    SceneCaptureType = 0;
    SensingMethod = 2;
    ShutterSpeedValue = "3.906905";
    SubjectArea =         (
        1631,
        1223,
        881,
        881
    );
    WhiteBalance = 0;
};
"{TIFF}" =     {
    Orientation = 1;
    ResolutionUnit = 2;
    XResolution = 72;
    YResolution = 72;
    "_YCbCrPositioning" = 1;
};
}

而且:

2012-10-12 23:17:45.421 Waypointer[3120:907] metadataAsMutable Info: {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 2448;
PixelWidth = 3264;
"{Exif}" =     {
    ApertureValue = "2.526069";
    BrightnessValue = "-4.410617";
    ColorSpace = 1;
    ComponentsConfiguration =         (
        1,
        2,
        3,
        0
    );
    ExifVersion =         (
        2,
        2,
        1
    );
    ExposureMode = 0;
    ExposureProgram = 2;
    ExposureTime = "0.06666667";
    FNumber = "2.4";
    Flash = 16;
    FlashPixVersion =         (
        1,
        0
    );
    FocalLenIn35mmFilm = 35;
    FocalLength = "4.28";
    ISOSpeedRatings =         (
        800
    );
    MeteringMode = 5;
    PixelXDimension = 3264;
    PixelYDimension = 2448;
    SceneCaptureType = 0;
    SensingMethod = 2;
    ShutterSpeedValue = "3.906905";
    SubjectArea =         (
        1631,
        1223,
        881,
        881
    );
    SubjectDistance = "69 m";
    UserComment = "[S.D.] kCGImagePropertyExifUserComment";
    WhiteBalance = 0;
};
"{GPS}" =     {
    DateStamp = "camera GPS Date Stamp";
    DestLatitude = "subject coord Latitude";
    DestLongitude = "subject coord Longitude";
    ImgDirection = "camera direction (heading) in degrees";
    Latitude = "camera coord Latitude";
    Longitude = "camera coord Longitude";
};
"{Raw}" =     {
};
"{TIFF}" =     {
    Orientation = 1;
    ResolutionUnit = 2;
    XResolution = 72;
    YResolution = 72;
    "_YCbCrPositioning" = 1;
};
}

然后,完成所有操作后:

And, after it's all done, this:

2012-10-12 23:17:47.131 Waypointer[3120:907] Properties {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 2448;
PixelWidth = 3264;
"{Exif}" =     {
    ApertureValue = "2.526069";
    BrightnessValue = "-4.410617";
    ColorSpace = 1;
    ComponentsConfiguration =         (
        0,
        0,
        0,
        1
    );
    ExifVersion =         (
        2,
        2,
        1
    );
    ExposureMode = 0;
    ExposureProgram = 2;
    ExposureTime = "0.06666667";
    FNumber = "2.4";
    Flash = 16;
    FlashPixVersion =         (
        1,
        0
    );
    FocalLenIn35mmFilm = 35;
    FocalLength = "4.28";
    ISOSpeedRatings =         (
        800
    );
    MeteringMode = 5;
    PixelXDimension = 3264;
    PixelYDimension = 2448;
    SceneCaptureType = 0;
    SensingMethod = 2;
    ShutterSpeedValue = "3.906905";
    SubjectArea =         (
        1631,
        1223,
        881,
        881
    );
    UserComment = "[S.D.] kCGImagePropertyExifUserComment";
    WhiteBalance = 0;
};
"{JFIF}" =     {
    DensityUnit = 1;
    JFIFVersion =         (
        1,
        1
    );
    XDensity = 72;
    YDensity = 72;
};
"{TIFF}" =     {
    Orientation = 1;
    ResolutionUnit = 2;
    XResolution = 72;
    YResolution = 72;
    "_YCbCrPositioning" = 1;
};
}



如示例所示,您可以看到图像的原始metaData,然后是它的最终输出。

As the example illustrates, you can see the image's original metaData, its modification, and then its final output.

最后的输出是令我烦恼的,因为无论我做什么,我都不能得到修改后的值。

The final output is what bothers me because no matter what I do, I cannot get my modified values to stick!

有没有一些非常具体的格式我错过了?
为什么iOS剥离我的修改?
我需要做什么来添加这些额外的值?

Is there some very specific format I'm missing? Why is iOS stripping my modifications? What do I need to do to add these extra values? They are listed in the .header and figured it should be easily accepted.

推荐答案

苹果的开发人员Scott回来时,事件报告并修复问题:

Scott, a developer at Apple, returned with my incident report and fixed the issue:


上面的上述代码为GPS编写字符串值
值 - 这将无效,他们必须是NS / CFNumbers (我们提取
a EXIF的浮点值)。

The previous code above is writing string values for the GPS values - this won't work, they must be NS/CFNumbers (we extract a float value for EXIF).

我将向他们的文档提交bug报告给Apple。

I'll be filing a bug report to Apple against their documentation.

虽然花了一个星期得到这个响应,我真的很感激苹果向他们的开发者提供的支持。 (感谢Scott!); - )

Although it took a week to get this response, I really do appreciate the support Apple provides to their developers. (thanks Scott!) ;-)

下面是改进的代码及其正确的输出:

Below is the improved code and its correct output:

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection
    completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
    {
        NSData *imageNSData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];

        CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge_retained CFDataRef)imageNSData, NULL);

        //get all the metadata in the image
        NSDictionary *metadata = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imgSource, 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] init];

        if(!GPSDictionary)
            GPSDictionary = [[NSMutableDictionary dictionary] init];

        if(!RAWDictionary)
            RAWDictionary = [[NSMutableDictionary dictionary] init];


        [GPSDictionary setObject:[NSNumber numberWithFloat:37.795]
                          forKey:(NSString*)kCGImagePropertyGPSLatitude];

        [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];

        [GPSDictionary setObject:[NSNumber numberWithFloat:122.410]
                          forKey:(NSString*)kCGImagePropertyGPSLongitude];

        [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];

        [GPSDictionary setObject:@"2012:10:18"
                          forKey:(NSString*)kCGImagePropertyGPSDateStamp];

        [GPSDictionary setObject:[NSNumber numberWithFloat:300]
                          forKey:(NSString*)kCGImagePropertyGPSImgDirection];

        [GPSDictionary setObject:[NSNumber numberWithFloat:37.795]
                          forKey:(NSString*)kCGImagePropertyGPSDestLatitude];

        [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSDestLatitudeRef];

        [GPSDictionary setObject:[NSNumber numberWithFloat:122.410]
                          forKey:(NSString*)kCGImagePropertyGPSDestLongitude];

        [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSDestLongitudeRef];

        [EXIFDictionary setObject:@"[S.D.] kCGImagePropertyExifUserComment"
                           forKey:(NSString *)kCGImagePropertyExifUserComment];

        [EXIFDictionary setObject:[NSNumber numberWithFloat:69.999]
                           forKey:(NSString*)kCGImagePropertyExifSubjectDistance];


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


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

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

        CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, 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, imgSource, 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:newImageData];
        NSDictionary *propDict = [testImage properties];
        NSLog(@"Final properties %@", propDict);

    }];

其输出结果如下:

Final properties info {
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 2448;
    PixelWidth = 3264;
    "{Exif}" =     {
        ApertureValue = "2.526069";
        BrightnessValue = "0.547474";
        ColorSpace = 1;
        ComponentsConfiguration =         (
            0,
            0,
            0,
            1
        );
        ExifVersion =         (
            2,
            2,
            1
        );
        ExposureMode = 0;
        ExposureProgram = 2;
        ExposureTime = "0.05";
        FNumber = "2.4";
        Flash = 16;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLenIn35mmFilm = 35;
        FocalLength = "4.28";
        ISOSpeedRatings =         (
            320
        );
        MeteringMode = 5;
        PixelXDimension = 3264;
        PixelYDimension = 2448;
        SceneCaptureType = 0;
        SensingMethod = 2;
        ShutterSpeedValue = "4.321929";
        SubjectArea =         (
            1631,
            1223,
            881,
            881
        );
        SubjectDistance = "69.999";
        UserComment = "[S.D.] kCGImagePropertyExifUserComment";
        WhiteBalance = 0;
    };
    "{GPS}" =     {
        DateStamp = "2012:10:18";
        DestLatitude = "37.795";
        DestLatitudeRef = N;
        DestLongitude = "122.41";
        DestLongitudeRef = W;
        ImgDirection = 300;
        Latitude = "37.795";
        LatitudeRef = N;
        Longitude = "122.41";
        LongitudeRef = W;
    };
    "{JFIF}" =     {
        DensityUnit = 1;
        JFIFVersion =         (
            1,
            1
        );
        XDensity = 72;
        YDensity = 72;
    };
    "{TIFF}" =     {
        Orientation = 6;
        ResolutionUnit = 2;
        XResolution = 72;
        YResolution = 72;
        "_YCbCrPositioning" = 1;
    };
}

正如你所看到的,所有的值现在都正确地嵌入到EXIF头我已经测试,这是正确地写的JPG到相机胶卷。

As you can see, all values are now properly embedded into the EXIF header and I've tested that this is correctly written with the JPG to the camera roll.

享受! :)

这篇关于修改的EXIF数据不能正确保存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆