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

Рамки для фотографий iOS 8. Доступ к метаданным фотографий

Я смотрю на замену рамки ALAssetsLibrary на фреймворк в моем приложении. Я могу получить фотографии, коллекции и источники активов просто отлично (даже записать их обратно), но нигде не вижу доступа к метаданным фотографий (словари, такие как {Exif}, {TIFF}, {GPS}, и т.д...). У ALAssetsLibrary есть путь. У UIImagePickerController есть способ. У фотографий тоже должен быть путь.

Я вижу, что у PHASset есть свойство местоположения, которое будет использоваться для словаря GPS, но я ищу доступ ко всем метаданным, включающим лица, ориентацию, экспозицию, ISO и т.д.

В настоящее время яблоко находится в бета-версии 2. Возможно, появилось больше API-интерфейсов?

UPDATE ---------------------------------------------- --------------------

Нет официального способа сделать это, используя только API фотографий. Однако вы можете прочитать метаданные после загрузки данных изображения. Существует несколько способов сделать это, используя PHImageManager или PHContentEditingInput. Метод PHContentEditingInput требует меньше кода и не требует импорта ImageIO. Я завернул его в категории PHAsset

4b9b3361

Ответ 1

Если вы запрашиваете вход для редактирования контента, вы можете получить полное изображение как CIImage, а CIImage имеет свойство под названием properties, которое является словарем, содержащим метаданные изображения.

Пример быстрого кода:

let options = PHContentEditingInputRequestOptions()
options.networkAccessAllowed = true //download asset metadata from iCloud if needed

asset.requestContentEditingInputWithOptions(options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
    let fullImage = CIImage(contentsOfURL: contentEditingInput!.fullSizeImageURL)

    print(fullImage.properties)
}

Пример Objective-C Код:

PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
options.networkAccessAllowed = YES; //download asset metadata from iCloud if needed

[asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
    CIImage *fullImage = [CIImage imageWithContentsOfURL:contentEditingInput.fullSizeImageURL];

    NSLog(@"%@", fullImage.properties.description);
}];

Вы получите желаемые {Exif}, {TIFF}, {GPS} и т.д. словари.

Ответ 2

Мне показалось, что я бы поделился некоторым кодом для чтения метаданных с использованием среды ImageIO в сочетании с фреймворком Photos. Вы должны запросить данные изображения, используя PHCachingImageManager.

@property (strong) PHCachingImageManager *imageManager;

Запросить изображение и использовать его для создания словаря метаданных

-(void)metadataReader{
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:self.myAssetCollection options:nil];
    [result enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndex:myIndex] options:NSEnumerationConcurrent usingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
        [self.imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
            NSDictionary *metadata = [self metadataFromImageData:imageData];
                           NSLog(@"Metadata: %@", metadata.description);
            NSDictionary *gpsDictionary = metadata[(NSString*)kCGImagePropertyGPSDictionary];
            if(gpsDictionary){
                NSLog(@"GPS: %@", gpsDictionary.description);
            }
            NSDictionary *exifDictionary = metadata[(NSString*)kCGImagePropertyExifDictionary];
            if(exifDictionary){
                NSLog(@"EXIF: %@", exifDictionary.description);
            }

            UIImage *image = [UIImage imageWithData:imageData scale:[UIScreen mainScreen].scale];
            // assign image where ever you need...
        }];

    }];
}

Преобразование NSData в метаданные

-(NSDictionary*)metadataFromImageData:(NSData*)imageData{
    CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
    if (imageSource) {
        NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
        CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
        if (imageProperties) {
            NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
            CFRelease(imageProperties);
            CFRelease(imageSource);
            return metadata;
        }
        CFRelease(imageSource);
    }

    NSLog(@"Can't read metadata");
    return nil;
}

Это накладные расходы на захват изображения, поэтому он не так быстро, как перечисление ваших активов или коллекций, но это как минимум.

Ответ 3

PhotoKit ограничивает доступ к метаданным свойствам PHAsset (location, creationDate, favorite, hidden, modificatonDate, pixelWidth, pixelHeight...). Причина (я подозреваю) заключается в том, что из-за введения iCloud PhotoLibrary изображения могут отсутствовать на устройстве. Поэтому все метаданные недоступны. Единственный способ получить полные метаданные EXIF ​​/IPTC - сначала загрузить исходное изображение (если оно недоступно) из iCloud, а затем использовать ImageIO для извлечения его метаданных.

Ответ 4

Лучшее решение, которое я нашел и работал хорошо для меня, это:

[[PHImageManager defaultManager] requestImageDataForAsset:photoAsset
                                                  options:reqOptions
                                            resultHandler:
         ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
             CIImage* ciImage = [CIImage imageWithData:imageData];
             DLog(@"Metadata : %@", ciImage.properties);
         }];

Ответ 5

Вы можете изменить PHAsset (например, добавить метаданные местоположения), используя Photos Framework и метод UIImagePickerControllerDelegate. Отсутствие накладных расходов из сторонних библиотек, создание дубликатов фотографий не производится. Работает для iOS 8.0 +

В методе делегирования didFinishPickingMediaWithInfo вызовите UIImageWriteToSavedPhotosAlbum, чтобы сначала сохранить изображение. Это также создаст PHASset, данные EXIF ​​GPS, которые мы будем изменять:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

    if let myImage = info[UIImagePickerControllerOriginalImage] as? UIImage  {

        UIImageWriteToSavedPhotosAlbum(myImage, self, Selector("image:didFinishSavingWithError:contextInfo:"), nil)
    }    
}

Функция селектора завершения будет запускаться после завершения сохранения или сбоя с ошибкой. В обратном вызове выберите вновь созданный PHAsset. Затем создайте файл PHAssetChangeRequest для изменения метаданных местоположения.

func image(image: UIImage, didFinishSavingWithError: NSErrorPointer, contextInfo:UnsafePointer<Void>)       {

    if (didFinishSavingWithError != nil) {
        print("Error saving photo: \(didFinishSavingWithError)")
    } else {
        print("Successfully saved photo, will make request to update asset metadata")

        // fetch the most recent image asset:
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
        let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)

        // get the asset we want to modify from results:
        let lastImageAsset = fetchResult.lastObject as! PHAsset

        // create CLLocation from lat/long coords:
        // (could fetch from LocationManager if needed)
        let coordinate = CLLocationCoordinate2DMake(myLatitude, myLongitude)
        let nowDate = NSDate()
        // I add some defaults for time/altitude/accuracies:
        let myLocation = CLLocation(coordinate: coordinate, altitude: 0.0, horizontalAccuracy: 1.0, verticalAccuracy: 1.0, timestamp: nowDate)

        // make change request:
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({

            // modify existing asset:
            let assetChangeRequest = PHAssetChangeRequest(forAsset: lastImageAsset)
            assetChangeRequest.location = myLocation

            }, completionHandler: {
                (success:Bool, error:NSError?) -> Void in

                if (success) {
                    print("Succesfully saved metadata to asset")
                    print("location metadata = \(myLocation)")
                } else {
                    print("Failed to save metadata to asset with error: \(error!)")
}