说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

详解OS X和iOS图像处理框架Core Image

HTML文档下载 WORD文档下载 PDF文档下载
本文结合实例详解了OS X和iOS图像处理框架Core Image的使用,如何通过Core Image来创建和使用iOS的内置滤镜,非常适合初学者学习。虽然示例代码是用Swift写的iOS程序,不过实现概念很容易转换到Objective-C和OS X。

这篇文章会为初学者介绍一下Core Image,一个OS X和iOS的图像处理框架。

如果你想跟着本文中的代码学习,你可以在GitHub上下载示例工程。示例工程是一个iOS应用程序,列出了系统提供的大量图像滤镜以供选择,并提供了一个用户界面用来调整参数并观察效果。

虽然示例代码是用Swift写的iOS程序,不过实现概念很容易转换到Objective-C和OS X。

基本概念

说到Core Image,我们首先需要介绍几个基本的概念。

一个滤镜是一个对象,有很多输入和输出,并执行一些变换。例如,模糊滤镜可能需要输入图像和一个模糊半径来产生适当的模糊后的输出图像。

一个滤镜图表是一个链接在一起的滤镜网络(无回路有向图),使得一个滤镜的输出可以是另一个滤镜的输入。以这种方式,可以实现精心制作的效果。我们将在下面看到如何连接滤镜来创建一个复古的拍照效果。

熟悉Core Image API

有了上述的这些概念,我们可以开始探索Core Image的图像滤镜细节了。

Core Image架构

Core Image有一个插件架构,这意味着它允许用户编写自定义的滤镜并与系统提供的滤镜集成来扩展其功能。我们在这篇文章中不会用到Core Image的可扩展性;我提到它只是因为它影响到了框架的API。

Core Image 是用来最大化利用其所运行之上的硬件的。每个滤镜实际上的实现,即内核,是由一个GLSL(即OpenGL的着色语言)的子集来书写的。当多个滤镜连接成一个滤镜图表,Core Image便把内核串在一起来构建一个可在GPU上运行的高效程序。

只要有可能,Core Image都会把工作延迟。通常情况下,直到滤镜图表的最后一个滤镜的输出被请求之前都不会发生分配或处理。

为了完成工作,Core Image需要一个称为上下文(context)的对象。这个上下文是框架真正工作的地方,它需要分配必要的内存,并编译和运行滤镜内核来执行图像处理。建立一个上下文是非常昂贵的,所以你会经常想创建一个反复使用的上下文。接下来我们将看到如何创建一个上下文。

查询可用的滤镜

Core Image滤镜是按名字创建的。要获得系统滤镜的列表,我们要向Core Image的kCICategoryBuiltIn类别请求得到滤镜的名字:

let filterNames = CIFilter.filterNamesInCategory(kCICategoryBuiltIn) as [String]

iOS上可用的滤镜列表非常接近于OS X上可用滤镜的一个子集。在OS X上有169个内置滤镜,在iOS上有127个。

通过名字创建一个滤镜

现在,我们有了可用滤镜的列表,我们就可以创建和使用滤镜了。例如,要创建一个高斯模糊滤镜,我们传给CIFilter初始化方法相应的名称就可以了:

let blurFilter = CIFilter(named:"CIGaussianBlur") 

设置滤镜参数

由于Core Image的插件结构,大多数滤镜属性并不是直接设置的,而是通过键值编码(KVC)设置。例如,要设置模糊滤镜的模糊半径,我们使用KVC来设置inputRadius属性:

blurFilter.setValue(10.0 forKey:"inputRadius")

由于这种方法需要AnyObject? (即Objective-C里的id)作为其参数值,它不是类型安全的。因此,设置滤镜参数需要谨慎一些,确保你传值的类型是正确的。

查询滤镜属性

为了知道一个滤镜提供什么样的输入和输出参数,我们就可以分别获取inputKeys和outputKeys数组。它们都返回NSString的数组。

要获取每个参数的详细信息,我们可以看看由滤镜提供的attributes字典。每个输入和输出参数名映射到它自己的字典里,描述了它是什么样的参数,如果有的话还会给出它的最大值和最小值。例如,下面是 CIColorControls滤镜对应的inputBrightness参数字典:

inputBrightness = {      CIAttributeClass = NSNumber;    CIAttributeDefault = 0;    CIAttributeIdentity = 0;    CIAttributeMin = -1;    CIAttributeSliderMax = 1;    CIAttributeSliderMin = -1;    CIAttributeType = CIAttributeTypeScalar;};

对于数值参数,该字典会包含 kCIAttributeSliderMin 和 kCIAttributeSliderMax 键,来限制期望的输入域。大多数参数还包含一个 kCIAttributeDefault 关键字,映射到该参数的默认值。

图片滤镜实战

图像滤镜的工作由三部分组成:构建和配置滤镜图表,发送等待滤镜处理的图像,得到滤镜处理后的图像。下面的部分对此进行了详细描述。

构建一个滤镜图表

构建一个滤镜图表由这几个部分组成:实例化我们需要的滤镜,设置它们的参数,把它们连接起来以便该图像数据按顺序传过每个滤镜。

在本节中,我们将创建一个用来制作 19 世纪锡版照风格图像的滤镜图表。我们将两个效果链在一起来达到这种效果:同时去饱和以及染色调的黑白滤镜,和一个暗角滤镜来创建一个有阴影效果的加框图片。

用Quartz Composer,来做Core Image滤镜图表的原型非常有用,可以从苹果开发者网站下载。下面,我们整理了所需的照片滤镜,把黑白滤镜和暗角滤镜串在一起:


一旦达到了我们满意的效果,我们可以重新在代码里创建滤镜图表:

let sepiaColor = CIColor(red: 0.76, green: 0.65, blue: 0.54)  let monochromeFilter = CIFilter(name: "CIColorMonochrome",      withInputParameters: ["inputColor" : sepiaColor, "inputIntensity" : 1.0])monochromeFilter.setValue(inputImage, forKey: "inputImage")let vignetteFilter = CIFilter(name: "CIVignette",      withInputParameters: ["inputRadius" : 1.75, "inputIntensity" : 1.0])vignetteFilter.setValue(monochromeFilter.outputImage, forKey: "inputImage")let outputImage = vignetteFilter.outputImage

需要注意的是黑白滤镜的输出图像变为暗角滤镜的输入图像。这将导致暗角效果要应用到黑白图像上。还要注意的是,我们可以在初始化中指定参数,而不一定需要用KVC单独设置它们。

创建输入图像

Core Image滤镜要求其输入图像是CIImage类型。而对于iOS的程序员来说这可能会有一点不寻常,因为他们更习惯用UIImage,但这个区别是值得的。一个CIImage实例实际上比UIImage更全面,因为CIImage可以无限大。当然,我们不能存储无限的图像在内存中,但在概念上,这意味着你可以从2D平面上的任意区域获取图像数据,并得到一个有意义的结果。

所有我们在本文中使用的图像都是有限的,而且也可以很容易从一个UIImage来创建一个CIImage。事实上,这只需要一行代码:

let inputImage = CIImage(image: uiImage)

也有很方便的初始化方法直接从图像数据或文件URL来创建CIImage。

一旦我们有了一个CIImage,我们就可以通过设置滤镜的inputImage参数来将其设置为滤镜的输入图像:

filter.setValue(inputImage, forKey:"inputImage")

得到一个滤镜处理后的图片

滤镜都有一个名为outputImage的属性。正如你可能已经猜到的一样,它是 CIImage 类型的。那么,我们如何实现从一个CIImage创建UIImage这样一个反向操作?好了,虽然我们到此已经花了所有的时间建立一个滤镜图表,现在是调用CIContext的力量来实际的做图像滤镜处理工作的时候了。

创建一个上下文最简单的方法是给它的构造方法传一个nil字典:

let ciContext = CIContext(options: nil)

为了得到一个滤镜处理过的图像,我们需要CIContext从输出图像的一个矩形内创建一个CGImage,传入输入图像的范围(bounds):

let cgImage = ciContext.createCGImage(filter.outputImage, fromRect: inputImage.extent())

我们使用输入图像大小的原因是,输出图像通常和输入图像具有不同的尺寸比。例如,一个模糊图像由于采样超出了输入图像的边缘,围绕在其边界外还会有一些额外的像素。

现在,我们可以从这个新创建的CGImage来创建一个UIImage了:

let uiImage = UIImage(CGImage: cgImage)

直接从一个CIImage创建UIImage也是可以的,但这种方法有点让人郁闷:如果你试图在一个UIImageView上显示这样的图像,其contentMode属性将被忽略。使用过渡的CGImage则需要一个额外的步骤,但可以省去这一烦恼。

用OpenGL来提高性能

用CPU来绘制一个CGImage是非常耗时和浪费的,它只将结果回传给UIKit来做合成。我们更希望能够在屏幕上绘制应用滤镜后的图像,而不必去Core Graphics里绕一圈。幸运的是,由于OpenGL和Core Image的可互操作性,我们可以这么做。

要OpenGL上下文和Core Image上下文之间共享资源,我们需要用一个稍微不同的方式来创建我们的 CIContext:

let eaglContext = EAGLContext(API: .OpenGLES2)  let ciContext = CIContext(EAGLContext: context)

在这里,我们用OpenGL ES 2.0的功能集创建了一个EAGLContext。这个GL上下文可以用作一个GLKView的背衬上下文或用来绘制成一个CAEAGLLayer。示例代码使用这种技术来有效地绘制图像。

当一个CIContext具有了关联GL的上下文,滤镜处理后的图像就可用OpenGL来绘制,像如下这样调用方法:

ciContext.drawImage(filter.outputImage, inRect: outputBounds, fromRect: inputBounds)

与以前一样,fromRect 参数是用滤镜处理后的图像的坐标空间来绘制的图像的一部分。这个inRect 参数是GL上下文的坐标空间的矩形应用到需要绘制图像上。如果你想保持图像的长宽比,你可能需要做一些数学计算来得到适当的inRect。

强制在CPU上做滤镜操作

只要有可能,Core Image将在GPU上执行滤镜操作。然而,它确实有回滚到CPU上执行的可能。滤镜操作在CPU上完成可具有更好的精确度,因为GPU经常在浮点计算上以失真换得更快的速度。在创建一个上下文时,你可以通过设置kCIContextUseSoftwareRenderer关键字的值为true来强制Core Image在CPU上运行。

你可以通过在Xcode中设置计划配置(scheme configuration)里的CI_PRINT_TREE环境变量为1来决定用CPU  还是GPU来渲染。这将导致每次一个滤镜处理图像被渲染的时候Core Image都会打印诊断信息。此设置用来检查合成图像滤镜树也很有用。

示例应用一览

本文的示例代码是一个iPhone应用程序,展示了iOS里大量的各式Core Image图像滤镜。

为滤镜参数创建一个GUI

为了尽可能多的演示各种滤镜,示例应用程序利用了Core Image的内省特点生成了一个界面,用于控制它支持的滤镜参数:


示例应用程序只限于单一的图像输入以及零个或多个数值输入的滤镜。也有一些有趣的滤镜不属于这一类(特别是那些合成和转换滤镜)。即便如此,该应用程序仍然很好的概述了Core Image支持的功能。

对于每个滤镜的输入参数,都有一个滑动条可以用于配置参数的最小值和最大值,其值被设置为默认值。当滑动条的值发生变化时,它把改变后的值传给它的 delegate,一个持有CIFilter引用的 UIImageView子类。

使用内建的照片滤镜

除了许多其他的内置滤镜,示例应用程序还展示了iOS 7中引入的照片滤镜。这些滤镜没有我们可以调整的参数,但它们值得被囊括进来,因为它们展示了如何在iOS中模拟照片应用程序的效果:


结论

这篇文章简要介绍了Core Image这个高性能的图像处理框架。我们一直在试图在如此简短的形式内尽可能多的展示这个框架的功能。你现在已经学会了如何实例化和串联Core Image的滤镜,在滤镜图表传入和输出图像,以及调整参数来获得想要的结果。你还学习了如何访问系统提供的照片滤镜,用以模拟在iOS上的照片应用程序的行为。

现在你知道了足够多的东西来写你自己的照片编辑应用程序了。随着更多的一些探索,你就可以写自己的滤镜了,利用你的Mac或iPhone的神奇的力量来执行以前无法想象的效果。快去动手做吧!

参考

  • Core Image Reference Collection是Core Image的权威文档集。
  • Core Image Filter Reference包含了Core Image提供的图像滤镜的完整列表,以及用法示例。
  • 如果想要写更函数式风格的Core Image代码,可以看看Florian Kluger在objccn.io话题#16里的文章。
本文转载自:objccn

比预想晚几年!IBM x86服务器或终将卖给联想 App推广的节操呢? iMessage垃圾短信产业链暗访 Apkudo CEO访谈:免费为Android开发者提供测试 Web API核查表:设计、测试、发布API时需思考的43件事 开源Android构建工具Buck 速度超Ant两倍 从史上八大MySQL事故中学到的经验 OpenStack Heat向应用市场更近一步 初创公司Ionic Security:云安全必须与时俱进 谷歌董事长:一年后消费者才能用上Goolge Glass Windows 8.1重新推出的“开始按钮”毫无意义 手眼并用 代码泄露三种Google Glass手势操作 Google新论文 CPI²:基于Linux的世界级跨数据中心服务器CPU监控 20款非常实用的Web工具和资源列表 数据库界大事件 随机写性能巨好的TokuDB开源了 CMDN Club 26期:数字渠道营销主题沙龙 暴强:用iOS设备控制的HTML5“小蜜蜂”游戏 Leap Motion:500元体感,精确到0.01毫米 大事件:三大运营商将发布融合计费SDK 再培养一个扎克伯克:六款适合儿童上手编程的App 首届中国软件工程标准高峰论坛:软件开发的新机遇与挑战 Verizon研究显示:中国成为2012年度网络间谍攻击的主要来源 Clang宣布全面支持C++11标准 北大陈钟:SEMAT—中国软件工程发展的新机遇 超华尔街预期:苹果第二财季营收为436亿美元 跨平台游戏引擎Spaceport已被Facebook收购 文科生开发GPU数据库 性能超传统数据库70倍 开发者赚了90亿:从苹果Q2财报,看iOS生态 备受开发者青睐的13款热门开源项目 Netflix赌赢《纸牌屋》背后的秘密武器:大数据分析 为什么:Google Glass只能运行Web应用? 对话Rackspace高级副总裁Jim Curry:我们没有"辜负"营销炒作 列表框中有多列,怎样让记录按列对齐? 求助capboy等网络视频播放高手-MPEG-1流的实时网络播放(2) the System V IPC机制是怎么一回事? 无组件上传图片,但我不能控制上传文件只为jpg or gif,请问怎样实现.急,在线等待!谢谢! 关于tomcat 的问题(急) help about windows printer 有关SDK的问题,高手请进! 高分寻求一个例子! dataset更新。100分 请您支招 通配符 有爱国心的高手快去黑了这个网站 这样的字符串比较怎么做?? 怎样在VB中加入动画? 费时操作是如何估计所花费时间的? 为什么我新装的机器在播放声音和视频文件时速度比原速度快了好多? 怎么样清楚Cookies 如何通过一个链接弹出一个没有标题栏,没有菜单栏,没有地址栏等的新页面? 配置问题,在线等待?分不够再加! MFC基础, 如何实现在CScrollView上, 把某一文本文件显示出来(不用file - new)? 求救:请问哪儿有《Beginning Visual Basic.Net》英文书下载??? 高分100 label 控件刷新问题 如何通过一个链接弹出一个没有标题栏,没有菜单栏,没有地址栏等的新页面? 实现这样一个报表??????? 求救。。。。。Access2000的数据库密码忘了,该怎么办呀!!! 动态产生的列表框,如何让记录按列对齐? 为什么有时自动打开网页,有时却不能? 在JDBTable中如何选中行? 我想让进度条竖着从下往上显示,应该怎么做? 可否知道数据表即将产生记录的标识键值???? 怎样让程序启动时最大化,但又不允许用户再还原或者改变窗口大小,谢谢! 紧急求救!关于ADSL下的固定IP地址访问问题,相关IP地址转发技术 谁见过这种编译错误,怎么办啊? 我的RH7.2不能Telnet ,请帮忙. GPRS 有无条件等待指令吗? 简单的问题_50分。 在vba的编写过程中无法在excel中调用word等其他对象,怎么办?(顶者有分) 事实证明,weblogic会对注释行报错! 一个让人困惑的问题 bcp 的问题 请问:有没有UNIX下的测试工具? 请问一个关于BCB消息MAP的问题... 系统2000,用installshield6.0包了程序,有问题 请问我用VC++做的数据库程序怎样打包在线等待急用, 在控制面斑-- >删除/添加程序 选项中 Win2000和Win98怎么不一样啊 改写了8254后如何得到正确的系统时钟 在别人机子上生成报表时提示出错,除打包安装外有无其他办法? ★ 懂 ORACLE 和 DB2 的高手请进... ★ 求助!!! 100分求小软件 消化产物 水解产物 氧化分解产物 有什么区别? 为什么利率降低会加剧通货膨胀啊? google搜索词之间“和”与“或”的使用怎么不能得到满意的结果呢?我在google.com上搜索关键词“cocktail and juice”的时候,结果43400000条;关键词“cocktail or juice”,结果43400000条,而且前几页结果 新制度经济学派对企业概念的理解 我认为那样做是毫无意义 的.I think doing( )that is ( ). 手提式二氧化碳灭火器是怎么换药的?因为我公司的几个灭火器换完药后瓶口的生料带还是原来那样脏脏的,要是换药的话是不是生料带也要换过?还是二氧化碳的灭火器换药只要把原来的气体 有ab两个圆柱体容器,原来,a容器里装2毫升的水,b容器是空的.现在往两个容器中以每分钟0.4毫升的速度注入水,4min后,两个容器的水面高度相等,已知b容器的底面半径为5厘米,a容器的底面直径是 已知一个铁原子的质量是一个碳原子质量的4.66倍,求铁的相对原子质量 遵守公共生活规则包括哪些 叫佳纯的女孩子应该取什么样的英文名呢我觉得最好是用佳开头sen结尾 或者是用卡开头森结尾之类要好听有意义一点的 风幕机型号如何确定选择 生活中最基本的社会规范有哪些? He went to the cafe yesterday?改否定句 经济过热和通货膨胀有什么区别? 基督徒应该遵守的生活规范和饮食规范应该是哪些?我不太懂,希望能够具体些. 用经济学解释政府如何采取措施控制房价 经济过热和通货膨胀有区别吗? board怎么读 we went to the ming tombs yesterday改为一般疑问句 煤粉细度R200是什么意思 求《吕氏春秋》的简介 i went to the parlc yesterday.变一般疑问句 请问关于煤粉细度的R90是什么意思啊?而R90为18%~请说得通俗点 《三豕涉河》道理这个是文言文,这个故事的道理是什么 为什么要装风幕机(风帘机,空气幕)? 粉末细度 《吕氏春秋》共有几篇?《吕氏春秋》共有几览?《吕氏春秋》共有几纪? 风幕机,空气幕 熟料煅烧为什么要控制煤粉的细度 生物中水解产物是什么意思? 如果一个经济出现过热(通货膨胀),政府应当如何进行宏观调控 风幕机一般多少W 关于糖类的生物题,我知道最终水解产物都是单糖,求具体的名称.3Q二糖:蔗糖水解最终产物是?麦芽糖水解最终产物是?乳糖水解最终产物是?多糖:淀粉的最终水解产物是?糖原的最终水解产物 利息=( )税后利息=( )利息税=( ) 煤粉细度R90=0.25的含义 化纤布料有哪些分类 (1)宏观调控 (2)通货膨胀 (3)投资减少 (4)物价回落 (5)经济过热A. 2-5-1-3-4 B. 3-5-2-1-4 C. 4-3-2-5-1 D. 5-2-1-3-4 答案是哪个呀 三豕涉河的字面解释一定要完整,不要害人哦! 化学纤维的布料都包含什么? 洋葱表皮实验 要是出现出现汽泡,怎么办 三豕涉河中 晋师已亥涉河也 涉 结合所学政治生活知识说明我国为什么坚持走和平发展道路 已知一个碳-12的原子的质量为1.99乘10的负26kg.镁的相对原子质量为24.035,求一个镁原子的质量 当经济出现通货膨胀时应采取什么样的政策? 不议价是什么意思 风幕机有什么作用 风幕机工作原理 关于天气的知识夏季的黄昏,一阵雷雨过后,你开始观察当时的天气现象.假如你随着时间的推移观察到了彩虹、晚霞、月亮周围的色彩光环,清说一说天气的趋势将如何发展?这是我们的思考题.. 议价粮油什么意思 They went to the zoo yesterday,_ _(反意疑问句) 风幕机什么作用 运用所学的经济生活知识,说明我国为什么要稳定房价 They went ti the zoo yesterday.改一般疑问句 松下风幕机FY-35ELC1单价多少钱? 运用所学经济生活知识,说明我们为什么能富起来 运用所学政治生活知识说明我们为什么能够站起来运用所学经济生活知识,说明我们为什么能富起来(11分)运用所学政治生活知识说明我们 羊毛衫的成分是什么? 经济增长和经济波动之间有什么区别?经济波动与通货膨胀、失业率和利率之间有怎样的关系? c4h8的同分异构体CH2=CHCH2CH3 CH3CH=CHCH3 CH3=C(CH3)2 还有别的么? 如何识别羊毛衫的标签成分? 通货膨胀会使利率升高还是降低?为什么? 食品添加剂甘油跟护肤品甘油一样吗
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘