How do you overwrite image metadata?
Asked Answered
R

1

9

I can't seem to get image metadata to be written to the image correctly if the key/val is already present in the original image metadata with CGImageDestination. It works just fine if it they key/val is not present in the original metadata.

It's almost as though image metadata properties in the original image take precedence over modifications. Is this some kind of byzantine formatting issue I am not aware of, where I need to populate the key/val in some unusual way, a bug, or? Anyone else seen this?

Code and output below, for both cases where it works properly (if the value is not already set) and fails to write (if the value is already set to something else).

Any help appreciated greatly appreciated.

Here is where/how I create the image NSData:

// convert the existing asset to nsdata to overwrite itself
ALAssetRepresentation* rep = [asset defaultRepresentation];
Byte* buffer               = (Byte*)malloc(rep.size);
NSUInteger buffered        = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
NSData* imageData          = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];

// write the metadata directly into the nsdata of the image itself
NSData* newImage = [self writeMetadataIntoImageData:imageData metadata:newMetadata];

Here is the actual modification of the metadata:

- (NSData*)writeMetadataIntoImageData:(NSData*)imageData metadata:(NSMutableDictionary*)metadataAsMutable
{
    // create an imagesourceref
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);

    // read and log pre write metadata
    NSDictionary* metadata = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source,0,NULL));
    NSLog(@"Before:\n------------------------------%@\n------------------------------", metadata);

    // set the new metadata keys here
    NSMutableDictionary* iptc = [metadataAsMutable[(NSString*)kCGImagePropertyIPTCDictionary] mutableCopy];
    if (!iptc)
    {
        iptc = [NSMutableDictionary dictionaryWithCapacity:1];
    }
    iptc[(NSString*)kCGImagePropertyIPTCCaptionAbstract] = @"Hardcoded Caption";
    metadataAsMutable[(NSString*)kCGImagePropertyIPTCDictionary] = iptc;

    // log the new metadata as we want it written
    NSLog(@"Parameter:\n------------------------------%@\n------------------------------", metadataAsMutable);

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

    // create a new data object and write the new image into it
    NSMutableData *dest_data = [NSMutableData data];
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data,UTI,1,NULL);
    if(!destination)
    {
        NSLog(@"Error: 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);

    BOOL success = NO;
    success = CGImageDestinationFinalize(destination);
    if(!success)
    {
        NSLog(@"Error: Could not create data from image destination");
    }

    // read and log post write metadata
    CGImageSourceRef  source2;
    source2 = CGImageSourceCreateWithData((__bridge CFDataRef) dest_data, NULL);
    NSDictionary *metadata2 = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source2,0,NULL));
    NSLog(@"After:\n------------------------------%@\n------------------------------", metadata2);

    // cleanup
    CFRelease(destination);

    // return the new data
    return dest_data;
}

Here are the NSLogs for when the image has an existing value for the key:

Before:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = Blurry;
        DateCreated = 20130923;
        DigitalCreationDate = 20130923;
        DigitalCreationTime = 173815;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 173815;
    };
    <...snip...>
}
------------------------------
Parameter:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = "Hardcoded Caption";
        DateCreated = 20130923;
        DigitalCreationDate = 20130923;
        DigitalCreationTime = 173815;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 173815;
    };
    <...snip...>
}
------------------------------
After:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = Blurry;
        DateCreated = 20130923;
        DigitalCreationDate = 20130923;
        DigitalCreationTime = 173815;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 173815;
    };
    <...snip...>
}
------------------------------

Here are the NSLogs for when the image has no value for the key:

Before:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        DateCreated = 20130925;
        DigitalCreationDate = 20130925;
        DigitalCreationTime = 192856;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 192856;
    };
    <...snip...>
}
------------------------------
Parameter:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = "Hardcoded Caption";
        DateCreated = 20130925;
        DigitalCreationDate = 20130925;
        DigitalCreationTime = 192856;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 192856;
    };
    <...snip...>
}
------------------------------
After:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = "Hardcoded Caption";
        DateCreated = 20130925;
        DigitalCreationDate = 20130925;
        DigitalCreationTime = 192856;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 192856;
    };
    <...snip...>
}
------------------------------
Ratcliff answered 26/9, 2013 at 16:44 Comment(11)
I tried CGImageDestinationCopyImageSource also, but that corrupts the JPEG datastream if you edit the same file more than once. Apple Technical Support Incident opened...Ratcliff
Hello Scott! any news on this? I have the same problem! please let me know!Asymptote
Take a look at this post: #14624884Feltner
Image/IO is weak about meta data. i suggest you should use Adobe's XMP toolkit. you can read/write XMP data to all media formats image, video, pdf etc. i tried to explain here. https://mcmap.net/q/1319829/-write-iptc-data-to-file/…Euniceeunuch
byline does not work as well.Euniceeunuch
Have you looked at this post? It might helpSibship
@secofr looks interesting, will have a look at it when I am at home. mohacs thanks but i would like to keep external toolkits as last resource if the default ones dont work at all. coder404 this applies only to reading data.. i already did that. now the problem is writing it back!Asymptote
OK guys! I found the solution! according to the IPTC documentation the description field is bound to the TIFF and EXIF address. changing the value in the TIFF it updates also the IPTC entry! since i cant give myself the vote if mohacs can post this as answer i can give the +50 instead of throwing it away! :)Asymptote
kinda cheating but wasting 50 is not correct. i hope it is not against policy of the SOEuniceeunuch
@Asymptote - I am using the ALAssetsLibrary to write an image to the photo library on my iPhone. I need to dpi to be 500 instead of the standard 72dpi. Your solution confuses me. Can I change a value to change the dpi of my image ?Roxi
This compress the image size.Whomsoever
E
3

According to the IPTC documentation the description field is bound to the TIFF and EXIF address. changing the value in the TIFF it updates also the IPTC entry! thank you user2452250 for the tip.

Euniceeunuch answered 28/5, 2014 at 17:43 Comment(11)
great! can assign the 50 in about an hour! thanks for your help and timeAsymptote
sorry for the late Bounty! forgot it.. here it is.. just in time.. :DAsymptote
@mohacs - I have followed the code in the question and it does appear that the image metadata properties in the original image take precedence over modifications. I'm trying to apply the answer from user2452250 but perhaps I'm not understanding it enough. If you would like the points I do have a question here: https://mcmap.net/q/586170/-uiimage-become-fuzzy-when-it-was-scaled-why-ios-5-0/1735836 Some idiot has more authority than intelligence and has marked it as duplicate of another question. If anything it is a duplicate of this one. Can you please help?Roxi
@Lucy, regarding your question: increasing DPI does not make image bigger. DPI is for the purpose of determining the size of the printed image nothing.Euniceeunuch
@mohacs - Let's pretend that I don't want the image bigger. Let's pretend that all I want to do is change the dpi from 72 to 500. Is there a way to do that?Roxi
@mohacs - If there is a way to programmatically change the dpi to 500 then answer my question and I'll be happy to give you the points. No resizing at all. Thank you. :-)Roxi
@Lucy I am not sure is it possible to change it without processing image. Let me try in my XMP project then I can give answer.Euniceeunuch
@mohacs - If it helps any, my coworker is doing it for Android using this method: https://mcmap.net/q/1137903/-android-how-to-get-or-set-print-dpi-dots-per-inch-of-jpeg-file-while-loading-or-saving-it-programmatically - If I have to edit the bits then I will. Call me stupid but I thought perhaps there was a better way in iOS.Roxi
@Lucy ok, I made it, I can change the DPI values in a image metadata however as I mention before it is just meta data without processing image, it is ok right? It is kinda faking right? Your question is closed I can't add answer.Euniceeunuch
I'll get somebody with enough points to open it and get back with you.Roxi
Let us continue this discussion in chat.Euniceeunuch

© 2022 - 2024 — McMap. All rights reserved.