iOS 8 照片框架。访问照片元数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24462112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iOS 8 Photos framework. Access photo metadata
提问by VaporwareWolf
I'm looking at replacing ALAssetsLibrary
with Photos
frameworkin my app.
我期待在更换ALAssetsLibrary
与Photos
框架在我的应用程序。
I can retrieve photos, collections, and asset sources just fine (even write them back out), but don't see anywhere to access the metadata of the photos (the dictionaries such as {Exif}, {TIFF}, {GPS}, etc...).
我可以很好地检索照片、收藏和资产来源(甚至将它们写回),但看不到任何地方可以访问照片的元数据(例如 {Exif}、{TIFF}、{GPS}、等等...)。
ALAssetsLibrary
has a way. UIImagePickerController
has a way. Photos
must have a way too.
ALAssetsLibrary
有办法。UIImagePickerController
有办法。Photos
也一定有办法。
I see that PHAsset
has a location
property which will do for the GPS dictionary, but I'm looking to access all of the metadata which include faces, orientation, exposure, ISO, and tons more.
我看到它PHAsset
有一个location
可以用于 GPS 字典的属性,但我希望访问所有元数据,包括面部、方向、曝光、ISO 等等。
Currently apple is at beta 2. Perhaps there are more APIs to come ?
目前苹果处于 beta 2。也许会有更多的 API 来?
UPDATE
更新
There is no official way to do this using only Photos APIs.
没有官方的方法可以只使用照片 API 来做到这一点。
However you can read the metadata after you download the image data. There are a couple of methods to do this using either PHImageManager
or PHContentEditingInput
.
但是,您可以在下载图像数据后读取元数据。有几种方法可以使用PHImageManager
或来做到这一点PHContentEditingInput
。
The PHContentEditingInput
method required less code and doesn't require you to import ImageIO
. I've wrapped it up in a PHAsset category.
该PHContentEditingInput
方法需要较少的代码,并且不需要您导入ImageIO
. 我已经将它包含在PHAsset 类别中。
回答by Jordan H
If you request a content editing input, you can get the full image as a CIImage
, and CIImage
has a property titled properties
which is a dictionary containing the image metadata.
如果您请求内容编辑输入,您可以获得完整图像作为CIImage
,并且CIImage
有一个属性properties
,它是一个包含图像元数据的字典。
Sample Swift Code:
示例 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)
}
Sample Objective-C Code:
示例 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);
}];
You'll get the desired {Exif}, {TIFF}, {GPS}, etc dictionaries.
您将获得所需的 {Exif}、{TIFF}、{GPS} 等字典。
回答by VaporwareWolf
I thought I'd share some code to read the metadata using the ImageIO framework in conjunction with Photos framework. You must request the image data using a PHCachingImageManager.
我想我会分享一些代码来使用 ImageIO 框架和照片框架来读取元数据。您必须使用 PHCachingImageManager 请求图像数据。
@property (strong) PHCachingImageManager *imageManager;
Request the image and use it's data to create a metadata dictionary
请求图像并使用它的数据来创建元数据字典
-(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...
}];
}];
}
Convert NSData to metadata
将 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;
}
This has the overhead of grabbing the image, so it's not nearly as fast as enumerating your assets or collections, but it's something at least.
这有抓取图像的开销,所以它不像枚举你的资产或集合那么快,但至少它是一些东西。
回答by lbsweek
I prefer not to CIImage solution, but to ImageIO solution:
我不喜欢 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)
}
below is code to get data from PHAseet
下面是从 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) = imageAndMetadataFromImageData(data: data as NSData)
//image = UIImage(data: data)
}
}
// here to return image and meta
}
回答by holtmann
PhotoKit limits the access to metadata to the properties of PHAsset (location, creationDate, favorite, hidden, modificatonDate, pixelWidth, pixelHeight...). The reason (I suspect) is that due to the introduction of iCloud PhotoLibrary the images may not be on the device. Therefore the whole metadata is not available. The only way to get full EXIF/IPTC metadata is to first download the original image (if not available) from iCloud and then use ImageIO to extract its metadata.
PhotoKit 将对元数据的访问限制为 PHAsset 的属性(位置、创建日期、收藏夹、隐藏、修改日期、像素宽度、像素高度...)。原因(我怀疑)是由于 iCloud PhotoLibrary 的引入,图像可能不在设备上。因此,整个元数据不可用。获取完整 EXIF/IPTC 元数据的唯一方法是首先从 iCloud 下载原始图像(如果不可用),然后使用 ImageIO 提取其元数据。
回答by Karthik
Better solution i found and worked well for me is:
我发现并为我工作的更好的解决方案是:
[[PHImageManager defaultManager] requestImageDataForAsset:photoAsset
options:reqOptions
resultHandler:
^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
CIImage* ciImage = [CIImage imageWithData:imageData];
DLog(@"Metadata : %@", ciImage.properties);
}];
回答by kev8484
You can modify the PHAsset (e.g. adding location metadata) using Photos Framework and the UIImagePickerControllerDelegate method. No overhead from third party libraries, no duplicate photos created. Works for iOS 8.0+
您可以使用照片框架和 UIImagePickerControllerDelegate 方法修改 PHAsset(例如添加位置元数据)。没有来自第三方库的开销,没有创建重复的照片。适用于 iOS 8.0+
In the didFinishPickingMediaWithInfo delegate method, call UIImageWriteToSavedPhotosAlbum to first save the image. This will also create the PHAsset whose EXIF GPS data we will modify:
在 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)
}
}
The completion selector function will run after the save completes or fails with error. In the callback, fetch the newly created PHAsset. Then, create a PHAssetChangeRequest to modify the location metadata.
完成选择器函数将在保存完成或因错误而失败后运行。在回调中,获取新创建的 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!)")
}