Core Image框架详细解析(十三) —— 在写一个自定义滤波器之前你需要知道什么?

版本记录

版本号 时间
V1.0 2018.01.28

前言

Core Image是IOS5中新加入的一个框架,里面提供了强大高效的图像处理功能,用来对基于像素的图像进行操作与分析。还提供了很多强大的滤镜,可以实现你想要的效果,下面我们就一起解析一下这个框架。感兴趣的可以参考上面几篇。
1. Core Image框架详细解析(一) —— 基本概览
2. Core Image框架详细解析(二) —— Core Image滤波器参考
3. Core Image框架详细解析(三) —— 关于Core Image
4. Core Image框架详细解析(四) —— Processing Images处理图像(一)
5. Core Image框架详细解析(五) —— Processing Images处理图像(二)
6. Core Image框架详细解析(六) —— 图像中的面部识别Detecting Faces in an Image(一)
7. Core Image框架详细解析(七) —— 自动增强图像 Auto Enhancing Images
8. Core Image框架详细解析(八) —— 查询系统中的过滤器 Querying the System for Filters
9. Core Image框架详细解析(九) —— 子类化CIFilter:自定义效果的配方 Subclassing CIFilter: Recipes for Custom Effects(一)
10. Core Image框架详细解析(十) —— 子类化CIFilter:自定义效果的配方 Subclassing CIFilter: Recipes for Custom Effects(二)
11. Core Image框架详细解析(十一) —— 获得最佳性能 Getting the Best Performance
12. Core Image框架详细解析(十二) —— 使用反馈处理图像 Using Feedback to Process Images

What You Need to Know Before Writing a Custom Filter - 在写一个自定义滤波器之前你需要知道什么?

Core Image提供了编写自定义过滤器的支持。 自定义过滤器是您为其编写一个称为内核的例程,用于指定要对每个源图像像素执行的计算。 如果您打算使用内置的Core Image过滤器,或者按照成为它们的子类化过滤,则不需要阅读本章。 如果您打算编写自定义过滤器,则应阅读本章,以便了解自定义过滤器中的处理路径和组件。 阅读本章后,您可以了解如何在Creating Custom Filters中编写过滤器。 如果您有兴趣打包自定义过滤器进行分发,您还应该阅读 Packaging and Loading Image Units


Filter Clients and Filter Creators - 过滤客户端和过滤器创建者

Core Image是为两种类型的开发人员设计的:Filter Clients and Filter Creators。 如果您只打算使用Core Image过滤器,则您是过滤客户端。 如果您打算编写自己的过滤器,则您是过滤器创建者。

图8-1显示了典型滤波器的组件。 图中的阴影部分表示“底层”部分 - 过滤器客户端不需要知道任何内容的部分,但过滤器创建者必须了解的部分。 未加阴影的部分显示了向过滤器客户端提供数据的两个方法(attributesoutputImage)。 过滤器的attributes方法返回描述过滤器的键值对的列表。 outputImage方法使用下面2点生成一个图像:

  • 采样器从源获取像素
  • 处理像素的内核
Figure 8-1 The components of a typical filter

每个自定义过滤器的核心都是内核。 内核指定在每个源图像像素上执行的计算。 内核计算可能非常简单或复杂。 一个非常简单的内核“无所事事”的过滤器可以简单地返回源像素:

destination pixel = source pixel

过滤器创建者使用OpenGL着色语言(glslang)的变体来指定每个像素的计算。 (请参阅Core Image Kernel Language Reference。)内核对过滤器客户端是不透明的。 过滤器实际上可以使用多个内核例程,将一个输出传递给另一个输入。 有关如何编写自定义过滤器的说明,请参阅Creating Custom Filters

Note: A kernel is the actual routine, written using the Core Image variant of glslang, that a filter uses to process pixels. A CIKernel object is a Core Image object that contains a kernel routine. When you create a filter, you’ll see that the kernel routine exists in its own file—one that has a .cikernel extension. You create a CIKernel object programmatically by passing a string that contains the kernel routine. 注意:内核是使用glslang的Core Image变体编写的实际例程,过滤器用它来处理像素。 CIKernel对象是包含内核例程的Core Image对象。 当你创建一个过滤器的时候,你会看到内核例程存在于它自己的文件中,一个扩展名是.cikernel。 通过传递一个包含内核例程的字符串,以编程方式创建一个CIKernel对象。

过滤器创建者可以使用NSBundle类指定的体系结构将它们的自定义过滤器打包为插件或图像单元,以便其他App可以使用。 图像单元可以包含多个过滤器,如图8-2所示。 例如,您可以编写一组执行不同类型边缘检测的滤镜,并将它们打包为单个图像单元。 过滤器客户端可以使用Core Image API来加载图像单元,并获取该图像单元中包含的滤镜列表。 请参阅Loading Image Units基本信息。 有关写入过滤器并将其打包为独立映像单元的深入示例和详细信息,请参阅Image Unit Tutorial

Figure 8-2 An image unit contains packaging information along with one or more filter definitions

The Processing Path - 处理路径

图8-3显示了在两个源图像上运行的滤镜的像素处理路径。源图像总是被指定为CIImage对象。 Core Image提供了多种获取图像数据的方法。您可以为图像提供URL,读取原始图像数据(使用NSData类),或者将Quartz 2D图像(CGContextRef),OpenGL纹理或Core Video图像缓冲区(CVImageBufferRef)转换为CIImage对象。

请注意,输入图像的实际数量以及过滤器是否需要输入图像取决于过滤器。过滤器非常灵活 - 过滤器可以:

  • 没有输入图像的工作。一些过滤器根据不是图像的输入参数生成图像。 (例如,请参阅Core Image Filter Reference中的CICheckerboardGeneratorCIConstantColorGenerator过滤器。)
  • 需要一个图像。 (例如,请参阅Core Image Filter Reference中的CIColorPosterizeCICMYKHalftone过滤器。)
  • 需要两个或更多的图像。过滤器合成图像或使用一个图像中的值来控制如何处理另一个图像中的像素通常需要两个或更多个图像。一个输入图像可以作为阴影图像,图像蒙版,背景图像,或者提供查找值的来源,以控制其他图像的处理方式。 (例如,请参阅Core Image Filter Reference中的CIShadedMaterial过滤器。)

处理图像时,您有责任创建一个包含适当输入数据的CIImage对象。

Note: Although a CIImage object has image data associated with it, it is not an image. You can think of a CIImage object as an image “recipe.” A CIImage object has all the information necessary to produce an image, but Core Image doesn’t actually render an image until it is told to do so. 注意:尽管CIImage对象具有与之关联的图像数据,但它不是图像。 您可以将CIImage对象视为图像“ recipe”。CIImage对象具有生成图像所需的所有信息,但Core Image实际上并不呈现图像,除非被告知这样做。

Figure 8-3 The pixel processing path

来自每个源图像的像素由CISampler对象提取,或简单地取样器sampler。顾名思义,采样器sampler检索图像的样本,并将其提供给内核。过滤器创建者为每个源图像提供一个采样器。过滤器客户端不需要知道有关采样器的任何信息。

采样器定义:

  • 坐标变换,如果不需要变换,可以是identity transform
  • 插值模式,可以是最近邻采样或双线性插值(这是默认值)。
  • 指定如何在采样区域位于源图像之外时生成像素的环绕模式 - 要么使用透明黑色,要么使用clamp to the extent

过滤器创建者在内核中定义每像素图像处理计算,但Core Image处理这些计算的实际实现。Core Image确定是否使用GPU或CPU执行计算。 Core Image根据设备功能使用Metal,OpenGL或OpenGL ES实现硬件光栅化。它通过仿真环境实现软件光栅化,专门为在大四边形(四边形)上使用非投影纹理查找来评估片段程序而进行了调整。

尽管像素处理路径是从源图像到目的地的,但Core Image使用的计算路径是从目的地开始的,并返回到源像素,如图8-4所示。 这个反向计算可能看起来很笨拙,但实际上最小化了任何计算中使用的像素数量。 Core Image不使用的替代方法是处理所有源像素的蛮力方法,然后决定目的地需要什么。 让我们仔细看看图8-4。

Figure 8-4 The Core Image calculation path

假设图8-4中的过滤器执行某种合成操作,例如源合成合成。过滤器客户端希望重叠两个图像,使得每个图像只有一小部分被合成,以实现图8-4左侧所示的结果。通过展望目标应该是什么,Core Image可以确定来自源图像的哪些数据影响最终图像,然后将计算仅限于那些源像素。因此,采样器仅从源图像中的阴影区域提取采样像素,如图8-4所示。

请注意图8-4中标有Domain of definition的部分。定义的范围只是进一步限制计算的一种方法。它是所有像素都透明的区域(即,alpha分量等于0)。在此示例中,定义的域与目标图像完全一致。Core Image让你提供一个CIFilterShape对象来定义这个区域。 CIFilterShape类提供了许多可以定义矩形形状,变换形状以及在形状上执行插入,合并和交叉操作的方法。例如,如果使用比图8-4所示阴影区域更小的矩形定义滤镜形状,则Core Image会使用该信息来进一步限制计算中使用的源像素。

Core Image以其他方式促进高效处理。它执行智能缓存和编译器优化,使其非常适合实时视频处理和图像分析等任务。它缓存重复评估的任何数据集的中间结果。每当添加新图像时,Core Image会以最近最少使用的次序逐出数据,这会导致缓存变得太大。经常重复使用的对象保留在缓存中,而偶尔使用的对象可能会根据需要移入和移出缓存。您的App可从Core Image缓存中受益,而无需了解如何实施缓存的详细信息。但是,只要可以,就可以通过重新使用对象(图像,上下文等)来获得最佳性能。

Core Image通过在内核和传递级别使用传统的编译技术也获得了很好的性能。Core Image用于分配寄存器的方法使临时寄存器(每个内核)和临时像素缓冲器(每个过滤器图)的数量最小化。编译器执行几次优化,并自动区分阅读基于以前计算的依赖于数据的纹理和不依赖于数据的纹理。再次,你不需要关心编译技术的细节。重要的一点是Core Image是硬件知识,它尽可能地使用GPU和多核CPU的功能,而且它是以智能的方式实现的。


Coordinate Spaces - 坐标空间

Core Image在独立于设备的工作空间中执行操作。Core Image的工作空间在理论上是无限的。工作空间中的点由坐标对(x,y)表示,其中x表示沿着水平轴的位置,并且y表示沿着垂直轴的位置。坐标是浮点值。默认情况下,原点是(0,0)。

Core Image读取图像时,会将像素位置转换为与设备无关的工作空间坐标。当显示处理后的图像时,Core Image将工作空间坐标转换为目标(例如显示器)的适当坐标。

编写自己的过滤器时,需要熟悉两个坐标空间:目标坐标空间和采样器空间。目标坐标空间表示要渲染的图像。采样器空间代表你正在从别处获取的纹理(另一个图像,查找表等)。您使用destCoord函数获取目标空间中的当前位置,而samplerCoord函数提供采样空间中的当前位置。 (请参阅 Core Image Kernel Language Reference。)

请记住,如果源数据平铺,则采样器坐标具有偏移量(dx / dy)。如果您的样本坐标有偏移量,则可能需要使用函数samplerTransform将目标位置转换为采样器位置。


The Region of Interest - 感兴趣的区域

虽然在图8-4中没有明确标注,但每个源图像中的阴影区域都是图中描述的采样器的region of interest。 感兴趣区域或ROI定义了采样器采用像素信息提供给内核进行处理的源中的区域。 如果您是过滤器客户端,则无需关心ROI。 但是,如果您是过滤器创建者,则需要了解关注区域与定义域之间的关系。

回想一下,定义域描述了一个过滤器的边界形状。 理论上,这个形状可以是无边界的。 例如,考虑一个过滤器,创建一个可以延伸到无限的重复模式。

ROI和定义域可以通过以下方式相互关联:

  • 它们恰好一致 - 源和目标之间有1:1映射。例如,色调过滤器处理来自ROI中的工作空间坐标(r,s)的像素以在定义域中的工作空间坐标(r,s)处产生像素。
  • 他们彼此依赖,但以某种方式调制。一些最有意思的滤镜 - 例如模糊和变形 - 在计算一个目标像素时使用许多源像素。例如,失真过滤器可以使用来自ROI中的工作坐标空间的像素(r,s)及其邻居在定义域中产生单个像素(r,s)。
  • 定义域是从采样器提供的查找表中的值中计算出来的。图或表格中值的位置与源图像和目的地中的工作空间坐标无关。位于(r,s)中的着色图像中的值不需要是在定义域中的工作空间坐标(r,s)处产生像素的值。许多滤镜使用着色图像或查找表中提供的值与图像源结合使用。例如,颜色渐变或近似函数的表(如arcsin函数)提供的值与工作坐标的概念无关。

除非另有指示,否则Core Image假定ROI和定义域一致。 如果你写一个这个假设不适用的过滤器,你需要为Core Image提供一个例程来计算特定采样器的ROI。

请参阅Supplying an ROI Function以获取更多信息。


Executable and Nonexecutable Filters - 可执行和不可执行的过滤器

您可以根据自定义Core Image过滤器是否需要辅助二进制可执行文件加载到客户端应用程序的地址空间中进行分类。在您使用Core Image API时,您会注意到这些API简称为可执行文件和不可执行文件。过滤器创建者可以选择写任何一种过滤器。过滤器客户端可以选择仅使用不可执行或使用两种过滤器。

安全性是区分CPU可执行文件和CPU不可执行过滤器的主要动机。不可执行的过滤器只包含一个Core Image内核程序来描述过滤器操作。相比之下,一个可执行的过滤器也包含在CPU上运行的机器代码。 Core Image内核程序在受限制的环境中运行,不能构成病毒,特洛伊木马或其他安全威胁,而运行在CPU上的任意代码都可以。

不可执行的过滤器有特殊要求,其中之一是不可执行的过滤器必须作为图像单元的一部分进行封装。过滤器创建者可以读取Writing Nonexecutable Filters以获取更多信息。过滤器客户端可以在Loading Image Units中查找有关加载每种过滤器的信息。


Color Components and Premultiplied Alpha - 颜色组件和预乘Alpha

Premultiplied alpha是一个术语,用于描述源颜色,其组成部分已经乘以一个alpha值。通过消除对每个颜色分量执行乘法运算的需要,预乘可以加快图像的渲染速度。例如,在RGB色彩空间中,使用预乘alpha来渲染图像会消除图像中每个像素的三次乘法运算(红色次alpha,绿色次alpha,蓝色次alpha)。

过滤器创建者必须提供Core Image以及由alpha值预乘的颜色分量。否则,过滤器的行为就好像一个颜色分量的alpha值是1.0。确保颜色组件预乘是非常重要的操作颜色的过滤器。

默认情况下,Core Image假定处理节点是每像素128位,线性光,预乘RGBA浮点值,使用GenericRGB色彩空间。您可以通过提供Quartz 2D CGColorSpace对象来指定不同的工作色彩空间。请注意,工作色彩空间必须是基于RGB的。如果您将YUV数据作为输入(或其他不是基于RGB的数据),则可以使用ColorSync功能转换为工作色彩空间。 (有关创建和使用CGColorspace对象的信息,请参见Quartz 2D Programming Guide。)

对于8位YUV 4:2:2来源,Core Image每千兆字节可以处理240个HD图层。八位YUV是DV,MPEG,未压缩的D1和JPEG等视频源的原生色彩格式。您需要将YUV色彩空间转换为Core Image的RGB色彩空间。


See Also - 也可参考

Shantzis, Michael A., “A Model for Efficient and Flexible Image Computing,” (1994), Proceedings of the 21st Annual Conference on Computer Graphics and Interactive Techniques.

Smith, Alvy Ray, “Image Compositing Fundamentals,” Memo 4, Microsoft, July 1995. Available from http://alvyray.com/Memos/MemosCG.htm#ImageCompositing

后记

本篇已结束,后面更精彩~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容