首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >修改的EXIF数据未正确保存

修改的EXIF数据未正确保存
EN

Stack Overflow用户
提问于 2012-10-13 11:36:29
回答 3查看 5.9K关注 0票数 9

经过无数次的尝试和筛选每一个SO答案+谷歌结果,这让我困惑,在iOS上使用EXIF是如此令人沮丧。

下面是工作代码及其结果。

代码语言:javascript
运行
复制
[[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);

    }];

它输出以下内容:

代码语言:javascript
运行
复制
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;
};
}

还有这个:

代码语言:javascript
运行
复制
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;
};
}

然后,在所有操作完成后,如下所示:

代码语言:javascript
运行
复制
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、其修改,然后是其最终输出。

最后的输出是困扰我的,因为无论我做什么,我都不能让我的修改后的值保持不变!

我是不是遗漏了什么特别的格式?为什么iOS要剥离我的修改?我需要做什么来添加这些额外的值?它们被列在.header中,并认为它应该很容易被接受。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-10-20 07:27:15

Scott,苹果的一名开发人员,带着我的事件报告回来了,并解决了这个问题:

上面的代码是为GPS值写入字符串值-这是行不通的,它们必须是NS/CFNumbers (我们为EXIF提取浮点值)。

我会针对他们的文档向Apple提交一份bug报告。

虽然花了一周时间才收到回复,但我真的很感谢苹果为他们的开发者提供的支持。(谢谢斯科特!) ;-)

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

代码语言:javascript
运行
复制
[[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);

    }];

它输出以下内容:

代码语言:javascript
运行
复制
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正确地写入到相机胶卷中。

尽情享受!:)

票数 11
EN

Stack Overflow用户

发布于 2016-12-08 23:37:10

在Swift 3中,我无法让CGImageDestinationAddImageFromSource使用我的元数据字典。这是一个低级的Swift版本,看起来效果很好:https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7

票数 1
EN

Stack Overflow用户

发布于 2020-03-01 05:43:39

我也有写exif数据的问题,我的问题是写kCGImagePropertyExifDateTimeOriginal。它需要是一种特定的格式。

代码语言:javascript
运行
复制
    dfExif.dateFormat = "yyyy:MM:dd HH:mm:ss"
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12869706

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档