Soy consciente de cómo guardar metadatos usando ALAssets. Pero, quiero guardar una imagen, o subirla a alguna parte, con exif intacto. Tengo datos exif como NSDictionary. ¿Pero cómo puedo inyectarlo correctamente en un UIImage (o probablemente una representación NSData JPEG)?Cómo escribir metadatos exif en una imagen (no en el rollo de la cámara, solo un UIImage o JPEG)
Respuesta
UIImage no contiene información de metadatos (está desprotegida). Así que si usted desea guardarla sin utilizar el método imagepicker (no en el rollo de la cámara):
Siga la respuesta aquí para escribir en un archivo con los metadatos intactos:
Problem setting exif data for an image
idea de por qué lo haría esto se downvoted pero aquí es el método:
en este caso im conseguir la imagen a través AVFoundation y esto es lo que pasa en el
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
// code here
}
bloque de código:
CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict);
// Create formatted date
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:timeZone];
[formatter setDateFormat:@"HH:mm:ss.SS"];
// Create GPS Dictionary
NSDictionary *gpsDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:fabs(loc.coordinate.latitude)], kCGImagePropertyGPSLatitude
, ((loc.coordinate.latitude >= 0) ? @"N" : @"S"), kCGImagePropertyGPSLatitudeRef
, [NSNumber numberWithFloat:fabs(loc.coordinate.longitude)], kCGImagePropertyGPSLongitude
, ((loc.coordinate.longitude >= 0) ? @"E" : @"W"), kCGImagePropertyGPSLongitudeRef
, [formatter stringFromDate:[loc timestamp]], kCGImagePropertyGPSTimeStamp
, [NSNumber numberWithFloat:fabs(loc.altitude)], kCGImagePropertyGPSAltitude
, nil];
// The gps info goes into the gps metadata part
CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, (__bridge void *)gpsDict);
// Here just as an example im adding the attitude matrix in the exif comment metadata
CMRotationMatrix m = att.rotationMatrix;
GLKMatrix4 attMat = GLKMatrix4Make(m.m11, m.m12, m.m13, 0, m.m21, m.m22, m.m23, 0, m.m31, m.m32, m.m33, 0, 0, 0, 0, 1);
NSMutableDictionary *EXIFDictionary = (__bridge NSMutableDictionary*)CFDictionaryGetValue(mutable, kCGImagePropertyExifDictionary);
[EXIFDictionary setValue:NSStringFromGLKMatrix4(attMat) forKey:(NSString *)kCGImagePropertyExifUserComment];
CFDictionarySetValue(mutable, kCGImagePropertyExifDictionary, (__bridge void *)EXIFDictionary);
NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer] ;
Después de este código que tendrá su imagen en el NSData jpeg y el diccionario correspoding de esa imagen en el cfdictionary mutable.
Todo lo que tiene que hacer ahora es:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL);
CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)
NSMutableData *dest_data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge 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) mutable);
//tell the destination to write the image data and metadata into our data object.
//It will return false if something goes wrong
BOOL 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
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"ImagesFolder"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
// NSString *imageName = @"ImageName";
NSString *fullPath = [dataPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.jpg", name]]; //add our image to the path
[dest_data writeToFile:fullPath atomically:YES];
//cleanup
CFRelease(destination);
CFRelease(source);
Nota cómo no estoy ahorrando el uso de los ALAssets sino directamente en una carpeta de mi elección.
Por cierto, la mayoría de este código se puede encontrar en el enlace que publiqué al principio.
Gracias. ¿Pero has hecho alguna optimización para la memoria? Recibo advertencias de memoria debido a la duplicación de los datos de imagen en su método. Está el jpeg nsdata, luego un destino. Vi en su respuesta vinculada una forma de no duplicar, pero estaba usando almacenamientos intermedios de muestra, que no estoy usando para las fotos tomadas a través de UIImagePickerController. – akaru
¿Quiere decir que está utilizando el método predeterminado para tomar fotografías? el que guarda automáticamente en el rollo de la cámara? Pensé que uno guardó la imagen con los metadatos incluidos. Si no, el ejemplo de la manzana sí. – Pochi
Estoy usando el selector, pero como una cámara. En este caso, obtengo un UIImage directamente, junto con algunos metadatos. Luego tengo que inyectar datos GPS, etc. Aún tengo que guardarlo manualmente en otra carpeta. – akaru
Estoy usando UIImagePickerController para obtener la imagen de la cámara y mi flujo es un poco diferente al descrito por Chiquis. Aquí está:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = info[@"UIImagePickerControllerOriginalImage"];
NSString *fullPhotoFilename = ...; // generate the photo name and path here
NSData *photoData = [UIImage taggedImageData:image.jpegData metadata:info[@"UIImagePickerControllerMediaMetadata"] orientation:image.imageOrientation];
[photoData writeToFile:fullPhotoFilename atomically:YES];
}
Y el uso de una categoría UIImage poner combinar los datos de imagen con sus metadatos:
#import <ImageIO/ImageIO.h>
#import "UIImage+Tagging.h"
#import "LocationHelper.h"
@implementation UIImage (Tagging)
+ (NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSMutableDictionary *)metadata {
// create an imagesourceref
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
// 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) metadata);
BOOL success = NO;
success = CGImageDestinationFinalize(destination);
if (!success) {
NSLog(@"Error: Could not create data from image destination");
}
CFRelease(destination);
CFRelease(source);
return dest_data;
}
+ (NSData *)taggedImageData:(NSData *)imageData metadata:(NSDictionary *)metadata orientation:(UIImageOrientation)orientation {
CLLocationManager *locationManager = [CLLocationManager new];
CLLocation *location = [locationManager location];
NSMutableDictionary *newMetadata = [NSMutableDictionary dictionaryWithDictionary:metadata];
if (!newMetadata[(NSString *)kCGImagePropertyGPSDictionary] && location) {
newMetadata[(NSString *)kCGImagePropertyGPSDictionary] = [LocationHelper gpsDictionaryForLocation:location];
}
// Reference: http://sylvana.net/jpegcrop/exif_orientation.html
int newOrientation;
switch (orientation) {
case UIImageOrientationUp:
newOrientation = 1;
break;
case UIImageOrientationDown:
newOrientation = 3;
break;
case UIImageOrientationLeft:
newOrientation = 8;
break;
case UIImageOrientationRight:
newOrientation = 6;
break;
case UIImageOrientationUpMirrored:
newOrientation = 2;
break;
case UIImageOrientationDownMirrored:
newOrientation = 4;
break;
case UIImageOrientationLeftMirrored:
newOrientation = 5;
break;
case UIImageOrientationRightMirrored:
newOrientation = 7;
break;
default:
newOrientation = -1;
}
if (newOrientation != -1) {
newMetadata[(NSString *)kCGImagePropertyOrientation] = @(newOrientation);
}
NSData *newImageData = [self writeMetadataIntoImageData:imageData metadata:newMetadata];
return newImageData;
}
Y, por último, aquí es el método que estoy utilizando para generar el diccionario de GPS sea necesario:
+ (NSDictionary *)gpsDictionaryForLocation:(CLLocation *)location {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:timeZone];
[formatter setDateFormat:@"HH:mm:ss.SS"];
NSDictionary *gpsDict = @{(NSString *)kCGImagePropertyGPSLatitude: @(fabs(location.coordinate.latitude)),
(NSString *)kCGImagePropertyGPSLatitudeRef: ((location.coordinate.latitude >= 0) ? @"N" : @"S"),
(NSString *)kCGImagePropertyGPSLongitude: @(fabs(location.coordinate.longitude)),
(NSString *)kCGImagePropertyGPSLongitudeRef: ((location.coordinate.longitude >= 0) ? @"E" : @"W"),
(NSString *)kCGImagePropertyGPSTimeStamp: [formatter stringFromDate:[location timestamp]],
(NSString *)kCGImagePropertyGPSAltitude: @(fabs(location.altitude)),
};
return gpsDict;
}
Espero que ayude a alguien. Gracias a Gustavo Ambrozio, Chiquis y otros miembros de SO, pude juntarlo y usarlo en mi proyecto.
Excelente trabajo, esta es la única solución de trabajo que funciona muy bien con ALAssets. – Michael
Gracias. Además de establecer la hora, también tuve que establecer la fecha. Esto solo requiere un formateador de fecha UTC con el formato "aaaa: MM: dd" y una clave de kCGImagePropertyGPSDateStamp. – Ian
Hay una manera más fácil.Si necesita ahorrar algo de Exif, puede utilizar SimpleExif pod
En primer lugar crear un ExifContainer:
ExifContainer *container = [[ExifContainer alloc] init];
y rellenarlo con todos los datos requred:
[container addUserComment:@"A long time ago, in a galaxy far, far away"];
[container addCreationDate:[NSDate dateWithTimeIntervalSinceNow:-10000000]];
[container addLocation:locations[0]];
continuación, puede agregar estos datos a imagen:
NSData *imageData = [[UIImage imageNamed:@"DemoImage"] addExif:container];
Luego solo guarde esta información como un archivo JPEG
Estos son los conceptos básicos para configurar los metadatos de Crear y modelar en un archivo .jpg
en Swift 3 https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7 Las versiones de nivel superior, como usar el pod ExifContainer, no me funcionaron.
¿Envolvió esto en una extensión o método, o simplemente incluyó el código en sus métodos de guardado? –
que enfrentan el mismo problema, ahora puedo subir archivos con los datos EXIF, también puede comprimir foto si lo necesita, esto resuelve el problema para mí:
// Get your image.
UIImage *loImgPhoto = [self getImageFromAsset:loPHAsset];
// 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);
getImageFromAsset
método:
-(UIImage *)getImageFromAsset:(PHAsset *)aPHAsset
{
__block UIImage *limgImageResult;
PHImageRequestOptions *lPHImageRequestOptions = [PHImageRequestOptions new];
lPHImageRequestOptions.synchronous = YES;
[self.imageManager requestImageForAsset:aPHAsset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault//PHImageContentModeAspectFit
options:lPHImageRequestOptions
resultHandler:^(UIImage *limgImage, NSDictionary *info) {
limgImageResult = limgImage;
}];
return limgImageResult;
}
- 1. Escribir metadatos EXIF para imágenes en Android
- 2. Escribir metadatos de dpi en una imagen jpeg en Java
- 3. ¿Cómo modificar los metadatos EXIF para imágenes JPEG usando Coldfusion?
- 4. datos Exif Android en archivo JPEG
- 5. ¿Cómo se realiza el corte cuadrado de las fotos en el rollo de la cámara?
- 6. Extraer EXIF de JPEG
- 7. ¿Cómo adjuntar metadatos EXIF a un Bitmap serializado en Android?
- 8. ¿Detecta si un UIImage es PNG o JPEG?
- 9. Visualizar UIImage desde la cámara en UIWebView
- 10. ¿Cuál es el tamaño máximo de los metadatos JPEG?
- 11. ¿Cómo añado metadatos a una imagen en Matlab?
- 12. Cómo recuperar información EXIF de una imagen en Rails
- 13. Cómo guardar un UIImage en formato JPEG progresivo en iOS?
- 14. Rotación inexplicable de la cámara Android en la captura de algunos dispositivos (no en EXIF)
- 15. ¿Cómo puedo guardar una imagen en el carrete de la cámara?
- 16. Establecer metadatos JPEG: problemas de implementación
- 17. ¿Cómo se colocan las etiquetas EXIF en un JPG, teniendo el búfer jpeg sin formato en C++?
- 18. escribiendo datos exif en php
- 19. escritura/Etiqueta geográfica imágenes JPEG (datos EXIF) en Android
- 20. Cómo reducir el tamaño de imagen JPEG en Android
- 21. Calcular el hash de solo los datos de imagen del núcleo (excluyendo los metadatos) para una imagen
- 22. ImageIO no puede escribir un archivo JPEG
- 23. ¿Cómo puedo eliminar los metadatos de una imagen JPEG en Java?
- 24. Uso de Android SDK No veo ningún EXIF en mi JPEG
- 25. Cómo editar datos EXIF en .NET
- 26. Mostrar una imagen o UIImage con un plano CALayer
- 27. ¿Cómo agrego datos exif a una imagen?
- 28. Cómo establecer la imagen en UIImage
- 29. En Python, ¿cómo leo los datos exif de una imagen?
- 30. Agregar metadatos personalizados a archivos jpeg
[Esta respuesta] (http://stackoverflow.com/questions/1238838/uiimagepickercontroller-and-extracting-exif-data-from-existing-photos) podría ser útil para usted. – user1118321
Pruebe libexif: http://libexif.sourceforge.net/docs.html –