Pergunta

É bem sabido que o UIImagePickerController não retorna os metadados da foto após a seleção.No entanto, alguns aplicativos na app store (Móvel Fotos, Michaeldmelo) parece ser capaz de ler os arquivos originais e os dados EXIF armazenados dentro deles, a ativação do aplicativo para extrair o geodata a partir da foto selecionada.

Eles parecem fazer isso lendo o arquivo original do /private/var/mobile/Media/DCIM/100APPLE/ pasta e executá-lo através de uma biblioteca EXIF.

No entanto, eu não posso trabalhar fora, uma forma de correspondência de uma foto retornado do UIImagePickerController para um arquivo no disco.Eu tenho explorado tamanhos de arquivo, mas o arquivo original é um JPEG, enquanto a devolvidos imagem é uma matéria-prima de UIImage, o que torna impossível saber o tamanho do arquivo da imagem que foi selecionada.

Estou pensando em fazer uma tabela de hashes e correspondência com o primeiro x pixels de cada imagem.Isso parece um pouco mais alto, porém, e, provavelmente, bastante lento.

Alguma sugestão?

Foi útil?

Solução 18

A maneira impertinente de fazer isso é para atravessar as opiniões do UIImagePickerViewController e escolher a imagem selecionada no callback 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);

}

Isso vai dar-lhe o caminho para a imagem em tamanho real, que você pode então aberto com uma biblioteca de EXIF ??de sua escolha.

Mas, isso requer uma API privado e esses nomes método irá ser detectado pela Apple se você enviar esta aplicação. Portanto, não faça isso, OK?

Outras dicas

Você já deu uma olhada nessa exif iPhone biblioteca?

http://code.google.com/p/iphone-exif/

Vou tentar no meu lado.Gostaria de obter o GPS (geotags) coordenadas da imagem que foi tirada com o UIImagePickerController :/

Depois de um olhar mais profundo, esta biblioteca parece ter NSData informações como entrada e o UIImagePickerController retorna um UIImage depois de tirar um instantâneo.Em teoria, se usamos o selecionado do UIkit categoria para UIImage

NSData * UIImageJPEGRepresentation (
   UIImage *image,
   CGFloat compressionQuality
);

Em seguida, podemos converter o UIImage em um NSData instância e, em seguida, usá-lo com o iPhone exif biblioteca.

ATUALIZAÇÃO:
Eu dei um teste para a biblioteca mencionado acima e que parece funcionar.No entanto, devido a minha pouca knwoledge sobre o formato EXIF e a falta de elevado nível de API na biblioteca, eu não consegui obter os valores para as etiquetas EXIF.Veja o meu código no caso de qualquer uma que pode ir mais longe :


#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]];
....
....

A obtenção de marcas de definição é OK, mas todos os valores de marca retorna nil :(

No caso de você querer dar uma chance para a biblioteca, você precisa definir uma variável global para fazê-lo funcionar (como explicado no doc, mas hum..:/)

BOOL gLogging = FALSE;

ATUALIZAÇÃO 2
Aqui a resposta : iPhone - acesso a informações de localização a partir de uma foto Um UIImage não encapsular as informações de meta, então nós estamos presos :com certeza, sem informações EXIF será dada através desta interface.

ATUALIZAÇÃO FINAL
Ok, eu consegui fazê-lo funcionar, pelo menos para geotag corretamente imagens retornadas pelo selecionador.
Antes de acionar o UIImagePickerController, cabe a você usar o CLLocationManager para recuperar o atual CLocation
Uma vez que você tem isso, você pode usar este método que utiliza exif-iPhone biblioteca para geotag o UIImage do CLLocation :


-(NSData*) geotagImage:(UIImage*)image withLocation:(CLLocation*)imageLocation {
    NSData* jpegData =  UIImageJPEGRepresentation(image, 0.8);
    EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
    [jpegScanner scanImageData: jpegData];
    EXFMetaData* exifMetaData = jpegScanner.exifMetaData;
    // end of helper methods 
    // adding GPS data to the Exif object 
    NSMutableArray* locArray = [self createLocArray:imageLocation.coordinate.latitude]; 
    EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init]; 
    [self populateGPS: gpsLoc :locArray]; 
    [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ]; 
    [gpsLoc release]; 
    [locArray release]; 
    locArray = [self createLocArray:imageLocation.coordinate.longitude]; 
    gpsLoc = [[EXFGPSLoc alloc] init]; 
    [self populateGPS: gpsLoc :locArray]; 
    [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLongitude] ]; 
    [gpsLoc release]; 
    [locArray release];
    NSString* ref;
    if (imageLocation.coordinate.latitude <0.0)
        ref = @"S"; 
    else
        ref =@"N"; 
    [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ]; 
    if (imageLocation.coordinate.longitude <0.0)
        ref = @"W"; 
    else
        ref =@"E"; 
    [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLongitudeRef] ]; 
    NSMutableData* taggedJpegData = [[NSMutableData alloc] init];
    [jpegScanner populateImageData:taggedJpegData];
    [jpegScanner release];
    return [taggedJpegData autorelease];
}

// Helper methods for location conversion -(NSMutableArray*) createLocArray:(double) val{ val = fabs(val); NSMutableArray* array = [[NSMutableArray alloc] init]; double deg = (int)val; [array addObject:[NSNumber numberWithDouble:deg]]; val = val - deg; val = val*60; double minutes = (int) val; [array addObject:[NSNumber numberWithDouble:minutes]]; val = val - minutes; val = val*60; double seconds = val; [array addObject:[NSNumber numberWithDouble:seconds]]; return array; } -(void) populateGPS:(EXFGPSLoc* ) gpsLoc :(NSArray*) locArray{ long numDenumArray[2]; long* arrPtr = numDenumArray; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:0]]; EXFraction* fract = [[EXFraction alloc] initWith:numDenumArray[0]:numDenumArray[1]]; gpsLoc.degrees = fract; [fract release]; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:1]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.minutes = fract; [fract release]; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:2]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.seconds = fract; [fract release]; }

Isso funciona com iOS5 (beta 4) e o rolo da câmera (é necessário digitar defs para os blocos no .h):

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

Há uma maneira em iOS 8

Sem usando qualquer 3o partido EXIF biblioteca.

#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
}

A Apple adicionou uma Imagem I / O quadro em iOS4, que pode ser usada para ler dados EXIF ??de fotos. Eu não sei se o UIImagePickerController retorna uma imagem com os dados EXIF ??embutidos embora.

Editar: Em iOS4 você pode buscar os dados EXIF, agarrando o valor da chave UIImagePickerControllerMediaMetadata na informação dicionário que é passado para o delegado UIImagePickerControllerDelegate.

Eu tinha uma pergunta semelhante onde eu queria apenas a data a foto foi tirada e nenhuma das opções acima parecem resolver o meu problema de uma forma simples (por exemplo, há bibliotecas externas), então aqui está todos os dados que eu poderia encontrar que você pode extrair de uma imagem após selecioná-la com o selecionador:

// 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

Assim, quando você seleciona uma imagem, você obter uma saída parecida com esta (incluindo a data!):

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

De qualquer forma, espero que salva alguém de algum tempo.

Eu passo enquanto trabalhava neste, bem como para uma aplicação que foi contratada para construir. Basicamente como a API está atualmente não é possível. O problema básico é a classe UIImage FITAS todos os dados EXIF, exceto para a Orientação. Também a função de salvar ao rolo da câmera retira esses dados para fora. Então, basicamente a única maneira de agarrar e manter quaisquer dados EXIF ??extras é para salvá-lo em um "rolo da câmera" privado na sua aplicação. Tenho pagamento este bug com maçã bem e enfatizou a necessidade dos representantes colaborador do app que esteve em contato com. Esperemos que algum dia eles vão adicioná-lo em .. Caso contrário, ele faz ter Geo Tagging completamente inútil, uma vez que só funciona no "estoque" aplicativo da câmera.

NOTA Algumas aplicações na App Store corte em torno deste. Por, o que eu encontrei, acessar diretamente o rolo da câmera e salvando fotos diretamente a ele para salvar os dados GEO. No entanto, isto só funciona com os rolo da câmera / fotos salvo e não o resto da biblioteca de fotos. As fotos "sincronizados" para o seu telefone a partir do computador tem todos os dados EXIF, exceto para a orientação despojado.

Eu ainda não consigo entender por que esses pedidos foram aprovados (diabos eles até mesmo apagar a partir do rolo da câmera) e nossa aplicação que não faz nada disso ainda está sendo retido.

Para iOS 8 e mais tarde você pode usar Fotos Framework.

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

Isso é algo que a API pública não fornece, mas poderia ser útil para muitas pessoas. Seu recurso principal é registrar um bug com a Apple que descreve o que você precisa (e ele pode ser útil para explicar porque você precisa dele também). Esperemos que o seu pedido poderia fazê-lo em uma versão futura.

Após a apresentação de um bug, você também pode usar um dos incidentes desenvolvedor Suporte Técnico (DTS) que acompanham o iPhone Developer associação ao programa. Se há uma maneira pública para fazer isso, um engenheiro da Apple vai saber. Caso contrário, pode pelo menos ajudar a obter a sua situação um pouco mais atenção dentro da nave-mãe. Melhor da sorte!

chave do dicionário Use o UIImagePickerControllerMediaURL para obter o URL do arquivo para o arquivo original. Apesar do que diz a documentação, você pode obter a URL do arquivo para fotos e não apenas filmes.

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

Você pode ser capaz de botar os dados da imagem retornados pelo UIImagePickerController e cada uma das imagens no diretório e compará-los.

Apenas um pensamento, mas você já tentou TTPhotoViewController no projeto Three20 no GitHub?

Isso fornece um selecionador de imagem que pode ler a partir de múltiplas fontes. Você pode ser capaz de usá-lo como uma alternativa para UIImagePickerController, ou a fonte pode lhe dar uma idéia de como trabalhar para fora como para obter a informação que você precisa.

Existe uma razão específica que você deseja extrair os dados de localização a partir da imagem? Uma alternativa poderia ser a de obter a localização separadamente usando a estrutura CoreLocation. Se é apenas a dados geográficos você precisa, isso pode poupar algumas dores de cabeça.

parece que foto alcançado por UIImagePickerControllerMediaURL não tem exif etiquetas em tudo

A fim de obter esses metadados você vai ter que usar o menor AVFoundation quadro nível.

Dê uma olhada exemplo Squarecam da Apple (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)

Encontre o método abaixo e adicione a linha, eu adicionei ao código. O dicionário de metadados retornado também contém um objeto NSDictionary diagnósticos.

- (BOOL)writeCGImageToCameraRoll:(CGImageRef)cgImage withMetadata:(NSDictionary *)metadata
{

   NSDictionary *Exif = [metadata objectForKey:@"Exif"];   // Add this line

}

Eu estou usando isso para imagens de câmera rolo

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

Ele funciona, obviamente, se a imagem contém gps meta informações

Hope isso ajuda

Esta é em Swift 3 se você ainda quiser suporte para iOS 8:

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!)
        })
    }

}

Para iOS 10 - Swift 3

retorno do selecionador tem um dict info onde há uma chave com metadados : UIImagePickerControllerMediaMetadata

imagem exemplo selecionador de metadados

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top