【问题标题】:UICollectionView bad performance with UIImageViews with Core Image-manipulated imagesUICollectionView 使用带有核心图像处理图像的 UIImageViews 性能不佳
【发布时间】:2013-08-19 09:48:49
【问题描述】:

我的应用程序中有一个UICollectionView,其单元格主要由UIImageViews 组成,其中包含已使用Core Image 处理以降低色彩饱和度的图像。滚动时的性能绝对可怕。当我分析它时,花费的大部分时间(80% 左右)不在我的代码中,甚至不在我的堆栈中。这一切似乎都在核心动画代码中。有人知道为什么会这样吗?

在我的 UICollectionViewCell 子类中,我有这样的东西:

UIImage *img = [self obtainImageForCell];
img = [img applySaturation:0.5];
self.imageView.image = img;

applySaturation 看起来像这样:

CIImage *image = [CIImage imageWithCGImage:self.CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"];
[filter setValue:image forKey:kCIInputImageKey];
[filter setValue:[NSNumber numberWithFloat:saturation] forKey:@"inputSaturation"];
return [UIImage imageWithCIImage:filter.outputImage];

我唯一的猜测是 Core Animation 不能很好地与 Core Image 配合使用。 Apple docs 这么说CIImage

虽然 CIImage 对象具有与之关联的图像数据,但它不是图像。您可以将 CIImage 对象视为图像“配方”。 CIImage 对象拥有生成图像所需的所有信息,但 Core Image 在被告知之前不会真正渲染图像。这种“惰性评估”方法可以让 Core Image 尽可能高效地运行。

在制作动画时在最后一刻进行此评估可能会很棘手。

【问题讨论】:

  • 您是否尝试过一次过滤并缓存结果?
  • 我看到同样缓慢的滚动行为。

标签: ios performance uicollectionview core-image


【解决方案1】:

我遇到了完全相同的问题,通过在单元格更新期间避免触发 Core Image 过滤器来解决。

我认为,Apple 文档中有关惰性评估/配方的内容更针对您可以非常有效地将核心图像过滤器链接在一起的想法。但是,当您要显示核心图像过滤器链的结果时,需要对事物进行评估,如果在快速滚动视图期间出现“当时和那里”并且过滤器在问题需要大量处理(其中许多都需要)。

您可以尝试摆弄 GPU 与 CPU 处理,但我发现将图像数据移入和移出 CIImage 的开销可能更大(请参阅我的回答 here

我的建议是将这种处理方式与处理在线图像填充滚动视图的方式相同 - 即异步处理、使用占位符(例如预处理的图像)以及缓存结果以供重复使用。

更新

回复您的评论:

从 CIImage 中提取数据时会应用适用的过滤器 - 例如,imageWithCIImage: [警告 - 这是我的推论,我没有测试过]。

但这不是您的问题...您需要在后台线程上处理图像,因为处理需要时间来阻止滚动。同时在滚动单元格中显示其他内容,例如平面颜色或 - 更好 - 您正在输入 CIImage 进行过滤的 UIImage。处理完成后更新单元格(检查它是否仍需要更新,到那时它可能已经滚动到屏幕外)。将过滤后的图像保存在某种持久性存储中,这样您就不需要再次过滤它,并在需要再次显示图像时检查缓存,然后从头开始重新处理。

【讨论】:

  • @Mr.Jefferson,查看我的扩展回复
  • 我已经在使用imageWithCIImage。我应用饱和度的方法返回 UIImage。当我分析应用程序时,我的代码不在繁重的堆栈跟踪中;看起来核心动画必须在滚动视图滚动时应用图像“配方”。
  • @Mr.Jefferson 我可以看到您正在使用问题中的代码中的imageWithCIImage ...正如我所说 - 除非您从缓存中读取图像并/或占位符,必须在显示结果图像之前应用配方。在您的情况下,当您尝试使用 imageWithCIImage 从 CIImage 中提取数据时会发生这种情况。不要被堆栈跟踪细节分心——你只需要知道应用配方是低效的,你应该只做一次,而不是在主线程上做。
  • 您确定imageWithCIImage 是实际应用配方的内容吗?性能跟踪似乎表明它是在 UIImageView 绘制自身时完成的。
  • @TomHamming 是正确的,imageWithCIImage 没有应用配方。您需要强制 CoreGraphics 进行渲染,请参阅上面的答案。
【解决方案2】:

我也有这个确切的问题 - 一直想要去饱和图像! – 过滤一次并缓存结果(即使作为 UIImage)也无济于事。

正如其他人所提到的,问题在于CIImage 封装了生成图像所需的信息,但实际上并不是图像本身。所以在滚动时,屏幕上的图像需要动态生成,这会降低性能。使用imageWithCIImage:scale:orientation: 方法创建的UIImage 也是如此,因此创建一次并重复使用它也无济于事。

解决方案是强制 CoreGraphics 在将图像保存为 UIImage 之前实际渲染图像。这给我的滚动性能带来了巨大的改进。在您的情况下,applySaturation 方法可能如下所示:

CIImage *image = [CIImage imageWithCGImage:self.CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"];

[filter setValue:image forKey:kCIInputImageKey];
[filter setValue:[NSNumber numberWithFloat:saturation] forKey:@"inputSaturation"];

CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:filter.outputImage fromRect:filter.outputImage.extent];
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);

return image;

如果您要经常使用此方法,您还可以考虑缓存 CIFilter 和/或 CIContext,因为创建它们的成本可能很高。

【讨论】:

    猜你喜欢
    • 2014-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多