解读官方文档中Core Image的能力与最佳使用

前言

Core Image是一种图像处理和分析的技术,用于对静态图像和视频图像提供近似实时的处理。使用GPU或者CPU渲染,支持来自Core Graphics、Core Video和Image I/O的图像数据类型进行操作。

Core Image通过提供易于使用的API来隐藏底层图形处理的细节。

能力

常用场景

不要使用Core Image来创建模糊效果,而是使用NSVisualEffectView(macOS)或UIVisualEffectView(iOS/tvOS)类,它们会自动匹配系统外观并提供高效的实时渲染。

图像处理

依赖于CIFilter和CIImage类,它们描述了滤镜的输入与输出。

处理流程

import CoreImage
let context = CIContext()                                           // 1
let filter = CIFilter(name: "CISepiaTone")!                         // 2
filter.setValue(0.8, forKey: kCIInputIntensityKey)
let image = CIImage(contentsOfURL: myURL)                           // 3
filter.setValue(image, forKey: kCIInputImageKey)
let result = filter.outputImage!                                    // 4
let cgImage = context.createCGImage(result, from: result.extent)    // 5
  1. 创建CIContext对象。我们并不总是需要创建新的CIContext,通常会从其他管理渲染的框架中获取。创建自定义的上下文可以更精准的控制渲染过程以及渲染所需要的资源。Context是高资源消耗的对象,使用时尽早创建,并且在处理图像时进行复用。
  2. 创建需要使用的滤镜,并配置参数。
  3. 创建一个表示待处理图像的CIImage对象,并为其作为滤镜的输入图像。
  4. 获得表示滤镜输出结果的CIImage对象。此时滤镜尚未实际生效,Core Image会在请求渲染时执行该对象的生成。
  5. 将输出图像渲染成Core Graphics图像,即CGImage,这样我们就可以显示图像或者保存成文件。

CIImage对象

CIImage对象表示的是一个不可变的待渲染图像信息集合,并不直接表示图像的位图信息。

输入图像

有关创建CIImage对象的完整列表,请参阅CIimage类参考

####输出图像

作为CIFilter的outputImage属性的类型。

可以通过CIContext或者draw方法来显式请求渲染(请参阅使用Core Image Context构建自己的工作流程

可以通过使用其他框架来实现隐式请求渲染(请参阅与其他框架集成

CIFilter

表示图像的处理效果,以及控制该效果的参数数值。需要指定输入参数,然后从outputImage中获得处理结果。

系统内集成很多滤镜,我们可以通过滤镜的名称来获取使用(请参阅查询系统以获取滤镜Core Image滤镜参考

线程安全

CIContext 和 CIImage 对象是不可变的,在线程之间共享这些对象是安全的。所以多个线程可以使用同一个 GPU 或者 CPU CIContext 对象来渲染 CIImage 对象。

多个滤镜的链式调用

每个Core Image的滤镜都会生成一个输出CIImage对象,因此可以使用该对象作为另一个滤镜的输入,来达到链式调用的目的。

Core Image会优化向这样的链式吊用,以快速有效的渲染结果。链中的每个CIImage对象都不会完整渲染成图像,而是传递滤镜的处理步骤和参数。Core Image不会单独执行每个滤镜,也不会在过程中产生临时像素缓存区。Core Image会重新组织滤镜,甚至可能重新排列滤镜的应用顺序(如Color -> Bloom -> Crop,会被优化为 Crop -> Color -> Bloom),来更有效率的渲染结果图。

func applyFilterChain(to image: CIImage) -> CIImage {
    // The CIPhotoEffectInstant filter takes only an input image
    let colorFilter = CIFilter(name: "CIPhotoEffectProcess", withInputParameters:
        [kCIInputImageKey: image])!
    
    // Pass the result of the color filter into the Bloom filter
    // and set its parameters for a glowy effect.
    let bloomImage = colorFilter.outputImage!.applyingFilter("CIBloom",
                                                             withInputParameters: [
                                                                kCIInputRadiusKey: 10.0,
                                                                kCIInputIntensityKey: 1.0
        ])
    
    // imageByCroppingToRect is a convenience method for
    // creating the CICrop filter and accessing its outputImage.
    let cropRect = CGRect(x: 350, y: 350, width: 150, height: 150)
    let croppedImage = bloomImage.cropping(to: cropRect)
    
    return croppedImage
}

特殊滤镜

大多数内置的Core Image滤镜是在输入图的基础上处理,生成单个输出图像。但还有几种其他类型的滤镜,可以与常用的滤镜组合,创建有趣的效果,但这也需要更复杂的工作流程。

Compositing / Blending

使用预设的公式组合两张照片。

更多有关Compositing / Blending滤镜的完整列表,请查询CICategoryCompositeOperation

注意:在合成输入图像前,可以对图像进行几何调整。请参阅CICategoryGeometryAdjustment滤镜或imageByApplyingTransform:方法。

Generator

不需要输入图像,这些滤镜会根据输入参数,创建新的图像。

一部分Generator滤镜可以直接产出可供使用的结果,还有一部分可以应用在滤镜的链式调用中,组合出更多的结果。

更多Generator滤镜,请查询CICategoryGeneratorCICategoryGradient类别。

Reduction

其输出的不是创建位图的信息,而是描述输入图像的信息。比如:

所有Core Image 滤镜都必须产生一个CIImage对象作为输出,因此Reduction滤镜生成的信息仍然是图像。但通常不会显示这些图像,而是从单像素或单行图像中读取颜色值,或将其用作其他滤镜的输入图像。

有关Reduction滤镜的完整列表,请查询CICategoryReduction类别。

Transition

接收两个输入图像,并根据某个变量生成图像之间的变化。

通常该变量是时间,因此您可以使用Transition滤镜创建动画,该动画以一张图像开始,以另一张图像结束,并使用有趣的视觉效果从一个图像进行到另一个图像。Core Image 提供了几个内置的过渡滤镜,包括:

有关Transition滤镜的完整列表,请查询CICategoryTransition类别。

通过Quartz 2D基于CPU进行渲染

如果App对实时性能要求不高,可以通过Core Graphics来做视图渲染(例如在drawRect:中),可以使用 contextWithCGContext:options:通过Core Graphics Context来获取CIContext。

最佳实践

前期准备

在做性能优化前,先判断当前业务的情况。可以从以下几方面进行分析

优化

在确定了业务情况后,在着手性能优化。有以下几点建议。

参考资料

官方文档