我的应用程序中有一个UICollectionView
,其单元格主要由UIImageView
组成,其中包含使用Core Image操作的图像,以减少色彩饱和度。滚动时性能绝对可怕。当我对它进行分析时,花费的大部分时间(80%左右)不在我的代码中,甚至在我的堆栈中。这一切似乎都在Core Animation代码中。可能有人知道为什么会这样吗?
在我的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 Image的搭配效果不佳。 Apple docs对此CIImage
说明了这一点:
虽然CIImage对象具有与之关联的图像数据,但它不是图像。您可以将CIImage对象视为图像“食谱”.CIImage对象具有生成图像所需的所有信息,但Core Image在被告知这样做之前实际上不会渲染图像。这种“惰性评估”方法允许Core Image尽可能高效地运行。
在制作动画时最后一分钟进行此评估可能会非常棘手。
答案 0 :(得分:2)
我遇到了完全相同的问题,通过避免在单元格更新期间触发Core Image过滤器来解决问题。
关于惰性评估/配方的Apple文档,我认为更多的是针对您可以非常有效地将核心图像过滤器链接在一起的想法。但是,当你想要显示核心图像过滤器链的结果时,需要对那个东西进行评估,如果在快速滚动的视图中使用'then and there'并且在过滤器中,则这不是一个好的情况问题需要大量处理(其中许多都是如此)。
你可以尝试摆弄GPU与CPU的处理,但我发现将图像数据移入和移出CIImage的开销可能是一个更大的开销(参见我的回答here)
我的建议是将这种处理方式与处理使用在线图像填充滚动视图的方式相同 - 即异步处理,使用占位符(例如预处理的图像),并缓存结果以便重复使用。
更新
回复你的评论:
从CIImage中提取数据时应用适用的过滤器 - 例如,使用imageWithCIImage:
[警告 - 这是我的推断,我还没有测试过]。
但这不是你的问题......你需要在背景线程上处理你的图像,因为处理将花费时间来阻止滚动。同时在滚动单元格中显示其他内容,例如平面颜色或 - 更好 - 您正在为CIImage进行过滤的UIImage。处理完成后更新单元格(检查是否仍需要更新,此时可能已在屏幕外滚动)。将过滤后的图像保存在某种持久性存储中,这样您就不需要再次过滤它,并在需要再次显示图像之前检查缓存,然后再从头开始重新处理。
答案 1 :(得分:0)
我也有这个确切的问题 - 直到想要去饱和图像! - 过滤一次并缓存结果(即使作为UIImage)也没有帮助。
正如其他人所提到的,问题是CIImage
封装了生成图像所需的信息,但实际上并不是图像本身。因此,在滚动时,需要动态生成屏幕上的图像,这会影响性能。使用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,因为创建它们可能很昂贵。