Подтвердить что ты не робот

Чтение данных GPS с изображения, возвращаемого камерой в iOS iphone

Мне нужно получить координаты GPS изображения, снятого с камерой устройства iOS. Мне не нравятся изображения Camera Roll, просто изображение, сделанное с помощью UIImagePickerControllerSourceTypeCamera.

Я прочитал много ответов stackoverflow, например Получить данные Exif из UIImage - UIImagePickerController, в котором предполагается, что вы используете инфраструктуру AssetsLibrary, которая, похоже, не работать с изображениями с камеры или использовать CoreLocaiton для получения широты/долготы из самого приложения, а не из изображения.

Использование CoreLocation не является вариантом. Это не даст мне координаты при нажатии кнопки спуска затвора. (При использовании решений на базе CoreLocation вам нужно либо записать координаты, прежде чем вы откроете представление камеры или после, и, конечно, если устройство движется, координаты будут неправильными. Этот метод должен работать со стационарным устройством.)

Я только iOS5, поэтому мне не нужно поддерживать старые устройства. Это также для коммерческого продукта, поэтому я не могу использовать http://code.google.com/p/iphone-exif/.

Итак, каковы мои возможности для чтения данных GPS с изображения, возвращаемого камерой в iOS5? Все, что я могу сейчас подумать, это сохранить изображение в Camera Roll, а затем использовать AssetsLibrary, но это кажется hokey.

Спасибо!


Вот код, который я написал на основе ответа Калеба.

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

и моя консоль показывает:

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

Нет широты/долготы.

4b9b3361

Ответ 1

Одна из возможностей - оставить CoreLocation запущенным, когда камера видна. Запишите каждый CCLocation в массив вместе со временем выборки. Когда фото возвращается, найдите свое время, а затем сопоставьте ближайший CClocation из массива.

Звучит kludgy, но он будет работать.

Ответ 2

Проблема заключается в том, что, поскольку iOS 4 UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; удаляет геолокацию. Чтобы решить эту проблему, вы должны использовать исходный путь фотографии, чтобы получить доступ к метаданным полного изображения. Что-то вроде этого:

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

Результат должен выглядеть примерно так:

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

Ответ 3

Мы много работали с камерой и UIImagePickerController и, по крайней мере, вплоть до iOS 5.1.1, она не возвращает данные о местоположении в метаданных для фотографий или видео, снятых с помощью UIImagePickerController.

Не имеет значения, включена ли служба определения местоположения для приложения "Камера" или нет; это контролирует использование приложения "Использование камеры" в службах определения местоположения, а не функцию камеры в пределах UIImagePickerController.

Ваше приложение должно будет использовать класс CLLocation, чтобы получить местоположение, а затем добавить его к изображению или видео, возвращенному с камеры. Может ли ваше приложение получить местоположение, зависит от того, разрешает ли пользователь доступ к службам местоположения для вашего приложения. Обратите внимание, что пользователь может отключить службы определения местоположения для вашего приложения (или полностью для устройства) в любое время через Settings > Location Сервисы.

Ответ 4

Вы не используете данные изображения из камеры в коде, который вы опубликовали, вы создали его JPEG-представление, которое по существу отменило бы все метаданные. Используйте image.CGImage, как предположил Калеб.

также:

Это также для коммерческого продукта, поэтому я не могу использовать http://code.google.com/p/iphone-exif/.

Автор совершенно четко заявляет, что коммерческое лицензирование доступно.

Ответ 5

Не могу сказать, что мне нужно было делать именно это в моих собственных материалах, но из документов видно, что если вы используете UIImagePickerController, вы можете получить изображение, которое только что взял пользователь из -imagePicker:didFinishPickingMediaWithInfo: делегат. Используйте клавишу UIImagePickerControllerOriginalImage, чтобы получить изображение.

После того, как вы получили изображение, вы сможете получить доступ к его свойствам, включая данные EXIF, как описано в QA1654 Доступ к свойствам изображения с помощью ImageIO. Чтобы создать CGImageSource, я бы посмотрел на CGImageSourceCreateWithData() и использовал данные, полученные из метода UIImage CGImage. Когда у вас есть источник изображения, вы можете получить доступ к различным атрибутам через CGImageSourceCopyProperties().

Ответ 6

Как отмечают Крис Маркл, Apple лишает данные GPS из EXIF. Но вы можете открыть RAW-данные изображения и самостоятельно проанализировать данные или использовать стороннюю библиотеку для этого пример.

Вот пример кода:

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

Ответ 7

В вашем делете UIImagePickerController сделайте следующее:

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

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

Ответ 8

Это проверено на iOS 8 и работает для видео, поэтому оно должно работать аналогично для фотографий с несколькими настройками.

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

    }

}

Ответ 9

Быстрый ответ:

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