1

我的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 文档这样说CIImage

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

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

4

2 回答 2

2

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

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

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

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

更新

回复您的评论:

当您从 CIImage 中提取数据时,将应用适用的过滤器 - 例如,使用imageWithCIImage:[警告 - 这是我的推论,我尚未测试]。

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

于 2013-08-16T22:08:56.980 回答
0

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

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

解决方案是强制 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,因为创建它们的成本可能很高。

于 2017-10-02T22:05:39.763 回答