web-dev-qa-db-fra.com

Lecture des données GPS à partir de l'image renvoyée par la caméra dans iOS pour iPhone

Je dois obtenir les coordonnées GPS d'une image prise avec l'appareil photo de l'appareil iOS. Je me moque des images de la pellicule, uniquement de la photo prise avec UIImagePickerControllerSourceTypeCamera.

J'ai lu de nombreuses réponses de stackoverflow, telles que Obtenir les données Exif de UIImage - UIImagePickerController , ce qui suppose que vous utilisez le cadre AssetsLibrary, qui ne semble pas fonctionner avec les images de caméra, ou utilisez CoreLocaiton pour obtenir la latitude/longitude de l'application elle-même, pas de l'image.

L'utilisation de CoreLocation n'est pas une option. Cela ne me donnera pas les coordonnées lorsque le déclencheur a été enfoncé. (Avec les solutions basées sur CoreLocation, vous devez soit enregistrer les coordonnées avant ou après l'affichage de la caméra, et bien sûr, si le périphérique bouge, les coordonnées seront fausses. Cette méthode devrait fonctionner avec un périphérique fixe.) 

Je suis uniquement iOS5, je n'ai donc pas besoin de prendre en charge les appareils plus anciens. Ceci est également pour un produit commercial donc je ne peux pas utiliser http://code.google.com/p/iphone-exif/ .

Alors, quelles sont mes options pour lire les données GPS à partir de l'image renvoyée par l'appareil photo sous iOS5? Pour le moment, tout ce que je peux penser, c’est de sauvegarder l’image en pellicule, puis d’utiliser la bibliothèque AssetsLibrary, mais cela me semble bien hokey.

Merci!


Voici le code que j'ai écrit basé sur la réponse de Caleb. 

    UIImage *image =  [info objectForKey:UIImagePickerControllerOriginalImage];

    NSData *jpeg = UIImageJPEGRepresentation(image,1.0);
    CGImageSourceRef  source ;
    source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL);

    NSDictionary *metadataNew = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);  

    NSLog(@"%@",metadataNew);

et ma console montre:

    2012-04-26 14:15:37:137 ferret[2060:1799] {
        ColorModel = RGB;
        Depth = 8;
        Orientation = 6;
        PixelHeight = 1936;
        PixelWidth = 2592;
        "{Exif}" =     {
            ColorSpace = 1;
            PixelXDimension = 2592;
            PixelYDimension = 1936;
        };
        "{JFIF}" =     {
            DensityUnit = 0;
            JFIFVersion =         (
                1,
                1
            );
            XDensity = 1;
            YDensity = 1;
        };
        "{TIFF}" =     {
            Orientation = 6;
        };
    }

Pas de latitude/longitude.

34
Paul Cezanne

Une possibilité consiste à laisser CoreLocation en marche lorsque la caméra est visible. Enregistrez chaque CCLocation dans un tableau avec l'heure de l'échantillon. Lorsque la photo revient, trouvez son heure, puis faites correspondre le CClocation le plus proche du tableau.

Cela semble kludgy mais ça va marcher.

4
Paul Cezanne

Le problème est que depuis iOS 4 UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; supprime la géolocalisation. Pour résoudre ce problème, vous devez utiliser le chemin d'accès photo d'origine pour accéder aux métadonnées de l'image complète. Avec quelque chose comme ça:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:referenceURL resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *rep = [asset defaultRepresentation];
        NSDictionary *metadata = rep.metadata;
        NSLog(@"%@", metadata);

        CGImageRef iref = [rep fullScreenImage] ;

        if (iref) {
            self.imageView.image = [UIImage imageWithCGImage:iref];
        }
    } failureBlock:^(NSError *error) {
        // error handling
    }];

La sortie devrait être quelque chose comme:

{
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 1936;
    PixelWidth = 2592;
    "{Exif}" =     {
        ApertureValue = "2.970854";
        BrightnessValue = "1.115874";
        ColorSpace = 1;
        ComponentsConfiguration =         (
            0,
            0,
            0,
            1
        );
        DateTimeDigitized = "2012:07:14 21:55:05";
        DateTimeOriginal = "2012:07:14 21:55:05";
        ExifVersion =         (
            2,
            2,
            1
        );
        ExposureMode = 0;
        ExposureProgram = 2;
        ExposureTime = "0.06666667";
        FNumber = "2.8";
        Flash = 24;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLength = "3.85";
        ISOSpeedRatings =         (
            200
        );
        MeteringMode = 5;
        PixelXDimension = 2592;
        PixelYDimension = 1936;
        SceneCaptureType = 0;
        SensingMethod = 2;
        Sharpness = 2;
        ShutterSpeedValue = "3.9112";
        SubjectArea =         (
            1295,
            967,
            699,
            696
        );
        WhiteBalance = 0;
    };
    "{GPS}" =     {
        Altitude = "1167.528";
        AltitudeRef = 0;
        ImgDirection = "278.8303";
        ImgDirectionRef = T;
        Latitude = "15.8235";
        LatitudeRef = S;
        Longitude = "47.99416666666666";
        LongitudeRef = W;
        TimeStamp = "00:55:04.59";
    };
    "{TIFF}" =     {
        DateTime = "2012:07:14 21:55:05";
        Make = Apple;
        Model = "iPhone 4";
        Orientation = 6;
        ResolutionUnit = 2;
        Software = "5.1.1";
        XResolution = 72;
        YResolution = 72;
        "_YCbCrPositioning" = 1;
    };
}
16
Carlos Borges

Nous avons beaucoup travaillé avec la caméra et UIImagePickerController et, du moins jusqu'à iOS 5.1.1 inclus, le système ne renvoie pas de données de localisation dans les métadonnées pour des photos ou des vidéos tournées avec UIImagePickerController

Peu importe que les services de localisation soient activés ou non pour l'application Appareil photo. ceci contrôle l'utilisation des services de localisation par l'application Caméra, et non la fonction de caméra dans UIImagePickerController.

Votre application devra utiliser la classe CLLocation pour obtenir l'emplacement, puis l'ajouter à l'image ou à la vidéo renvoyée par la caméra. Que votre application puisse ou non obtenir l'emplacement dépendra du fait que l'utilisateur autorise ou non l'accès aux services de localisation de votre application. Et notez que l'utilisateur peut désactiver les services de localisation pour votre application (ou entièrement pour l'appareil) à tout moment via les services Settings > Location.

12
Chris Markle

Vous n'utilisez pas les données d'image de la caméra dans le code que vous avez posté, vous en avez généré une représentation JPEG, ce qui éliminerait essentiellement toutes les métadonnées. Utilisez image.CGImage comme suggéré par Caleb.

Également:

Ceci est également pour un produit commercial donc je ne peux pas utiliser http://code.google.com/p/iphone-exif/ .

L'auteur indique assez clairement que les licences commerciales sont disponibles.

4
Jim

Je ne peux pas dire que j’ai besoin de faire exactement cela dans mes propres fichiers, mais dans la documentation, il semble assez clair que si vous utilisez UIImagePickerController, vous pouvez obtenir l’image que l’utilisateur vient de prendre à partir de la méthode de délégation -imagePicker:didFinishPickingMediaWithInfo:. Utilisez la clé UIImagePickerControllerOriginalImage pour obtenir l'image.

Une fois que vous avez obtenu l'image, vous devriez pouvoir accéder à ses propriétés, y compris aux données EXIF, comme décrit dans QA1654 Accès aux propriétés de l'image avec ImageIO . Pour créer le CGImageSource, jetez un œil à CGImageSourceCreateWithData() et utilisez les données que vous obtenez de la méthode CGImage de UIImage. Une fois que vous avez la source de l'image, vous pouvez accéder aux différents attributs via CGImageSourceCopyProperties().

3
Caleb

Comme le souligne Chris Markle , Apple supprime les données GPS de EXIF. Mais vous pouvez ouvrir les données RAW de l'image et les analyser vous-même ou utiliser une bibliothèque tierce pour le faire, pour exemple .

Voici un exemple de code:

- (void) imagePickerController: (UIImagePickerController *) picker
 didFinishPickingMediaWithInfo: (NSDictionary *) info {

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL]
             resultBlock:^(ALAsset *asset) {

                 ALAssetRepresentation *image_representation = [asset defaultRepresentation];
                 NSUInteger size = (NSUInteger)image_representation.size;
                 // create a buffer to hold image data
                 uint8_t *buffer = (Byte*)malloc(size);
                 NSUInteger length = [image_representation getBytes:buffer fromOffset: 0.0  length:size error:nil];

                 if (length != 0)  {

                     // buffer -> NSData object; free buffer afterwards
                     NSData *adata = [[NSData alloc] initWithBytesNoCopy:buffer length:size freeWhenDone:YES];

                     EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
                     [jpegScanner scanImageData: adata];
                     EXFMetaData* exifData = jpegScanner.exifMetaData;

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

                     self.locationLabel.text = [NSString stringWithFormat:@"Local: %@ - %@",latitudeValue,longitudeValue];
                     self.dateLavel.text = [NSString stringWithFormat:@"Data: %@", datetime];

                 }
                 else {
                     NSLog(@"image_representation buffer length == 0");
                 }
             }
            failureBlock:^(NSError *error) {
                NSLog(@"couldn't get asset: %@", error);
            }
     ];
}
1

Dans votre délégué UIImagePickerController, procédez comme suit:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
  NSDictionary *metadata = [info valueForKey:UIImagePickerControllerMediaMetadata];

  // metadata now contains all the image metadata.  Extract GPS data from here.
}
0
rekle

Swift réponse:

import AssetsLibrary
import CoreLocation


// MARK: - UIImagePickerControllerDelegate
extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        defer {
            dismiss(animated: true, completion: nil)
        }
        guard picker.sourceType == .photoLibrary else {
            return
        }
        guard let url = info[UIImagePickerControllerReferenceURL] as? URL else {
            return
        }

        let library = ALAssetsLibrary()
        library.asset(for: url, resultBlock: { (asset) in
            guard let coordinate = asset?.value(forProperty: ALAssetPropertyLocation) as? CLLocation else {
                return
            }
            print("\(coordinate)")

            // Getting human-readable address.
            let geocoder = CLGeocoder()
            geocoder.reverseGeocodeLocation(coordinate, completionHandler: { (placemarks, error) in
                guard let placemark = placemarks?.first else {
                    return
                }
                print("\(placemark.addressDictionary)")
            })
        }, failureBlock: { (error: Error?) in
            print("Unable to read metadata: \(error)")
        })
    }
}
0
Andrey Gordeev

Ceci est testé sur iOS 8 et fonctionne pour les vidéos, il devrait donc fonctionner de la même manière pour les photos avec quelques ajustements.

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    NSURL *videoUrl = (NSURL *)[info objectForKey:UIImagePickerControllerMediaURL];
    NSString *moviePath = [videoUrl path];

    if ( UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(moviePath) ) {

        ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];

        [assetLibrary assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) {

            CLLocation *location = [asset valueForProperty:ALAssetPropertyLocation];
            NSLog(@"Location Meta: %@", location);

        } failureBlock:^(NSError *error) {
            NSLog(@"Video Date Error: %@", error);
        }];

    }

}
0
Nur Iman Izam