Сохраните метаданные exif с помощью новой библиотеки PHPhotoLibrary.

В настоящее время я использую AVCaptureStillImageOutput, чтобы получить изображение с полным разрешением. Я также могу получить метаданные exif, используя следующий код:

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
     { 

         CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
         CFMutableDictionaryRef mutableDict = CFDictionaryCreateMutableCopy(NULL, 0, metaDict);

         NSLog(@"test attachments %@", mutableDict);

         // set the dictionary back to the buffer
         CMSetAttachments(imageSampleBuffer, mutableDict, kCMAttachmentMode_ShouldPropagate);

         NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];

         UIImage *image = [[UIImage alloc] initWithData:imageData];    
         [self.delegate frameReadyToSave:image withExifAttachments: mutableDict];
     }];

Метаданные находятся в переменной mutableDict. Теперь я хочу сохранить это изображение в двух разных местах с метаданными. Хочу сохранить на диске в папках приложений и в библиотеке фотографий.

Теперь я попытался сохранить изображение другим способом, используя следующее (переменная изображения, которую вы видите, является настраиваемым объектом):

NSData* imageData = UIImageJPEGRepresentation(image.image, 1.0f);

[imageData writeToFile:image.filePath atomically:YES];

UIImageWriteToSavedPhotosAlbum(image.image, nil, nil, nil);

Теперь изображение правильно сохранено, но не содержит метаданных Exif.

Из того, что я прочитал, для этого мне нужно использовать PHPhotoLibrary, но документация по этому поводу не слишком обширна. Вот что я нашел:

[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image.image];      

} completionHandler:nil];

Но как мне сохранить с ним метаданные?


person Etienne Noël    schedule 02.03.2016    source источник


Ответы (2)


Я бы посоветовал вам использовать ImageIO для этого:

-(void)frameReadyToSave:(UIImage*)image withExifAttachments:(NSMutableDictionary*)mutableDict
{
    NSData* imageData = UIImageJPEGRepresentation(image, 1.0f);
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
    __block NSURL* tmpURL = [NSURL fileURLWithPath:@"example.jpg"]; //modify to your needs
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef) tmpURL, kUTTypeJPEG, 1, NULL);
    CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) mutableDict);
    CGImageDestinationFinalize(destination);
    CFRelease(source);
    CFRelease(destination);
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:tmpURL];
    }   completionHandler:^(BOOL success, NSError *error) {
        //cleanup the tmp file after import, if needed
    }];
}
person holtmann    schedule 13.03.2016
comment
Это сохраняет метаданные в EXIF-части JPEG, но они не отображаются в фотографиях (на Mac). - person Ortwin Gentz; 03.05.2016
comment
[PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL: tmpURL] должен при импорте считывать данные метаданных EXIF ​​фотографии и заполнять базу данных библиотеки фотографий iCloud необходимыми данными (дата записи, данные о местоположении). Отображается ли фотография в приложении iOS Photos с правильной меткой времени? - person holtmann; 10.05.2016
comment
Действительно, это работает для даты и местоположения. Однако это не работает для заголовка, описания и ключевых слов. По крайней мере, с полями EXIF, которые я пробовал. - person Ortwin Gentz; 10.05.2016
comment
Заголовок, описание и ключевые слова не являются частью базы данных библиотеки фотографий iOS (и не редактируются в iOS - ни в приложении «Фотографии», ни в качестве свойств PHAsset). Правильно, что эти свойства можно редактировать с помощью приложения «Фотографии» на Mac. Заголовок, описание и ключевые слова, похоже, не синхронизируются через iCloud (пока). - person holtmann; 10.05.2016
comment
Нет ли возможности сохранить фотографию прямо в альбоме камеры без предварительного создания локального файла? - person Deepak Sharma; 13.05.2017
comment
Кто-нибудь нашел для этого решение? и если да, то каким способом можно сохранить изображение с новыми метаданными как uiimage перед сохранением его в библиотеке фотографий? Спасибо - person Reza.Ab; 26.01.2018

Используйте это, чтобы объединить метаданные с данными изображения и сохранить их в библиотеке фотографий:

func saveImageData(data: Data, metadata: NSDictionary? = nil, album: PHAssetCollection, completion:((PHAsset?)->())? = nil) {
    var placeholder: PHObjectPlaceholder?

    PHPhotoLibrary.shared().performChanges({
        var changeRequest: PHAssetChangeRequest

        if let metadata = metadata {
            let newImageData = UIImage.mergeImageData(imageData: data, with: metadata)
            changeRequest = PHAssetCreationRequest.forAsset()
            (changeRequest as! PHAssetCreationRequest).addResource(with: .photo, data: newImageData as Data, options: nil)
        }
        else {
            changeRequest = PHAssetChangeRequest.creationRequestForAsset(from: UIImage(data: data)!)
        }

        guard let albumChangeRequest = PHAssetCollectionChangeRequest(for: album),
            let photoPlaceholder = changeRequest.placeholderForCreatedAsset else { return }

        placeholder = photoPlaceholder
        let fastEnumeration = NSArray(array: [photoPlaceholder] as [PHObjectPlaceholder])
        albumChangeRequest.addAssets(fastEnumeration)
    }, completionHandler: { success, error in
        guard let placeholder = placeholder else {
            completion?(nil)
            return
        }

        if success {
            let assets:PHFetchResult<PHAsset> =  PHAsset.fetchAssets(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
            let asset:PHAsset? = assets.firstObject
            completion?(asset)
        }
        else {
            completion?(nil)
        }
    })
}

func mergeImageData(imageData: Data, with metadata: NSDictionary) -> Data {
    let source: CGImageSource = CGImageSourceCreateWithData(imageData as NSData, nil)!
    let UTI: CFString = CGImageSourceGetType(source)!
    let newImageData =  NSMutableData()
    let cgImage = UIImage(data: imageData)!.cgImage
    let imageDestination: CGImageDestination = CGImageDestinationCreateWithData((newImageData as CFMutableData), UTI, 1, nil)!
    CGImageDestinationAddImage(imageDestination, cgImage!, metadata as CFDictionary)
    CGImageDestinationFinalize(imageDestination)

    return newImageData as Data
}
person Clouds    schedule 21.06.2018
comment
(changeRequest как! PHAssetCreationRequest) .addResource (‹---------------- это помогает! - person Amos; 16.09.2018