来自存储在 iCloud 上的图像的 CGImage 看起来不对
Posted
技术标签:
【中文标题】来自存储在 iCloud 上的图像的 CGImage 看起来不对【英文标题】:CGImage from images stored on iCloud look wrong 【发布时间】:2020-02-16 23:08:58 【问题描述】:我正在使用this answer 中描述的代码从CGImage
中提取像素颜色。
但是,我刚刚意识到,如果我加载在另一台设备上创建的图像,像素值看起来是错误的。第一个明显的问题是 alpha 已经消失了。 CGImageAlphaInfo
报告 .noneSkipLast
,但我知道图像是 RGBA。如果我从创建它的同一设备读取它,它看起来很好。第二个问题是有一些渗色,好像图像已调整大小。也许正在被压缩或其他什么。
这是一个例子:
源图是这个西瓜,12x12
它是在我的 iPad 上创建的。但是,如果我使用我链接的代码通过 iCloud 将它加载到我的 iPhone 上,我会得到:
Alpha 通道消失,颜色溢出。
如果我从同一部 iPhone 使用 Airdrop 将小西瓜发送到我的 Mac,然后再次使用 Airdrop 将其发送回(所以它应该是相同的图像!),现在加载它,我得到正确的图像:
(深棕色区域是 alpha 为 0 的地方)
如果您有几个启用了 iCloud 的 ios 设备,您可以在此应用中重现此行为: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 在下面的 cmets 中建议的那样,我将手机中的照片设置更改为“下载并保留原件”(而不是“优化 iPhone 存储”),这样就解决了问题。所以我想问题是为什么iCloud试图压缩一个只有1355字节的PNG图像,以及是否可以从UIImagePickerController
访问原始图像
【问题讨论】:
“...如果我加载在另一台设备上创建的图像”...您这样做的精确度如何?您在这些设备之间交换什么样的表示? PNG? JPG?归根结底,问题不在于UIImage
与 CGImage
,而是您交换的文件表示/有效负载的排序。
这是我在此处发布的 PNG。 JPG 没有透明度,除非您使用 Jpeg2000。 PNG是使用我链接的应用程序创建的。它在 iPad 上保存到照片文件夹,然后它出现在 iPhone 上,因为我启用了 iCloud 照片。它说:“自动上传所有照片和视频并将其安全地存储在 iCloud 中,以便您可以从任何设备浏览、搜索和共享”。它看起来不像 iCloud 将 PNG 转换为 Jpeg,因为如果我将它从 iPhone 空投到我的 Mac,我可以看到它仍然是 PNG(我的问题上附有小西瓜)。
是的!谢谢 :) 如果我将其设置为“下载并保留原件”,则图像看起来正确。 12x12
西瓜是一个只有 1,355 字节的 PNG,所以我不明白对其应用了什么压缩......不过,Airdrop 似乎可以访问原始图像。如何让我的应用也访问原始版本?
我发现了一个相关的问题,我想,***.com/q/25012731/1765629 -- 我已经用那个链接和一些代码更新了我的问题。为了方便起见,我使用了照片库,我认为这对应用程序的用户来说会是一个更好的体验。该应用程序自 2016 年以来一直存在,我现在才注意到这个问题,所以我想这不是一个主要的不便。不过,很高兴了解发生了什么。
【参考方案1】:
在“设置”应用中,“照片”下方有“优化 iPhone 存储”与“下载并保留原件”选项。后者应该在不以任何方式更改图像的情况下同步照片库。
不过,Airdrop 似乎可以访问原始图像。
Airdrop 不依赖照片库 iCloud 同步来发送图像。这就是为什么资产会保持不变。
如何让我的应用也访问原始版本?
这取决于您是否真的想使用照片库来同步您的资源。这似乎特别真实,因为许多人可能根本不会选择将他们庞大的照片库同步到 iCloud。我不确定你想依赖它。
您可能只想自己使用 CloudKit,而不是依赖照片库。也许您应该在应用程序中有一个选项,以便用户可以选择使用 iCloud 存储,如果是这样,请在您的应用程序中使用 iCloud 存储,而不是依赖照片库。
【讨论】:
【参考方案2】:图像看起来模糊且没有 alpha 的原因似乎是照片默认从 iCloud 流式传输 Jpeg 压缩图像。即使您的原始图像在没有压缩的情况下会更小,就像在这个像素艺术示例中一样。
正如Rob 所指出的,验证是否属于这种情况的一种方法是更改您的照片设置。在“设置”应用中:
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 图像。
【讨论】:
以上是关于来自存储在 iCloud 上的图像的 CGImage 看起来不对的主要内容,如果未能解决你的问题,请参考以下文章