iOS 8照片框架。访问照片元数据

时间:2014-06-27 23:31:13

标签: ios iphone uiimageview alassetslibrary ios8

我希望在我的应用中用Photos framework替换ALAssetsLibrary

我可以很好地检索照片,收藏品和资源来源(甚至可以将其退回),但无法随时随地访问照片的元数据({Exif},{TIFF}等字典,{GPS}等......)。

ALAssetsLibrary有办法。 UIImagePickerController有办法。 Photos也必须有办法。

我看到PHAsset有一个location属性可用于GPS词典,但我希望访问所有元数据,包括面部,方向,曝光,ISO,还有更多。

目前苹果处于测试阶段2.也许还有更多的API?

更新

没有官方的方法只使用照片API。

但是,您可以在下载图像数据后读取元数据。有两种方法可以使用PHImageManagerPHContentEditingInput执行此操作。

PHContentEditingInput方法需要较少的代码,并且不需要您导入ImageIO。我把它包裹在PHAsset category中。

6 个答案:

答案 0 :(得分:34)

如果您请求内容编辑输入,则可以将完整图像作为CIImage获取,CIImage具有标题为properties的属性,该属性是包含图像元数据的字典。< / p>

示例Swift代码:

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}等词典。

答案 1 :(得分:7)

我以为我会分享一些代码来使用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;
}

这具有抓取图像的开销,因此它不像枚举资产或集合那么快,但它至少是这样的。

答案 2 :(得分:5)

PhotoKit将对元数据的访问限制为PHAsset的属性(location,creationDate,favorite,hidden,modificatonDate,pixelWidth,pixelHeight ......)。原因(我怀疑)是由于引入了iCloud PhotoLibrary,图像可能不在设备上。因此,整个元数据不可用。 获取完整EXIF / IPTC元数据的唯一方法是首先从iCloud下载原始图像(如果不可用),然后使用ImageIO提取其元数据。

答案 3 :(得分:5)

我发现并且对我有用的更好的解决方案是:

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

答案 4 :(得分:4)

我不喜欢CIImage解决方案,而不是ImageIO解决方案:

func imageAndMetadataFromImageData(data: NSData)-> (UIImage?,[String: Any]?) {
    let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse]
    if let imgSrc = CGImageSourceCreateWithData(data, options as CFDictionary) {
        let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options as CFDictionary) as! [String: Any]
        //print(metadata)
        // let image = UIImage(cgImage: imgSrc as! CGImage)
        let image = UIImage(data: data as Data)
        return (image, metadata)
    }
    return (nil, nil)
}

下面是从PHAseet获取数据的代码

func getImageAndMeta(asset: PHAsset){
    let options = PHImageRequestOptions()
    options.isSynchronous = true
    options.resizeMode = .none
    options.isNetworkAccessAllowed = false
    options.version = .current
    var image: UIImage? = nil
    var meta:[String:Any]? = nil
    _ = PHCachingImageManager().requestImageData(for: asset, options: options) { (imageData, dataUTI, orientation, info) in
        if let data = imageData {
            (image, meta) = metadataFromImageData(data: data as NSData)
            //image = UIImage(data: data)
        }
    }
    // here to return image and meta
}

答案 5 :(得分:0)

您可以使用Photos Framework和UIImagePickerControllerDelegate方法修改PHAsset(例如添加位置元数据)。第三方库没有开销,没有创建重复的照片。适用于iOS 8.0 +

在didFinishPickingMediaWithInfo委托方法中,调用UIImageWriteToSavedPhotosAlbum以首先保存图像。这也将创建我们将修改其EXIF GPS数据的PHAsset:

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