购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1 滤镜类型

CIFilter 是Core Image中的核心类之一,用于创建图像滤镜。当实例化一个 CIFilter 对象时,你(几乎)总是通过 kCIInputImageKey 键提供输入图像,再通过 kCIOutputImageKey 键取回处理后的图像。取回的结果可以作为下一个滤镜的输入值。

在本章即将开发的API中,我们会尝试封装应用这些键值对的具体细节,从而呈现给用户一个安全的强类型API。我们将 Filter 类型定义为一个函数,该函数接受一个图像作为参数并返回一个新的图像:

typealias Filter=CIImage-> CIImage

我们将在这个类型的基础上进行后续的构建。 gtAMXTBHZt6Gtf+VODkToWudxez0pWcBMowJRwKCyuMhyISdHXgOMsmRANJgYhme



2.2 构建滤镜

现在我们已经定义了 Filter 类型,接着就可以开始定义函数来构建特定的滤镜了。这些函数在接受特定滤镜所需要的参数之后,构造并返回一个 Filter 类型的值。它们的基本形态大概都是下面这样:

模糊

让我们来定义第一个简单的滤镜——高斯模糊滤镜。定义它只需要模糊半径这一个参数:

一切就是这么简单。 blur 函数返回一个新函数,新函数接受一个 CIImage 类型的参数 image ,并返回一个新图像( return filter.outputImage )。因此, blur 函数的返回值满足我们之前定义的 CIImage-> CIImage ,也就是 Filter 类型。

这个例子仅仅只是对Core Image中一个已经存在的滤镜进行的简单封装。我们可以反复使用相同的模式来创建自己的滤镜函数。

颜色叠层

现在让我们来定义一个能够在图像上覆盖纯色叠层的滤镜。Core Image默认不包含这样一个滤镜,但是我们完全可以用已经存在的滤镜来组成它。

我们将使用的两个基础组件:颜色生成滤镜( CIConstantColorGenerator )和图像覆盖合成滤镜( CISourceOverCompositing )。首先让我们来定义一个生成固定颜色的滤镜:

这段代码看起来和我们用来定义模糊滤镜的代码非常相似,但是有一个显著的区别:颜色生成滤镜不检查输入图像。因此,我们不需要给返回函数中的图像参数命名。取而代之,我们使用一个匿名参数 _ 来强调滤镜的输入图像参数是被忽略的。

接下来,我们将要定义合成滤镜:

在这里我们将输出图像剪裁为与输入图像一致的尺寸。严格来说,这不是必需的,而完全取决于我们希望滤镜如何工作。不过,这个选择在我们即将涉及的例子中效果很好。

最后,我们通过结合两个滤镜来创建颜色叠层滤镜:

我们再次返回了一个接受图像作为参数的函数。 colorOverlay 函数首先调用了 colorGenerator 滤镜。 colorGenerator 滤镜需要一个 color 作为参数,然后返回一个新的滤镜,因此代码片段colorGenerator(color)是Filter类型。而Filter类型本身就是一个从CIImage到CIImage的函数;因此我们可以向colorGenerator(color)函数传递一个 附加 的CIImage类型的参数,最终我们能够得到一个CIImage类型的新叠层。这就是我们在定义overlay的过程中所发生的全部事情,可以大致概括为——首先使用colorGenerator函数创建一个滤镜,接着向这个滤镜传递一个image参数来创建新图像。与之类似,返回值compositeSourceOver(overlay)(image)由一个通过compositeSourceOver(overlay)函数构建的滤镜和随即被作为参数的image组成。 gtAMXTBHZt6Gtf+VODkToWudxez0pWcBMowJRwKCyuMhyISdHXgOMsmRANJgYhme



2.3 组合滤镜

到现在为止,我们已经定义了模糊滤镜和颜色叠层滤镜,可以把它们组合在一起使用:首先将图像模糊,然后再覆盖上一层红色叠层。让我们来载入一张图片试试看:

现在我们可以链式地将两个滤镜应用到载入的图像上:

我们再一次通过创建滤镜来处理图像,例如先创建了 blur(blurRadius) 滤镜,然后将其运用到图像上。

复合函数

当然,我们可以将上面代码里两个调用滤镜的表达式简单合为一体:

然而,由于括号错综复杂,这些代码很快失去了可读性。更好的解决方式是自定义一个运算符来组合滤镜。为了定义该运算符,首先我们要定义一个用于组合滤镜的函数:

composeFilters 函数接受两个Filter类型的参数,并返回一个新定义的滤镜。这个复合滤镜接受一个CIImage类型的图像参数,然后将该参数传递给filter1,取得返回值之后再传递给filter2。我们可以使用复合函数来定义复合滤镜,就像下面这样:

为了让代码更具可读性,我们可以再进一步,为组合滤镜引入运算符。诚然,随意自定义运算符并不一定对提升代码的可读性有帮助。不过,在图像处理库中,滤镜的组合是一个反复被讨论的问题,所以引入运算符极有意义:

与之前使用 composeFilters 的方法相同,现在我们可以使用 >>> 运算符达到目的:

由于已经定义的运算符 >>> 是左结合的(lef-associative),就像Unix的管道一样,滤镜将以从左到右的顺序被应用到图像上。

我们定义的组合滤镜运算符是一个 复合函数 的例子。在数学中, f g 两个函数构成的复合函数有时候被写作 f · g ,表示定义的新函数将输入的 x 映射到 f(g(x)) 。除了顺序,这恰恰也是我们的 >>> 运算符所做的:将一个图像参数传递给运算符操作的两个滤镜。 gtAMXTBHZt6Gtf+VODkToWudxez0pWcBMowJRwKCyuMhyISdHXgOMsmRANJgYhme

点击中间区域
呼出菜单
上一章
目录
下一章
×