Problema al configurar datos exif para una imagen

Estoy usando el nuevo marco de ImageIO en iOS 4.1. Recupero con éxito los metadatos exif usando lo siguiente:

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

Al leerlo, parece válido. Guardar una imagen funciona, pero nunca hay datos exif en la imagen.

  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); 

En la siguiente publicación del blog recibí mi respuesta cuando tuve problemas para modificar y guardar los datos de Exif Cacao cafeinado. Esto funciona en iOS.

Aquí está mi código de prueba para escribir datos de Exif y GPS. Es más o menos una copia y pega del código del blog anterior. Estoy usando esto para escribir datos exif en una imagen capturada.

 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); 

Intenté la respuesta de Steve y funciona, pero creo que es lenta para imágenes grandes porque está duplicando toda la imagen.

Puede establecer las propiedades directamente en CMSampleBuffer usando CMSetAttachments. Simplemente haz esto antes de llamar a 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); 

Y el método getGPSDictionaryForLocation: se puede encontrar aquí:

Guardar información de geoetiqueta con foto en iOS4.1

Creé una categoría MSMutableDictionary para ayudar a guardar geotag y otros metadatos en una imagen. Mira mi blog aquí:

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

Una pieza implica la creación del diccionario de metadatos GPS para los datos EXIF. Aquí hay una categoría de CLLocation para hacer justamente eso:

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

Como todavía estoy aprendiendo, me llevó un tiempo recostackr las respuestas de Steve y Marty para agregar metadatos a los datos para una imagen y luego guardarlos. A continuación, hice una implementación Swift de sus respuestas que no usa métodos de ImageIO.

Dado un buffer de buffer muestra CMSampleBuffer, alguna location CLLocation, y usando la sugerencia de Morty de usar CMSetAttachments para evitar la duplicación de la imagen, podemos hacer lo siguiente. El método gpsMetadata que extiende CLLocation se puede encontrar aquí (también Swift).

 if let location = location { // Get the existing metadata dictionary (if there is one) var metaDict = CMCopyDictionaryOfAttachments(nil, buffer, kCMAttachmentMode_ShouldPropagate) as? Dictionary ?? [:] // 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 } 

En este punto, puede escribir los datos de imagen en un archivo o usar la API de Fotos para guardar la imagen en Camera Roll (usando PHAssetCreationRequest's addResource:with:data:options para iOS 9+, o para versiones anteriores de iOS escribiendo el imageData a un archivo temporal y luego llamando a PHAssetChangeRequest.creationRequestForAssetFromImage:atFileURL ). Tenga en cuenta que ALAssertLibrary está en desuso para iOS 9. Proporciono más detalles de implementación en una respuesta aquí .

[Revisando esta respuesta debido a los votos a la baja sin explicaciones.]

Apple ha actualizado su artículo sobre este tema (Technical Q & A QA1622). Si está utilizando una versión anterior de Xcode, es posible que todavía tenga el artículo que dice, más o menos, mala suerte, no puede hacer esto sin un análisis de bajo nivel de los datos de la imagen.

La versión actualizada está aquí:

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

Adapte el código allí de la siguiente manera:

 - (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]; } 

y yo llamo esto desde

 imagePickerController:didFinishPickingMediaWithInfo: 

que es el método de delegado para el selector de imágenes. (Ahí es donde puse la lógica para ver si hay una imagen para guardar, etc.)

Para completar, aquí está el método de ayuda para obtener los datos del GPS como un diccionario:

 - (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; } 

Consulte también Escribir UIImage junto con metadatos (EXIF, GPS, TIFF) en la biblioteca de fotos de iPhone

De esta forma configuré los datos EXIF, también puedes comprimir la foto si la necesitas, esto me solucionó el problema: espero que ayude

 // Get your image. NSURL *url = @"http://sofes.miximages.com/iphone/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);