iOS CoreImage专题(一)—— 概述

  • 前言
  • 什么是CoreImage
    • 概览
    • 查询CoreImage的滤镜列表及它们的属性
  • 处理图像
    • 概述
    • 内建滤镜
    • 创建CoreImage上下文
    • 在iOS上创建一个不需要实时性能的CoreImage上下文
    • 在iOS上创建一个需要实时性能的CoreImage上下文
    • 创建一个CIImage对象
    • 创建CIFilter并设值
    • 获取输出图像
    • 渲染输出图像
  • 总结

前言

虽然是一个专题,但是每个章节的内容都不算多。
所以我将本专题的Demo汇总到了下面这个git仓库中。
这个demo里面包含了本专题所有的小demo的实现。
通过一个navigationController来管理。

本专题是对苹果CoreImage编程指南的整理以及demo的具体实现。
更多内容请参见苹果官方的CoreImage类参考:
https://developer.apple.com/library/prerelease/tvos/documentation/GraphicsImaging/Reference/CoreImagingRef/index.html

github link for demo

什么是CoreImage

CoreImage一个图像处理和分析技术,同时也提供了对视频图像实时处理的技术。它处理的图像数据基于CoreGraphics,CoreVideo,和Image I/O框架,既可以使用GPU也可以使用CPU的渲染路径。CoreImage封装了底层图形处理的实现细节,它只提供非常易用的API接口。你不必知道OpenGL和OpenGL ES是如何利用GPU的,你爷不必知晓GCD(Grand Central Dispatch)是如何利用多核进行处理的,CoreImage帮你把这些都处理好了。

iOS CoreImage专题(一)—— 概述_第1张图片

概览

CoreImage框架提供:
- 对内建(built-in)图像处理滤镜的访问
- 特征识别能力
- 对自动图像增强的支持
- 能将多个滤镜组成滤镜链来创建自定义效果

查询CoreImage的滤镜列表及它们的属性

CoreImage拥有其内建(built-in)滤镜库的参考文档。你可以向系统查询哪些滤镜是可用的。对于每个滤镜,你都可以获取一个包含了它所有属性的字典,比如它的输入参数啊,默认参数值啊,最小值和最大值啊,显示名等等。

处理图像

CoreImage拥有三个类来支持图像处理:
- CIFilter,是个可变对象,它代表了一种效果。一个滤镜对象至少有一个输入参数并且产生一个输出图像。
- CIImage,是一个不可变对象,它表示一个图像。一个CIImage所需的图像数据,你可以直接合成,或者从一个文件中提供出来,也可以从一个CIFilter对象的输出来获取。
- CIContext,是Core Image绘制滤镜产生的结果的地方。Core Image上下文可以基于CPU或者GPU。

概述

要使用CoreImage,需要导入头文件: CoreImage/CoreImage.h(在iOS中已经不需要手动import了)。先来看一个简单的例子。

    // 创建一个CIContext对象
    CIContext *context = [CIContext contextWithOptions:nil];
    // 创建一个CIImage对象。你可以通过各种资源创建CIImage
    CIImage *image = [CIImage imageWithContentsOfURL:myURL];
    // 创建一个滤镜,然后设置它的输入参数
    CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"];
    [filter setValue:image forKey:kCIInputImageKey];
    [filter setValue:@0.8f forKey:kCIInputIntensityKey];
    // 获取输出图像,该图像只是如何产生图像的一种方式,它还没有被渲染
    CIImage *result = [filter valueForKey:kCIOutputImageKey];
    CGRect extent = [result extent];
    // 将CIImage渲染成CoreGraphics图像用来显示或者保存为文件
    CGImageRef cgImage = [context createCGImage:result fromRect:extent];

注意:有些CoreImage滤镜产生的图片范围是无穷大的,比如那些在CICategoryTileEffect类目中的。在渲染它们之前,无穷大的图片必须被剪裁(CICrop 滤镜),或者必须制定一个有限大尺寸的矩形来渲染图像。

内建滤镜

CoreImage配备了几十个内置的滤镜来支持在你的App中进行图像处理。这些滤镜是可以改变的,处于这个原因,CoreImage提供了能让你向系统查询可用滤镜的方法。
滤镜类目指定了效果的类型 – 模糊,虚化等等 – 亦或它的预定用途 – 静止图像,视频,非方形像素等等。一个滤镜可能属于多个类目。滤镜拥有一个显示名称,是用来显示给app用户看的,它还有一个滤镜名称,那是用来给你编程的。
大多数滤镜都有一个或多个输入参数,你要用它们来控制图像处理。每个输入参数都有属性类,属性类指定了它的数据类型,比如NSNumber。一个输入参数可能还有其他的属性,比如它的默认值,最大值和最小值,这个参数的显示名称。
举个栗子,CIColorMonochrome滤镜有三个输入参数 – 要处理的图像,黑白色,及色彩强度。你应该提供图像,然后选择设置颜色和它的色彩强度。大多数滤镜,包括CIColorMonochrome滤镜,它们的输入参数,只要不是输入图像,就都有默认值。如果你没有提供你自己的值给这些参数的话CoreImage就会使用这些默认值来处理你的图片。
滤镜属性以键值对的形式存放。键是一个常量,用来定义这个属性,值通过这个键来进行设置。CoreImage使用键值编码,那意味着你可以通过NSKeyValueCoding协议里的方法来获取或者设置属性。

创建CoreImage上下文

你需要创建一个CoreImage上下文来渲染图像,并且你需要使用它来绘制输出图像。CoreImage上下文是绘制发生的地方。它决定了CoreImage是使用GPU还是CPU来渲染。下表列出了指定了平台和渲染者的各种方法。

上下文 渲染者 支持平台
contextWithOptions: CPU or GPU iOS
contextWithEAGLContext: contextWithEAGLContext: options: GPU iOS

在iOS上创建一个不需要实时性能的CoreImage上下文

如果你的App不要求实时的显示图像,你可以像这样创建一个CIContext对象:

CIContext *context = [CIContext contextWithOptions:nil];

该方法要么用CPU要么用GPU来渲染。如果要指定渲染者,则需要创建一个选项的字典,使用kCIContextUseSoftwareRenderer作为key,指定一个布尔值作为这个键的值。CPU渲染比GPU渲染要慢,但是如果使用GPU来渲染,渲染结果图像将不会被显示,直到它被拷贝到CPU内存并且被转换成其他图像类型,比如被转换成一个UIImage对象。

在iOS上创建一个需要实时性能的CoreImage上下文

如果你的app支持实时图像处理,那么你就应该通过一个EAGL上下文来创建一个CIContext对象而不是使用contextWithOptions:并且指定渲染者为GPU。实时渲染的好处在于被渲染的图像被保存在了GPU,它永远不会被拷贝到CPU内存。首先你需要创建一个EAGL上下文:

EAGLContext *myEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

接下来使用contextWithEAGLContext:方法来创建CIContext对象。你应该设置一个Null参数给工作颜色空间来关掉色彩管理,色彩管理会降低你的性能。你只有在需要色彩保真度的情境下才会希望使用色彩管理,然而实时App通常都不会关注色彩保真。

NSDictionary *options = @{ kCIContextWorkingColorSpace : [NSNull null] };
    CIContext *myContext = [CIContext contextWithEAGLContext:myEAGLContext
options:options];

创建一个CIImage对象

CoreImage滤镜只会对CIImage对象进行处理。下表列出了创建一个CIImage对象的方法,你使用哪个方法取决于图像的资源。一定要记住CIImage对象只是一个图像的配方而已,CoreImage并不会产生任何像素直到它被叫去一个上下文里面进行渲染。

iOS CoreImage专题(一)—— 概述_第2张图片

创建CIFilter并设值

filterWithName:方法将创建一个这个名称参数所指定类型的滤镜。名称参数必须和内建滤镜的名称一致(在上面提到过内建滤镜的属性,用来编程的滤镜名称就是这个)。
在iOS中,输入参数的值将会在调用filterWithName:后被设置为默认值。
如果你不太清楚这个滤镜有哪些输入参数,你可以调用inputKeys这个方法来获取这个滤镜输入参数的数组。除了生成者滤镜(generator filters)外,所有滤镜都需要一个输入图像。其中一些两个或者更多的图像或者纹理。如果你想要改变默认值,你可以通过setValue:forKey:方法来对输入参数重新赋值。
来看个通过滤镜调整图像色调的例子。滤镜名称为CIHueAdjust。只要你正确输入了名称,你就能创建这个滤镜:

CIFilter * hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"];

在iOS中,这样调用就已经设置了默认参数。这个滤镜有两个输入参数:输入图像和输入角度。色调调整滤镜的输入角度指的是色调在HSV和HLS颜色空间中的位置。这是角度测量法,取值范围是0到2π。赋值为0则指示为红色,绿色则对应2/3π,蓝色则是4/3π。
接下来你需要指定输入图像和一个角度。你可以按照之前介绍的方法创建一个CIImage对象作为输入图像。假设我们用下面这张图作为输入图像:

iOS CoreImage专题(一)—— 概述_第3张图片

下面代码中赋给输入角度的值指定了一个玫瑰红的色调:

CIFilter * hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"];
[hueAdjust setValue: myCIImage forKey: kCIInputImageKey];
[hueAdjust setValue: @2.094f forKey: kCIInputAngleKey];

你也可以用下面这种更为简洁的代码来完成上面的工作:

hueAdjust = [CIFilter filterWithName:@"CIHueAdjust" keysAndValues:
                 kCIInputImageKey, myCIImage,
                 kCIInputAngleKey, @2.094f,
                 nil];

你可以提供任何你想要的参数,但是一定要用nil作为结束。

获取输出图像

kCIOutputImageKey作为键所对应的值将返回输出图像:
CIImage *result = [hueAdjust valueForKey: kCIOutputImageKey];
CoreImage不会进行任何的图像处理直到你真正调用了会渲染这个图像的方法。当你请求输出图像时,CoreImage将会组装一些运算用来生成输出图像,并且会将这些运算(也就是图像配方)存进一个CIImage对象。真正的图像渲染只会发生在一个图像绘制方法被显式地调用时(从而运算将会执行)(在接下里你将会看到如何渲染输出图像)。
直到渲染时再进行处理,这样的延迟处理将会使CoreImage变得快速和高效(Deferring processing until rendering time makes Core Image fast and efficient)。在渲染时刻到来时,CoreImage就可以看到有没有多于一个的滤镜需要被应用于一张图像。如果有,它将会自动在一次操作中连接多个“配方”,那就意味着每个像素只会被处理一次而不是多次。下图描述了使CoreImage变得更加高效的多操作工作流,最终的图像是原图的缩小版。在图像比较大的情况下,在缩小图像前进行色彩调整,要比先缩小图像后调整色彩进行更多的处理。通过等到渲染时刻才应用滤镜,CoreImage就能确定把这些操作反过来执行将是一个更高效的执行方案。

iOS CoreImage专题(一)—— 概述_第4张图片

渲染输出图像

渲染输出图像将会触发处理器密集型操作(processor-intensive operation),是CPU还是GPU取决于你使用的上下文。下面的方法都是可用的渲染方法:

  • drawImage:inRect:fromRect:在制定的上下文中渲染一个图片的矩形区域。在iOS中,这个方法只在通过contextWithEAGLContext:方法生成的CIContext中进行渲染。
  • createCGImage:fromRect:、createCGImage:fromRect:format:colorSpace: 通过CIImage对象中的一个区域创建一个Quartz 2D图像。
  • render:toBitmap:rowBytes:bounds:format:colorSpace: 将一个CIImage对象渲染进位图中。
  • render:toCVPixelBuffer:、render:toCVPixelBuffer:bounds:colorSpace: 将CIImage对象渲染进一个CoreVideo像素缓冲中。

在这个例子中的源图像现在将在它被处理后的形态下出现:
iOS CoreImage专题(一)—— 概述_第5张图片

总结

这一部分我们简单介绍了一下CoreImage框架,如何使用内建滤镜并获取输出图像。在下一部分我们将使用一些比较高级的技巧来实现更复杂的效果。

你可能感兴趣的:(ios,guide,CoreImage)