pesa - detalles de fotos en iphone
UIImagePickerController y extracción de datos EXIF de fotos existentes (18)
Para iOS 10 - Swift 3
La devolución de llamada del selector tiene un dict de info
donde hay una clave con metadata : UIImagePickerControllerMediaMetadata
Es bien sabido que UIImagePickerController no devuelve los metadatos de la foto después de la selección. Sin embargo, un par de aplicaciones en la tienda de aplicaciones (Mobile Fotos, PixelPipe) parecen ser capaces de leer los archivos originales y los datos EXIF almacenados en ellos, lo que permite que la aplicación extraiga los datos geográficos de la foto seleccionada.
Parecen hacer esto leyendo el archivo original de la carpeta / private / var / mobile / Media / DCIM / 100APPLE / y ejecutándolo a través de una biblioteca EXIF.
Sin embargo, no puedo encontrar la forma de emparejar una foto devuelta por UIImagePickerController con un archivo en el disco. He explorado los tamaños de los archivos, pero el archivo original es un JPEG, mientras que la imagen devuelta es un UIImage sin formato, por lo que es imposible saber el tamaño del archivo de la imagen que se seleccionó.
Estoy considerando hacer una tabla de hashes y hacer coincidir los primeros x píxeles de cada imagen. Esto parece un poco exagerado, y probablemente bastante lento.
¿Alguna sugerencia?
¿Has echado un vistazo a esta biblioteca de exif iPhone?
http://code.google.com/p/iphone-exif/
Voy a intentarlo de mi lado. Me gustaría obtener las coordenadas de GPS (geoetiquetas) de la imagen que se ha tomado con el UIImagePickerController: /
Después de una mirada más profunda, esta biblioteca parece tomar información de NSData como entrada y el UIImagePickerController devuelve un UIImage después de tomar una instantánea. En teoría, si usamos el seleccionado de la categoría UIkit para UIImage
NSData * UIImageJPEGRepresentation (
UIImage *image,
CGFloat compressionQuality
);
Entonces podemos convertir el UIImage en una instancia NSData y luego usarlo con la biblioteca exif de iPhone.
ACTUALIZAR:
Le di una prueba a la biblioteca mencionada anteriormente y parece funcionar. Sin embargo, debido a mis conocimientos limitados sobre el formato EXIF y la falta de API de alto nivel en la biblioteca, no logro obtener los valores para las etiquetas EXIF. Aquí está mi código en caso de que alguno de ustedes pueda ir más allá:
#import "EXFJpeg.h"
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo {
NSLog(@"image picked %@ with info %@", image, editingInfo);
NSData* jpegData = UIImageJPEGRepresentation (image,0.5);
EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
[jpegScanner scanImageData: jpegData];
EXFMetaData* exifData = jpegScanner.exifMetaData;
EXFJFIF* jfif = jpegScanner.jfif;
EXFTag* tagDefinition = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_DateTime]];
//EXFTag* latitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLatitude]];
//EXFTag* longitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLongitude]];
id latitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]];
id longitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]];
id datetime = [exifData tagValue:[NSNumber numberWithInt:EXIF_DateTime]];
id t = [exifData tagValue:[NSNumber numberWithInt:EXIF_Model]];
....
....
La recuperación de la definición de etiquetas está bien, pero todos los valores de las etiquetas devuelven nil
:(
En caso de que quiera probar la biblioteca, necesita definir una variable global para ejecutarla (como se explica en el documento pero hum ...: /)
BOOL gLogging = FALSE;
ACTUALIZACIÓN 2
Responda aquí: iPhone: acceda a la información de ubicación de una fotografía. Un UIImage no encapsula la metainformación , por lo que estamos atascados: de seguro, no se proporcionará información EXIF a través de esta interfaz.
ACTUALIZACIÓN FINAL
Ok, logré que funcione, al menos para geoetiquetar correctamente las imágenes devueltas por el selector.
Antes de activar el UIImagePickerController, depende de usted utilizar CLLocationManager para recuperar el CLocation actual
Una vez que lo tenga, puede usar este método que usa la biblioteca exif-iPhone para geoetiquetar el UIImage desde CLLocation:
-(void) imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
if (url) {
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
CLLocation *location = [myasset valueForProperty:ALAssetPropertyLocation];
// location contains lat/long, timestamp, etc
// extracting the image is more tricky and 5.x beta ALAssetRepresentation has bugs!
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) {
NSLog(@"cant get image - %@", [myerror localizedDescription]);
};
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
[assetsLib assetForURL:url resultBlock:resultblock failureBlock:failureblock];
}
}
¿Hay alguna razón específica por la que quiera extraer los datos de ubicación de la imagen? Una alternativa podría ser obtener la ubicación por separado utilizando el marco CoreLocation. Si solo son los datos geográficos que necesita, esto podría ahorrarle algunos dolores de cabeza.
Apple ha agregado un Marco de E / S de imagen en iOS4 que se puede utilizar para leer datos EXIF de imágenes. No sé si el UIImagePickerController
devuelve una imagen con los datos EXIF incrustados.
Editar: en iOS4 puede buscar los datos EXIF tomando el valor de la clave UIImagePickerControllerMediaMetadata
en el diccionario de información que se pasa al delegado UIImagePickerControllerDelegate
.
Es posible que pueda copiar los datos de imagen devueltos por UIImagePickerController y cada una de las imágenes en el directorio y compararlas.
Esto es algo que la API pública no proporciona, pero podría ser útil para muchas personas. Su principal recurso es presentar un error con Apple que describa lo que necesita (y puede ser útil explicar por qué lo necesita). Esperemos que su solicitud pueda convertirse en una versión futura.
Después de presentar una falla, también puede usar uno de los incidentes de Asistencia técnica para desarrolladores (DTS) que vino con la membresía del Programa para desarrolladores de iPhone. Si hay una forma pública de hacerlo, un ingeniero de Apple lo sabrá. De lo contrario, al menos puede ayudar a obtener su atención un poco más dentro de la nave nodriza. ¡La mejor de las suertes!
Esto está en Swift 3 si aún quieres soporte para iOS 8:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
id thumbnailView = [[[[[[[[[[picker.view subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0];
NSString *fullSizePath = [[[thumbnailView selectedPhoto] fileGroup] pathForFullSizeImage];
NSString *thumbnailPath = [[[thumbnailView selectedPhoto] fileGroup] pathForThumbnailFile];
NSLog(@"%@ and %@", fullSizePath, thumbnailPath);
}
Esto funciona con iOS5 (beta 4) y el rollo de la cámara (necesita defs de tipo para los bloques en .h):
#import <Photos/Photos.h>
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil];
PHAsset *asset = fetchResult.firstObject;
//All you need is
//asset.location.coordinate.latitude
//asset.location.coordinate.longitude
//Other useful properties of PHAsset
//asset.favorite
//asset.modificationDate
//asset.creationDate
}
Estoy usando esto para imágenes de rollo de cámara
import AssetsLibrary
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if picker.sourceType == UIImagePickerControllerSourceType.photoLibrary,
let url = info[UIImagePickerControllerReferenceURL] as? URL {
let assetLibrary = ALAssetsLibrary()
assetLibrary.asset(for: url, resultBlock: { (asset) in
if let asset = asset {
let assetRep: ALAssetRepresentation = asset.defaultRepresentation()
let metaData: NSDictionary = assetRep.metadata() as NSDictionary
print(metaData)
}
}, failureBlock: { (error) in
print(error!)
})
}
}
Funciona obviamente si la imagen contiene metadatos gps
Espero eso ayude
Hay una forma en iOS 8
Sin usar ninguna biblioteca EXIF
terceros.
// Inside whatever implements UIImagePickerControllerDelegate
@import AssetsLibrary;
// ... your other code here ...
@implementation MYImagePickerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
UIImage *originalImage = info[UIImagePickerControllerOriginalImage];
UIImage *editedImage = info[UIImagePickerControllerEditedImage];
NSValue *cropRect = info[UIImagePickerControllerCropRect];
NSURL *mediaUrl = info[UIImagePickerControllerMediaURL];
NSURL *referenceUrl = info[UIImagePickerControllerReferenceURL];
NSDictionary *mediaMetadata = info[UIImagePickerControllerMediaMetadata];
NSLog(@"mediaType=%@", mediaType);
NSLog(@"originalImage=%@", originalImage);
NSLog(@"editedImage=%@", editedImage);
NSLog(@"cropRect=%@", cropRect);
NSLog(@"mediaUrl=%@", mediaUrl);
NSLog(@"referenceUrl=%@", referenceUrl);
NSLog(@"mediaMetadata=%@", mediaMetadata);
if (!referenceUrl) {
NSLog(@"Media did not have reference URL.");
} else {
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
[assetsLib assetForURL:referenceUrl
resultBlock:^(ALAsset *asset) {
NSString *type =
[asset valueForProperty:ALAssetPropertyType];
CLLocation *location =
[asset valueForProperty:ALAssetPropertyLocation];
NSNumber *duration =
[asset valueForProperty:ALAssetPropertyDuration];
NSNumber *orientation =
[asset valueForProperty:ALAssetPropertyOrientation];
NSDate *date =
[asset valueForProperty:ALAssetPropertyDate];
NSArray *representations =
[asset valueForProperty:ALAssetPropertyRepresentations];
NSDictionary *urls =
[asset valueForProperty:ALAssetPropertyURLs];
NSURL *assetUrl =
[asset valueForProperty:ALAssetPropertyAssetURL];
NSLog(@"type=%@", type);
NSLog(@"location=%@", location);
NSLog(@"duration=%@", duration);
NSLog(@"assetUrl=%@", assetUrl);
NSLog(@"orientation=%@", orientation);
NSLog(@"date=%@", date);
NSLog(@"representations=%@", representations);
NSLog(@"urls=%@", urls);
}
failureBlock:^(NSError *error) {
NSLog(@"Failed to get asset: %@", error);
}];
}
[picker dismissViewControllerAnimated:YES
completion:nil];
}
@end
La forma maliciosa de hacerlo es atravesar las vistas de UIImagePickerViewController y seleccionar la imagen seleccionada en la devolución de llamada del delegado.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { id thumbnailView = [[[[[[[[[[picker.view subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0]; NSString *fullSizePath = [[[thumbnailView selectedPhoto] fileGroup] pathForFullSizeImage]; NSString *thumbnailPath = [[[thumbnailView selectedPhoto] fileGroup] pathForThumbnailFile]; NSLog(@"%@ and %@", fullSizePath, thumbnailPath); }
Eso le dará la ruta a la imagen de tamaño completo, que luego puede abrir con una biblioteca EXIF de su elección.
Pero esto llama una API privada y Apple detectará estos nombres de métodos si envía esta aplicación. Entonces no hagas esto, ¿de acuerdo?
Para iOS 8 y posterior, puede usar Photos Framework.
- (BOOL)writeCGImageToCameraRoll:(CGImageRef)cgImage withMetadata:(NSDictionary *)metadata
{
NSDictionary *Exif = [metadata objectForKey:@"Exif"]; // Add this line
}
Para obtener estos metadatos, deberá usar el marco de nivel inferior AVFoundation.
Eche un vistazo al ejemplo de Apple''s Squarecam (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)
Encuentre el método a continuación y agregue la línea que agregué al código. El diccionario de metadatos devuelto también contiene un objeto NSDictionary de diagnóstico.
-(CLLocation*)locationFromAsset:(ALAsset*)asset
{
if (!asset)
return nil;
NSDictionary* pickedImageMetadata = [[asset defaultRepresentation] metadata];
NSDictionary* gpsInfo = [pickedImageMetadata objectForKey:(__bridge NSString *)kCGImagePropertyGPSDictionary];
if (gpsInfo){
NSNumber* nLat = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLatitude];
NSNumber* nLng = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLongitude];
if (nLat && nLng)
return [[CLLocation alloc]initWithLatitude:[nLat doubleValue] longitude:[nLng doubleValue]];
}
return nil;
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
// create the asset library in the init method of your custom object or view controller
//self.library = [[ALAssetsLibrary alloc] init];
//
[self.library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
// try to retrieve gps metadata coordinates
CLLocation* myLocation = [self locationFromAsset:asset];
// Do your stuff....
} failureBlock:^(NSError *error) {
NSLog(@"Failed to get asset from library");
}];
}
Pasé un tiempo trabajando en esto también para una aplicación que me contrataron para construir. Básicamente, como la API actualmente está en pie, no es posible. El problema básico es que la clase UIImage TIRAS todos los datos EXIF excepto la orientación hacia fuera. Además, la función para guardar en el rollo de la cámara elimina estos datos. Así que, básicamente, la única forma de obtener y mantener cualquier información EXIF adicional es guardarla en un "rollo de cámara" privado en su aplicación. También he archivado este error con Apple y he enfatizado la necesidad de los representantes de revisión de aplicaciones con los que hemos estado en contacto. Esperemos que algún día lo agreguen. De lo contrario, hace que etiquetar GEO sea completamente inútil, ya que solo funciona en la aplicación de cámara "estándar".
NOTA Algunas aplicaciones en la tienda de aplicaciones piratean esto. Por, lo que he encontrado, acceder directamente al rollo de la cámara y GUARDAR fotos directamente a él para guardar los datos de GEO. Sin embargo, esto solo funciona con el rollo de la cámara / fotos guardadas y NO con el resto de la biblioteca de fotos. Las fotos "sincronizadas" con su teléfono desde su computadora tienen todos los datos EXIF a excepción de la orientación despojada.
Todavía no puedo entender por qué esas aplicaciones fueron aprobadas (diablos, incluso ELIMINAR desde el rollo de la cámara) y nuestra aplicación que no hace nada de eso todavía se está reteniendo.
Sólo un pensamiento, pero ¿has probado TTPhotoViewController en el proyecto Three20 en GitHub?
Eso proporciona un selector de imágenes que puede leer de múltiples fuentes. Es posible que pueda usarlo como una alternativa a UIImagePickerController, o la fuente podría darle una pista de cómo encontrar la información que necesita.
Tenía una pregunta similar en la que solo quería la fecha en que se tomó una foto y ninguna de las anteriores parece resolver mi problema de una manera sencilla (por ejemplo, no hay bibliotecas externas), así que aquí están todos los datos que pude encontrar y que puede extraer de una imagen después de seleccionarla con el selector:
mediaType=public.image
originalImage=<UIImage: 0x7fb38e00e870> size {1280, 850} orientation 0 scale 1.000000
editedImage=<UIImage: 0x7fb38e09e1e0> size {640, 424} orientation 0 scale 1.000000
cropRect=NSRect: {{0, 0}, {1280, 848}}
mediaUrl=(null)
referenceUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG
mediaMetadata=(null)
type=ALAssetTypePhoto
location=(null)
duration=ALErrorInvalidProperty
assetUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG
orientation=0
date=2014-07-14 04:28:18 +0000
representations=(
"public.jpeg"
)
urls={
"public.jpeg" = "assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG";
}
Entonces, cuando seleccionas una imagen, obtienes resultados que se ven así (¡incluyendo la fecha!):
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Try to get the original file.
NSURL *originalFile = [info objectForKey:UIImagePickerControllerMediaURL];
if (originalFile) {
NSData *fileData = [NSData dataWithContentsOfURL:originalFile];
}
}
De todos modos, con suerte eso le ahorra a alguien más algo de tiempo.
Use la clave del diccionario UIImagePickerControllerMediaURL
para obtener la URL del archivo en el archivo original. A pesar de lo que dice la documentación, puede obtener la URL del archivo para las fotos y no solo las películas.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let url = info[UIImagePickerControllerReferenceURL] as? URL
if url != nil {
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [url!], options: nil)
let asset = fetchResult.firstObject
print(asset?.location?.coordinate.latitude)
print(asset?.creationDate)
}
}
parece que la foto obtenida por UIImagePickerControllerMediaURL no tiene etiquetas exif en absoluto