我希望在我的应用中用Photos
framework替换ALAssetsLibrary
。
我可以很好地检索照片,收藏品和资源来源(甚至可以将其退回),但无法随时随地访问照片的元数据({Exif},{TIFF}等字典,{GPS}等......)。
ALAssetsLibrary
有办法。 UIImagePickerController
有办法。 Photos
也必须有办法。
我看到PHAsset
有一个location
属性可用于GPS词典,但我希望访问所有元数据,包括面部,方向,曝光,ISO,还有更多。
目前苹果处于测试阶段2.也许还有更多的API?
更新
没有官方的方法只使用照片API。
但是,您可以在下载图像数据后读取元数据。有两种方法可以使用PHImageManager
或PHContentEditingInput
执行此操作。
PHContentEditingInput
方法需要较少的代码,并且不需要您导入ImageIO
。我把它包裹在PHAsset category中。
答案 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!)")
}