将更新的图像保存到 PhotoKit 中时缺少图像元数据 [英] Missing image metadata when saving updated image into PhotoKit

查看:39
本文介绍了将更新的图像保存到 PhotoKit 中时缺少图像元数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在更新图像的元数据并将其保存回照片库时遇到问题.一切有效,除了更改后的图像元数据丢失了之前存在的条目,并且我在操作图像或执行照片库更改块时没有遇到任何错误.此外,在将其写回图像之前的字典看起来像原始字典加上我在调试器中的字典.

I am having an issue updating an image's metadata and saving that back to the Photos library. Everything works except that the image metadata after it's altered has missing entries that were there before and I am not getting any errors manipulating the image or executing the photos library change block. Also, the dictionary before it's written back into the image looks like the original plus my dictionary in the debugger.

我的问题是:

  1. 我做错了什么会导致重写现有的属性回来用额外的数据来消灭有什么?
  2. 有没有更好、更规范的方法来做到这一点?更新图像中的一些元数据似乎有很多机制.看起来就像其他人都在做什么.

在保存之前,所有 Exif 和 Tiff 值都存在.这是使用以下代码保存到照片后的全部元数据:

Before the save all the Exif and Tiff values are present. This is the entirety of the metadata after the save to photos using the code below:

["PixelHeight": 2448, "PixelWidth": 3264, "{Exif}": {
ColorSpace = 1;
PixelXDimension = 3264;
PixelYDimension = 2448;}, "Depth": 8, "ProfileName": sRGB IEC61966-2.1, "Orientation": 1, "{TIFF}": {
Orientation = 1;}, "ColorModel": RGB, "{JFIF}": {
DensityUnit = 0;
JFIFVersion =     (
    1,
    0,
    1
);
XDensity = 72;
YDensity = 72;}]

代码全部在 Swift 3 中,在 iOS 10.1 上测试

The code, all in Swift 3, testing on iOS 10.1

基本工作流程是:

    // Get a mutable copy of the existing Exif meta
    let mutableMetaData = getMutableMetadataFrom(imageData: data)

    // Check to see if it has the {GPS} entry, if it does just exit.
    if let _ = mutableMetaData[kCGImagePropertyGPSDictionary as String] {
       callback(imageAsset, true, nil)
       return
    }

    // Add the {GPS} tag to the existing metadata
    let clLocation = media.location!.asCLLocation()
    mutableMetaData[kCGImagePropertyGPSDictionary as String] =
       clLocation.asGPSMetaData()

    // Attach the new metadata to the existing image
    guard let newImageData = attach(metadata: mutableMetaData, toImageData: data) else {
            callback(imageAsset, false, nil)
            return
    }

    let editingOptions = PHContentEditingInputRequestOptions()
    imageAsset.requestContentEditingInput(with: editingOptions) { editingInput, info in
        guard let editingInput = editingInput else { return }
        let library = PHPhotoLibrary.shared()
        let output = PHContentEditingOutput(contentEditingInput: editingInput)
        output.adjustmentData = PHAdjustmentData(formatIdentifier: "Project", formatVersion: "0.1",
                                                 data: "Location Adjustment".data(using: .utf8)!)
        do {
            try newImageData.write(to: output.renderedContentURL, options: [.atomic])
        } catch {
            callback(imageAsset, false, error)
            return
        }

        library.performChanges({
            let changeRequest = PHAssetChangeRequest(for: imageAsset)
            changeRequest.location = clLocation
            changeRequest.contentEditingOutput = output

        }, completionHandler: { success, error in ... ...

工作流的辅助方法是:

func attach(metadata: NSDictionary, toImageData imageData:Data) -> Data? {

    guard
        let imageDataProvider = CGDataProvider(data: imageData as CFData),
        let cgImage = CGImage(jpegDataProviderSource: imageDataProvider, decode: nil,
                              shouldInterpolate: true, intent: .defaultIntent),
        let newImageData = CFDataCreateMutable(nil, 0),
        let type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
                                                         "image/jpg" as CFString, kUTTypeImage),
        let destination = CGImageDestinationCreateWithData(newImageData,
                                                           (type.takeRetainedValue()), 1, nil) else {

            return nil
        }

    CGImageDestinationAddImage(destination, cgImage, metadata as CFDictionary)
    CGImageDestinationFinalize(destination)

    guard
        let newProvider = CGDataProvider(data: newImageData),
        let newCGImage = CGImage(jpegDataProviderSource: newProvider, decode: nil,
                                 shouldInterpolate: false, intent: .defaultIntent) else {

            return nil
    }

    return UIImageJPEGRepresentation(UIImage(cgImage: newCGImage), 1.0)
}

func getMutableMetadataFrom(imageData data : Data) -> NSMutableDictionary {

    let imageSourceRef = CGImageSourceCreateWithData(data as CFData, nil)
    let currentProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef!, 0, nil)
    let mutableDict = NSMutableDictionary(dictionary: currentProperties!)

    return mutableDict
}

此外,asGPSMetaDataCLLocation 的扩展,看起来是 这个要点

Also the asGPSMetaData is an extension on CLLocation than looks a Swift 3 version of this Gist

推荐答案

事实证明,问题根本不是 CoreGraphics 对图像或元数据的处理,而是我忽略的几件事:

It turns out that it was not the manipulation of the image or metadata with CoreGraphics that was the issue at all, it was a couple of things I overlooked:

  1. 从包含 EXIF 或 GPS 数据的数据构建 UIImage删除该数据...事实上,它删除了大部分元数据除了一组核心 JFIF 和大小数据(使用 JPEG 时).回想起来这是有道理的,因为它们的内部表示只是原始数据.但是,我没有在文档中找到任何关于元数据的明确声明.
  2. 鉴于前面的观点,获取数据(带有元数据的图像)对象的主要两种方法照片库要么将其写入临时文件并阅读它PHAssetChangeRequest::creationRequestForAssetFromImage(atFileURL:)或使用 PHAssetCreationRequest::forAsset 创建创建请求然后使用 PHAssetCreationRequest::addResource(with:data:options:)将数据添加为照片.我选择了后者,因为它移动较少部分.
  1. Constructing a UIImage from data that contains EXIF or GPS data removes that data... in fact, it removes most of the meta data except a core set of JFIF and size data (when using JPEG). It makes sense in retrospect as their internal representation would be just raw data. However, I did not find any explicit statement about metadata in the docs.
  2. Given the prior point, the main two ways to get a Data (image with metadata) object into the Photos library was either to write it to a temp file and read it in with PHAssetChangeRequest::creationRequestForAssetFromImage(atFileURL:) or use PHAssetCreationRequest::forAsset to create a creation request and then use PHAssetCreationRequest::addResource(with:data:options:) to add the data as a photo. I chose the latter as it had less moving parts.

所以我想所有这些都取代了漂亮、简洁的ALAssetsLibrary::writeImage(toSavedPhotosAlbum:metadata:completionBlock:).

So I guess all that replaces the nice, succinct ALAssetsLibrary::writeImage(toSavedPhotosAlbum:metadata:completionBlock:).

照片库的最终更改块是这样的:

The final change block for the Photos library ended up being this:

    var assetID: String?
    PHPhotoLibrary.shared().performChanges({ 
        let creationRequest = PHAssetCreationRequest.forAsset()
        creationRequest.addResource(with: .photo, data: imageWithMetaData as Data, options: nil)
        creationRequest.location = clLocation
        assetID = creationRequest.placeholderForCreatedAsset?.localIdentifier
    }) { success, error  in ...

这篇关于将更新的图像保存到 PhotoKit 中时缺少图像元数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆