存储在 iCloud 上的图像中的 CGImage 看起来不对

CGImage from images stored on iCloud look wrong

我正在使用 this answer 中描述的代码从 CGImage 中提取像素颜色。

但是,我刚刚意识到,如果我加载在另一台设备上创建的图像,像素值看起来是错误的。第一个明显的问题是阿尔法消失了。 CGImageAlphaInfo 报告 .noneSkipLast,但我知道图像是 RGBA。如果我从创建它的同一个设备读取它,它看起来很好。第二个问题是有一些颜色渗色,就好像图像被调整过大小一样。可能是被压缩了什么的。

这是一个例子:

源图是这个西瓜,12x12

它是在我的 iPad 上创建的。但是如果我使用我链接的代码通过 iCloud 将它加载到我的 iPhone 上,我会得到这个:

Alpha 通道消失,颜色渗出。

如果来自同一个 iPhone 我使用 Airdrop 将小西瓜发送到我的 Mac,然后再次使用 Airdrop 将其发送回去(所以它应该是相同的图像!),然后加载它现在,我得到了正确的图像:

(深棕色区域是 alpha 为 0 的地方)

如果您有几台 iOS 设备启用了 iCloud,您可以在此应用程序中重现此行为:http://swiftpixels.endavid.com 我正在使用该代码读取像素颜色的地方。

这些图像之间有什么区别?如何从 iCloud 读取正确的图像?我应该在 UIImage 而不是 CGImage 中寻找提示吗?

有线索吗?谢谢!

更新

作为参考,我正在使用 UIImagePickerController 读取图像,使用此代码:

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            loadImageInMemory(image)
        }
        picker.dismiss(animated: true, completion: nil)
        self.presentingViewController?.dismiss(animated: true, completion: nil)
    }

    fileprivate func loadImageInMemory(_ image: UIImage) {
       /// skipping some preparation
       guard let cgImage = image.cgImage else {
          return
       }
       /// getImageData is a function like getPixelColor linked above,
       /// but for a Rect 
       self.imageData = cgImage.getImageData(rect, width, height)
    }

我也发现了这个可能相关的问题:UIImagePickerController and iCloud photos

正如 Rob 在下面的评论中所建议的,我将 phone 中的照片设置更改为 "Download and Keep Originals"(而不是 "Optimize iPhone Storage"),这解决了问题。所以我想问题是为什么 iCloud 会尝试压缩只有 1,355 字节的 PNG 图像,以及是否可以从 UIImagePickerController

访问原始图像

在“设置”应用中,“照片”下有一个“优化 iPhone 存储”与“下载并保留原件”选项。后者应该在不以任何方式更改图像的情况下同步照片库。

It seems Airdrop can access the original image, though.

A​​irdrop 不依赖照片库 iCloud 同步来发送图像。这就是资产原封不动地传播的原因。

How can I make my app access the original as well?

这取决于您是否真的想使用照片库来同步您的资产。这似乎尤其如此,因为许多人可能根本不会选择将他们庞大的照片库同步到 iCloud。我不确定你想依赖它。

您可能只想自己使用 CloudKit 而不是依赖照片库。也许您应该在应用程序中有一个选项,以便用户可以选择使用 iCloud 存储,如果是这样,请在您的应用程序中使用 iCloud 存储,而不是依赖照片库。

图像看起来模糊且没有 alpha 的原因似乎是照片默认从 iCloud 流式传输 Jpeg 压缩图像。即使您的原始图像在没有压缩的情况下会更小,就像在这个像素艺术示例中一样。

正如 所指出的,验证是否属于这种情况的一种方法是更改​​您的照片设置。来自“设置”应用:

Settings -> Photos -> Download and Keep Originals

这可以解决问题,但当然不是我们所希望的。如果您想继续使用照片,而不是实施您自己的 iCloud 解决方案,同时保持 Optimize iPhone Storage 设置,您可以使用 PhotoKit 检索原始图像。

替换此代码,

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            loadImageInMemory(image)
        }
        picker.dismiss(animated: true, completion: nil)
        self.presentingViewController?.dismiss(animated: true, completion: nil)
    }

通过其他代码:

import Photos

// ...

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        // it will be loaded asynchronously
        loadImageFromPicker(info: info)
        picker.dismiss(animated: true, completion: nil)
        self.presentingViewController?.dismiss(animated: true, completion: nil)
    }

    private func loadImageFromPicker(info: [UIImagePickerController.InfoKey : Any]) {
        var phAsset: PHAsset?
        if #available(iOS 11.0, *) {
            phAsset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset
        } else {
            // Fallback on earlier versions
            if let referenceURL = info[UIImagePickerController.InfoKey.referenceURL] as? URL {
                let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
                phAsset = fetchResult.firstObject
            }
        }
        guard let asset = phAsset else {
            return
        }
        // size doesn't matter, because resizeMode = .none
        let size = CGSize(width: 32, height: 32)
        let options = PHImageRequestOptions()
        options.version = .original
        options.deliveryMode = .highQualityFormat
        options.resizeMode = .none
        options.isNetworkAccessAllowed = true
        PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFit, options: options) { [weak self] (image, info) in
            if let s = self, let image = image {
                s.loadImageInMemory(image)
            }
        }
    }

此代码适用于本地图片和 iCloud 图片。