Setting exif data parameters for an image

I am using the new ImageIO environment in iOS 4.1. I am successfully extracting exif metadata using the following:

CFDictionaryRef metadataDict = CMGetAttachment(sampleBuffer, kCGImagePropertyExifDictionary , NULL); 

After reading this, he looks valid. The image is being saved, but there is never any exif data on the image.

  CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef) docurl, kUTTypeJPEG, 1, NULL); // Add the image to the destination using previously saved options. CGImageDestinationAddImage(myImageDest, iref, NULL); //add back exif NSDictionary *props = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithFloat:.1], kCGImageDestinationLossyCompressionQuality, metadataDict, kCGImagePropertyExifDictionary, //the exif metadata nil]; //kCGImagePropertyExifAuxDictionary CGImageDestinationSetProperties(myImageDest, (CFDictionaryRef) props); // Finalize the image destination. bool status = CGImageDestinationFinalize(myImageDest); 
+54
iphone core-graphics exif macos javax.imageio
Feb 26 '11 at 5:45
source share
8 answers

The following blog post, where I received my answer, when I had problems changing and saving Exif Caffeinated Cocoa data. This works on iOS.

Here is my test code for writing Exif and GPS data. This is pretty much copy and paste of code from the above blog. I use this to write exif data to a captured image.

 NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer] ; CGImageSourceRef source ; source = CGImageSourceCreateWithData((CFDataRef)jpeg, NULL); //get all the metadata in the image NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL); //make the metadata dictionary mutable so we can add properties to it NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease]; [metadata release]; NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease]; NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]autorelease]; if(!EXIFDictionary) { //if the image does not have an EXIF dictionary (not all images do), then create one for us to use EXIFDictionary = [NSMutableDictionary dictionary]; } if(!GPSDictionary) { GPSDictionary = [NSMutableDictionary dictionary]; } //Setup GPS dict [GPSDictionary setValue:[NSNumber numberWithFloat:_lat] forKey:(NSString*)kCGImagePropertyGPSLatitude]; [GPSDictionary setValue:[NSNumber numberWithFloat:_lon] forKey:(NSString*)kCGImagePropertyGPSLongitude]; [GPSDictionary setValue:lat_ref forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; [GPSDictionary setValue:lon_ref forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; [GPSDictionary setValue:[NSNumber numberWithFloat:_alt] forKey:(NSString*)kCGImagePropertyGPSAltitude]; [GPSDictionary setValue:[NSNumber numberWithShort:alt_ref] forKey:(NSString*)kCGImagePropertyGPSAltitudeRef]; [GPSDictionary setValue:[NSNumber numberWithFloat:_heading] forKey:(NSString*)kCGImagePropertyGPSImgDirection]; [GPSDictionary setValue:[NSString stringWithFormat:@"%c",_headingRef] forKey:(NSString*)kCGImagePropertyGPSImgDirectionRef]; [EXIFDictionary setValue:xml forKey:(NSString *)kCGImagePropertyExifUserComment]; //add our modified EXIF data back into the image's metadata [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (eg, public.jpeg) //this will be the data CGImageDestinationRef will write into NSMutableData *dest_data = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)dest_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, (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 ***"); } //now we have the data ready to go, so do whatever you want with it //here we just write it to disk at the same path we were passed [dest_data writeToFile:file atomically:YES]; //cleanup CFRelease(destination); CFRelease(source); 
+80
Mar 14 2018-11-11T00:
source share

I tried to answer Steve and it works, but I think it is slow for large images because it duplicates the whole image.

You can set properties directly on a CMSampleBuffer using CMSetAttachments. Just do it before calling jpegStillImageNSDataRepresentation

 CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate); CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict); NSMutableDictionary * mutableGPS = [self getGPSDictionaryForLocation:self.myLocation]; CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, mutableGPS); // set the dictionary back to the buffer CMSetAttachments(imageSampleBuffer, mutable, kCMAttachmentMode_ShouldPropagate); 

And the getGPSDictionaryForLocation: method can be found here:

Saving Geotag photo information on iOS4.1

+14
May 11 '11 at 16:09
source share

I created the MSMutableDictionary category to help save the geotag and other metadata for the image. Check out my blog here:

http://blog.codecropper.com/2011/05/adding-metadata-to-ios-images-the-easy-way/

+3
May 14 '11 at 22:44
source share

Part includes creating a GPS metadata dictionary for EXIF ​​data. Here's the CLLocation category to do just that:

https://gist.github.com/phildow/6043486

+1
Jul 20 '13 at 1:45
source share

Since I was still studying, it took me a while to put together the answers of Steve and Marty to add metadata to the data for the image and subsequently save it. Below I have fulfilled My answers that do not use ImageIO methods.

Given the CMSampleBuffer buffer , some CLLocation location and using Mortys to use CMSetAttachments to avoid image duplication, we can do the following. The gpsMetadata method extending CLLocation can be found here (also Swift).

 if let location = location { // Get the existing metadata dictionary (if there is one) var metaDict = CMCopyDictionaryOfAttachments(nil, buffer, kCMAttachmentMode_ShouldPropagate) as? Dictionary<String, Any> ?? [:] // Append the GPS metadata to the existing metadata metaDict[kCGImagePropertyGPSDictionary as String] = location.gpsMetadata() // Save the new metadata back to the buffer without duplicating any data CMSetAttachments(buffer, metaDict as CFDictionary, kCMAttachmentMode_ShouldPropagate) } // Get JPG image Data from the buffer guard let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer) else { // There was a problem; handle it here } 

At this point, you can write image data to a file or use the photo API to save the image to Camera Roll (using PHAssetCreationRequest addResource:with:data:options for iOS 9+ or for older versions of iOS, writing imageData to a temporary file, and then calling PHAssetChangeRequest.creationRequestForAssetFromImage:atFileURL ). Note that ALAssertLibrary is deprecated for iOS 9. I provide more implementation details in the answer here .

+1
Apr 12 '17 at 18:17
source share

SWIFT 5:

It took me a week to assemble all the parts into working code.

This will save a temporary UIImage to JPEG file with GPS metadata:

 let image:UIImage = mImageView.image! // your UIImage // create filename let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy.MM.dd-HH.mm.ss" let now = Date() let date_time = dateFormatter.string(from: now) let fileName:String = "your_image_"+date_time+".jpg" // name your file the way you want let temporaryFolder:URL = FileManager.default.temporaryDirectory let temporaryFileURL:URL = temporaryFolder.appendingPathComponent(fileName) // save the image to chosen path let jpeg = image.jpegData(compressionQuality: 0.85)! // set JPG quality here (1.0 is best) let src = CGImageSourceCreateWithData(jpeg as CFData, nil)! let uti = CGImageSourceGetType(src)! let cfPath = CFURLCreateWithFileSystemPath(nil, temporaryFileURL.path as CFString, CFURLPathStyle.cfurlposixPathStyle, false) let dest = CGImageDestinationCreateWithURL(cfPath!, uti, 1, nil) // create GPS metadata from current location let gpsMeta = gCurrentLocation?.exifMetadata() // gCurrentLocation is your CLLocation (exifMetadata is an extension) let tiffProperties = [ kCGImagePropertyTIFFMake as String: "Camera vendor", kCGImagePropertyTIFFModel as String: "Camera model" // --(insert other properties here if required)-- ] as CFDictionary let properties = [ kCGImagePropertyTIFFDictionary as String: tiffProperties, kCGImagePropertyGPSDictionary: gpsMeta as Any // --(insert other dictionaries here if required)-- ] as CFDictionary CGImageDestinationAddImageFromSource(dest!, src, 0, properties) if (CGImageDestinationFinalize(dest!)) { print("Saved image with metadata!") } else { print("Error saving image with metadata") } 

And here is the GPS metadata extension (from https://gist.github.com/chefren/8b50652d67c397a825619f83c8dba6d3 ):

 import Foundation import CoreLocation extension CLLocation { func exifMetadata(heading:CLHeading? = nil) -> NSMutableDictionary { let GPSMetadata = NSMutableDictionary() let altitudeRef = Int(self.altitude < 0.0 ? 1 : 0) let latitudeRef = self.coordinate.latitude < 0.0 ? "S" : "N" let longitudeRef = self.coordinate.longitude < 0.0 ? "W" : "E" // GPS metadata GPSMetadata[(kCGImagePropertyGPSLatitude as String)] = abs(self.coordinate.latitude) GPSMetadata[(kCGImagePropertyGPSLongitude as String)] = abs(self.coordinate.longitude) GPSMetadata[(kCGImagePropertyGPSLatitudeRef as String)] = latitudeRef GPSMetadata[(kCGImagePropertyGPSLongitudeRef as String)] = longitudeRef GPSMetadata[(kCGImagePropertyGPSAltitude as String)] = Int(abs(self.altitude)) GPSMetadata[(kCGImagePropertyGPSAltitudeRef as String)] = altitudeRef GPSMetadata[(kCGImagePropertyGPSTimeStamp as String)] = self.timestamp.isoTime() GPSMetadata[(kCGImagePropertyGPSDateStamp as String)] = self.timestamp.isoDate() GPSMetadata[(kCGImagePropertyGPSVersion as String)] = "2.2.0.0" if let heading = heading { GPSMetadata[(kCGImagePropertyGPSImgDirection as String)] = heading.trueHeading GPSMetadata[(kCGImagePropertyGPSImgDirectionRef as String)] = "T" } return GPSMetadata } } extension Date { func isoDate() -> String { let f = DateFormatter() f.timeZone = TimeZone(abbreviation: "UTC") f.dateFormat = "yyyy:MM:dd" return f.string(from: self) } func isoTime() -> String { let f = DateFormatter() f.timeZone = TimeZone(abbreviation: "UTC") f.dateFormat = "HH:mm:ss.SSSSSS" return f.string(from: self) } } 

It!

Now you can use activityViewController to save a temporary image (using temporaryFileURL ) to a photo album, or save it as a file, or share it with other applications or in any other way.

+1
Jun 17 '19 at 15:35
source share

[Revision of this answer due to downvotes without explanation.]

Apple updated its article on this issue (Technical Q & A QA1622). If you are using an older version of Xcode, you can still have an article that says more or less complicated luck, you cannot do this without a low-level analysis of image data.

The updated version is here:

https://developer.apple.com/library/ios/#qa/qa1622/_index.html

I adapted the code as follows:

 - (void) saveImage:(UIImage *)imageToSave withInfo:(NSDictionary *)info { // Get the image metadata (EXIF & TIFF) NSMutableDictionary * imageMetadata = [[info objectForKey:UIImagePickerControllerMediaMetadata] mutableCopy]; // add (fake) GPS data CLLocationCoordinate2D coordSF = CLLocationCoordinate2DMake(37.732711,-122.45224); // arbitrary altitude and accuracy double altitudeSF = 15.0; double accuracyHorizontal = 1.0; double accuracyVertical = 1.0; NSDate * nowDate = [NSDate date]; // create CLLocation for image CLLocation * loc = [[CLLocation alloc] initWithCoordinate:coordSF altitude:altitudeSF horizontalAccuracy:accuracyHorizontal verticalAccuracy:accuracyVertical timestamp:nowDate]; // this is in case we try to acquire actual location instead of faking it with the code right above if ( loc ) { [imageMetadata setObject:[self gpsDictionaryForLocation:loc] forKey:(NSString*)kCGImagePropertyGPSDictionary]; } // Get the assets library ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; // create a completion block for when we process the image ALAssetsLibraryWriteImageCompletionBlock imageWriteCompletionBlock = ^(NSURL *newURL, NSError *error) { if (error) { NSLog( @"Error writing image with metadata to Photo Library: %@", error ); } else { NSLog( @"Wrote image %@ with metadata %@ to Photo Library",newURL,imageMetadata); } }; // Save the new image to the Camera Roll, using the completion block defined just above [library writeImageToSavedPhotosAlbum:[imageToSave CGImage] metadata:imageMetadata completionBlock:imageWriteCompletionBlock]; } 

and I call it from

 imagePickerController:didFinishPickingMediaWithInfo: 

which is the delegate method for the image picker. (Where I set the logic to see if there is an image to save, etc.)

For completeness, here is a helper method for getting GPS data as a dictionary:

 - (NSDictionary *) gpsDictionaryForLocation:(CLLocation *)location { CLLocationDegrees exifLatitude = location.coordinate.latitude; CLLocationDegrees exifLongitude = location.coordinate.longitude; NSString * latRef; NSString * longRef; if (exifLatitude < 0.0) { exifLatitude = exifLatitude * -1.0f; latRef = @"S"; } else { latRef = @"N"; } if (exifLongitude < 0.0) { exifLongitude = exifLongitude * -1.0f; longRef = @"W"; } else { longRef = @"E"; } NSMutableDictionary *locDict = [[NSMutableDictionary alloc] init]; // requires ImageIO [locDict setObject:location.timestamp forKey:(NSString*)kCGImagePropertyGPSTimeStamp]; [locDict setObject:latRef forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; [locDict setObject:[NSNumber numberWithFloat:exifLatitude] forKey:(NSString *)kCGImagePropertyGPSLatitude]; [locDict setObject:longRef forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; [locDict setObject:[NSNumber numberWithFloat:exifLongitude] forKey:(NSString *)kCGImagePropertyGPSLongitude]; [locDict setObject:[NSNumber numberWithFloat:location.horizontalAccuracy] forKey:(NSString*)kCGImagePropertyGPSDOP]; [locDict setObject:[NSNumber numberWithFloat:location.altitude] forKey:(NSString*)kCGImagePropertyGPSAltitude]; return locDict; } 

See also Record UIImage along with metadata (EXIF, GPS, TIFF) in the iPhone photo library

0
Jun 14
source share

Thus, I set the EXIF ​​data, also you can compress the photo, if necessary, this solved the problem for me: I hope it helps

 // Get your image. NSURL *url = @"http://somewebsite.com/path/to/some/image.jpg"; UIImage *loImgPhoto = [NSData dataWithContentsOfURL:url]; // Get your metadata (includes the EXIF data). CGImageSourceRef loImageOriginalSource = CGImageSourceCreateWithData(( CFDataRef) loDataFotoOriginal, NULL); NSDictionary *loDicMetadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(loImageOriginalSource, 0, NULL); // Set your compression quality (0.0 to 1.0). NSMutableDictionary *loDicMutableMetadata = [loDicMetadata mutableCopy]; [loDicMutableMetadata setObject:@(lfCompressionQualityValue) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality]; // Create an image destination. NSMutableData *loNewImageDataWithExif = [NSMutableData data]; CGImageDestinationRef loImgDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)loNewImageDataWithExif, CGImageSourceGetType(loImageOriginalSource), 1, NULL); // Add your image to the destination. CGImageDestinationAddImage(loImgDestination, loImgPhoto.CGImage, (__bridge CFDictionaryRef) loDicMutableMetadata); // Finalize the destination. if (CGImageDestinationFinalize(loImgDestination)) { NSLog(@"Successful image creation."); // process the image rendering, adjustment data creation and finalize the asset edit. //Upload photo with EXIF metadata [self myUploadMethod:loNewImageDataWithExif]; } else { NSLog(@"Error -> failed to finalize the image."); } CFRelease(loImageOriginalSource); CFRelease(loImgDestination); 
0
Nov 27 '17 at 23:40
source share



All Articles