4

I can not understand why metaDic is always null. There is a code.

    CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(img.CGImage)); //(UIImage *img)
    CGImageSourceRef mySourceRef =  CGImageSourceCreateWithData(dataRef, NULL);
    NSDictionary *metaDic = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(mySourceRef,0,NULL);
    NSDictionary *tiffDic = (NSDictionary *)[metaDic objectForKey:(NSString *)kCGImagePropertyTIFFDictionary];
    NSString *AuthorName  =  [tiffDic objectForKey:(NSString *)kCGImagePropertyTIFFArtist];

I did some variants of getting picture. And here what I have discovered:

One way of getting picture with its info - I need to get it from site and there what I've got:

              //  NSURL *UrlPath  - path of picture    image.jpg   from web site
            NSData *dataImg = [NSData dataWithContentsOfURL:UrlPath];

            CGImageSourceRef mySource =  CGImageSourceCreateWithData((CFDataRef)dataImg, NULL); 
            NSDictionary *metaDic = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(mySource,0,NULL);
            NSDictionary *tiffDic = [metaDic objectForKey:(NSString *)kCGImagePropertyTIFFDictionary];

            /// Log of tiffDic    
tiffDic = {
Artist =(
  "mr. Smith"
  );
}

another way - read picture from NSBoudle mainBundle:

           // NSURL *NSBundleUrl  -  - path of the same  picture    image.jpg   from [[NSBundle mainBundle] 
            CGImageSourceRef mySource = CGImageSourceCreateWithURL( (CFURLRef) NSBundleUrl, NULL);
            NSDictionary *metaDic = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(mySource,0,NULL);
            NSDictionary *tiffDic = [metaDic objectForKey:(NSString *)kCGImagePropertyTIFFDictionary];

/// Log of tiffDic
tiffDic = {
    Artist = "mr. Smith"; 
}

why it get braces as array for name of artist when the picture data come from web site?

Green Ree
  • 97
  • 9
  • Where is the original image taken from? – David Doyle Apr 19 '13 at 16:05
  • You can send `URLByAppendingPathComponent:` to a URL (no need to go to and from a string), and you can create a CGImageSource directly from a URL—no need to read the whole file in yourself first. What do you mean by “AuthorV … could not be read”? – Peter Hosey Apr 20 '13 at 17:51
  • [tiffDic objectForKey:(NSString *)kCGImagePropertyTIFFArtist] return nil. – Green Ree Apr 20 '13 at 17:53
  • @GreenRee: If you open the image in Preview, and open the Inspector and switch to the TIFF tab, does it have an artist property there? – Peter Hosey Apr 20 '13 at 18:42
  • yes, it has an artist. NSlog of tiffDic shows the author too. – Green Ree Apr 20 '13 at 18:46
  • @GreenRee: Artist or author? It would help if you would edit your question to include the entirety of `metaDic`. – Peter Hosey Apr 20 '13 at 19:01
  • nslog of tiffDic : { Artist = "mr. Smith"; } – Green Ree Apr 20 '13 at 19:31
  • @GreenRee: Please show the entire `metaDic`, not just `tiffDic`. (And please edit it into your question, as it will probably be too large to fit comfortably into a comment.) – Peter Hosey Apr 20 '13 at 19:35
  • Interesting. So how did you determine that `AuthorV` is `nil`? It should certainly be `@"mr. Smith"` given the above dictionary. – Peter Hosey Apr 20 '13 at 20:17
  • Thats the point! have no idea why – Green Ree Apr 20 '13 at 20:24
  • So, again, how did you determine that `AuthorV` is `nil`? Please edit your question to show the code you used to examine `AuthorV`. – Peter Hosey Apr 20 '13 at 21:59
  • “why it get braces as array for name of artist when the picture data come from web site?” What happens if you use `CGImageSourceCreateWithURL` for both URLs? – Peter Hosey Apr 22 '13 at 03:45
  • when I use CGImageSourceCreateWithURL for web url. it returns nil, but it works with CGImageSourceCreateWithData – Green Ree Apr 22 '13 at 09:19

2 Answers2

7

Your data path looks something like this:

UIImage -> CGImage -> CGDataProvider -> CGImageSource

It's that third step that is cleansing your image of metadata. CGDataProviders are an "older" mechanism for getting data into Quartz with "limited functionality" - meaning - amongst other things - they do not support metadata.

Try something like this instead:

NSData* jpegData = UIImageJPEGRepresentation(image,1.0);
CFDataRef dataRef = (__bridge CFDataRef)jpegData;
CGImageSourceRef source = CGImageSourceCreateWithData(dataRef, NULL);

Data path:

UIImage -> NS/CFData -> CGImageSource

That will preserve the metadata.

You may not have much luck getting the authorName this way if you use the UIImage as your starting point. UIImage strips out a lot of the metadata that may accompany the original image source (the TiffDict seems to get stripped down to just the orientation tag). You really want to be reading the 'uninterpreted' data from it's source and extracting metadata without reading the image data (that is one of the benefits of using a CGImageSourceRef).

I have a little test project up on github that compares methods of extracting image metadata from various sources - filesystem, network URL, asset library, camera, maybe you should take a look.

update As Peter points out (and my project shows) - you shouldn't be using the UIImage, but the original data source. As in your case it is a filesystem source, something like this:

 NSString* path = @"/path/to/resource";
 NSData *data = [NSData dataWithContentsOfFile:path];
 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);

Or better still (as Peter points out again!) you can use CGImageSourceCreateWithURL and skip that NSData step altogether.

foundry
  • 31,615
  • 9
  • 90
  • 125
  • Creating a JPEG from the original image is another form of data loss—namely, lossy compression. Creating a PNG would avoid that, but there's already PNG or JPEG data in the original file, so why not just use that? – Peter Hosey Apr 20 '13 at 17:43
  • @PeterHosey - that's what my github example is designed to demonstrate. _If_ your aim is to extract metadata _and_ you start with UIImage, then it's a tossup between `UIImageJPEGRepresentation` and `UIImagePNGRepresentation` (the actual image quality being irrelevant) ... but the point is to use neither, but to go back to the original file as you say. This both from an image quality AND a metadata quality point of view. – foundry Apr 20 '13 at 17:47
  • @PeterHosey, thanks for your various comments, I have updated my github demo to reflect them. – foundry Apr 20 '13 at 18:41
  • @PeterHosey - actually there _is_ a difference between metadata extracted via `UIImageJPEGRepresentation` vs `UIImagePNGRepresentation` - the PNG version returns a much smaller subset of the original metadata... but - as you point out - this is a moot point when you can get the full metadata from the original file. – foundry Apr 20 '13 at 19:04
1

The data provider of a CGImage provides raw pixels, not PNG or TIFF or other external-format data with metadata included. Consequently, there are no properties to get.

I wouldn't be surprised if that source can't even give you an image, since it has no way of knowing what pixel format to interpret the data in.

You need to create the image source with the URL or data you got the original image from, not the pixel data of that image. Ideally, you should create the image source first, and then create the image and its properties from the image source.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • I take the image from Document folder. Init it with imageWithContentsOfFile – Green Ree Apr 20 '13 at 10:02
  • @GreenRee: I think you meant to post that comment on your question, in reply to David Doyle, not on my answer. – Peter Hosey Apr 20 '13 at 17:07
  • @GreenRee: The solution remains to create the image source from the original image file (the one in the Documents folder), not from a UIImage or CGImage that you created in between. – Peter Hosey Apr 20 '13 at 17:44