ios - 将更新的图像保存到 PhotoKit 时缺少图像元数据

标签 ios swift3 core-graphics photosframework

我在更新图像的元数据并将其保存回照片库时遇到问题。一切工作除了图像元数据在它被改变之后缺少之前存在的条目并且我在处理图像或执行照片库更改 block 时没有收到任何错误。此外,在将字典写回图像之前,字典看起来就像是原始字典加上我在调试器中的字典。

我的问题是:

  1. 我做错了什么会导致重写现有的 带附加数据的属性回来抹去有什么?
  2. 是否有更好、更规范的方法来做到这一点?更新图像中的某些元数据似乎需要很多技巧。好像 其他人都在做什么。

编辑:

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

["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 上测试

基本工作流程是:

    // 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 的扩展,而不是 this Gist 的 Swift 3 版本。

最佳答案

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

  1. 从包含 EXIF 或 GPS 数据的数据构建 UIImage 删除该数据...事实上,它删除了大部分的元数据 除了一组核心的 JFIF 和大小数据(使用 JPEG 时)。回想起来这是有道理的,因为它们的内部表示只是原始数据。但是,我没有在文档中找到任何关于元数据的明确声明。
  2. 鉴于前面的观点,将数据(带有元数据的图像)对象放入其中的主要两种方法 照片库要么将其写入临时文件 并阅读它 PHAssetChangeRequest::creationRequestForAssetFromImage(atFileURL:) 或使用 PHAssetCreationRequest::forAsset 创建创建请求 然后使用 PHAssetCreationRequest::addResource(with:data:options:) 将数据添加为照片。我选择了后者,因为它移动较少 部分。

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

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

    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 ...

关于ios - 将更新的图像保存到 PhotoKit 时缺少图像元数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41169156/

相关文章:

ios - 如何使用新版 ADALiOS 2.1 注销

iphone - 这是否高效 - 向每个 UITableViewCell 添加手势识别器

iOS 11 UITableView isEditing 标志设置不正确

ios - 在 swift 3 中按钮位置变化出错

ios - Nativescript - 如何正确使用 ui/image-cache 中的占位符属性使其工作?

ios - 快速从结构数据中提取字节

swift - 使用 UIButtons 在放大的 UIImageView 中左、右、上、下移动

ios - 多点触控跟踪问题

ios - 检测图像 ios 上的黑线

ios - iOS 应用内购买的沙盒测试帐户反复询问密码