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

详解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

磊友创始人赵霏:关于HTML5与Flash争执之我见 国际混乱C语言代码大赛优胜作品详解之“A clock in one line” 揭秘程序员大脑编程的七大“误区” 从Java到ObjC语言的桥梁:谷歌J2ObjC 0.7版本亮相 数据库管理系统ParElastic:打破常规 扩展云端MySQL 谷歌苹果专利纠纷意在战略无意和解 美法官为其苦恼 Facebook收购神秘移动软件创业公司Osmeta 修改驱动,使用Erlang+VoltDB实现每秒87.7万事务 横评美国IaaS“8匹狼”:AWS、GCE、Azure、Rackspace、SoftLayer、Dell、HP、Joyent 独立游戏精神释放的舞台:IGF China 2013作品火热征集中 三款热门入门级在线编程教程对比 微软modern.IE:网站兼容性测试利器 Localytics:AngularJS代替Backbone 代码减少一半 史上增长最快的SaaS服务Optimizely:13亿访客,10万次测试 IDF上的云计算故事 移动周报:百万年薪、微信疑云,老罗小马本周很忙 一个创业失败者的自述:我是如何搞砸一款产品 收入百万美元:一个放弃上大学的程序员 Cocos2D配Box2D:逆天小白程序员作iOS游戏 招聘移动开发,老板必问的10个问题 黑客用智能手机劫持和控制民航飞机 几小时即可使互联网瘫痪的黑客 现已加入谷歌 Tsar开源:淘宝内部大量使用的系统监控工具 五款超实用的开源SVG工具 微信收费事件背后被广泛忽略的技术细节 UIDesigner 3.0架构设计总结 黑客怒了:政府花930万美金仅做一款App Email服务迎来“第二春” 未来将更加个性化 贯穿应用开发始终的八大陷阱 微评微软也推智能手表:是追求未来,还只是跟随? Firefox OS今年6月在5国首发 不含美国市场 关于控制动态产生Checkbox,并进行动态控问题 BCB中使用MSComm控件,无法定义动态数组变量,该怎么办?--急啊!在线等待 战神----巴蒂 两个问题! 用ADO访问DBASE数据库 百分求救:关于COM方法的reference参数不返回值的问题 请教各位高手 如何将程序中不同的查询结果写入报表? 怎样自编程序监控icmp包,应该截获什么消息,还是用别的方法? java本地化的问题(各位帮帮我!) 一个关于类型转换的菜鸟问题!! 招聘 Jpeg中FFd9可以有几个? ?????不同窗口中多个参数传递的问题,在线等待中........ 关于 EmbeddedWb 的Mshtml_tlb, SHDocVw_tlb 问题! 各位大虾 帮我翻译一下 急 悲... DBGrid为什么没有数据集记录失去焦点事件? 怎样手动和在程序中更改SEQUENCE的当前值? 怎么结贴子,什么叫FAQ? 关于“软件蓝领”的3个疑惑。 在函数中以引用方式调用结构的问题?给我能给的最高分100 怎样实现随机打印一个数据表的几个子段? 大家看看,这个功能如何实现? 怎样编一个程序读取服务器上的时间? VC工程不能正常打开? Jpeg中FFd9可以有几个? datawidgets简介!简单的问题---“送大伙一点小分” 新人到!!有问题想要问!!! 有谁知道sybase12.5中定义的所有保留字keywords? 为什么我的Tomcat突然找不到Serverlet了 关于报考程序员的问题 请问:一个应用程序发布时需要为每个发布的实例建立一个唯一标识用GUID好不好? 介绍些ActiveX/COM的好书吧! 图片的名字是中文就不能显示,怎么办 奇怪的问题关于NULL! 高分求助:pb中数据传输的问题 请教,在窗体改变大小时,有没有通用的办法,改变窗体内控件的大小,位置 如何用VB在CMOS中获取指定信息? ??低级问题,如何得到一个hdc(最好多几个方法) !!!!!!!!!急急急!!!!!!!!!!!!! 如何在頁面引用outlook的收件人地址控件,使得輸入的mail帳號會自動加上下划线,请给代码,并解释,非常感谢! 有名的ehlib控件,2.3版,功能多多,本人在其基础上又增加了很多属性,不能错过! 不是高手莫进来!:) 这是小雨田子第N次给大家送分了,请进吧!!! 如何打包 一个百思不得其解的问题,使用SetSQLSelect()语句重置数据窗体的SQL语句,SQL语句无错,但存在部分机器不能成功执行。不知何故。 关于排列组合的问题 ACL能容纳多少个项目? 怎样在pb里面去取Excel表中的数据?????? 假设在表达式中只有加、减、乘、除及小括号运算,试利用栈来实现对表达式的求值运算? 汽车电路中,50线的作用是什么? 声音的响度跟人与声源的什么有关?急 如何用原子运动的角度来解释声音的传播速度及响度变化?用原子碰撞或能量消耗来解释 检查汽车电路应注意哪些事项 温度传感器的种类 多波段收音机音量逐渐变小的原因是什么?一台21波段的集成电路的收音机(凯隆KK-210B),用了5年后,搁置10年后再收听,发现刚开始收听声音洪亮,收听到40分钟后音量逐渐变小,更换新电池后还 汽车电路怎么查线 温度传感器的种类有哪些 朱自清的《春》给我们展示了五幅图,分别是什么图,给五幅图配上相应的古诗五幅,不要四幅 将玻璃罩罩住两根蜡烛,为什么长蜡烛会先灭? 某同学设计了如图实验:将一支充满CO2的试管倒扣在盛有澄清石灰水的水槽中,过一段时间可以观察到试管内的水面开始___________,因为______.试管内液体变_________. 某体积为V的密闭绝热环境内有p0,ρ0,T0的理想气体,现充入m质量,温度T的理想气体,如何计算平衡后的压强p 为什么2HI=H2+I2这个反应中反应前后气体的物质的量不变 两根蜡烛一根长,一根短,问如果将两根蜡烛点燃后,用一个封闭的玻璃罩罩住后,问哪个先灭? 如图,充满二氧化碳、氧气的试管,倒置于盛有澄清石灰水的水槽中,则最后在试管内收集到的气?E如图,充满二氧化碳、氧气的试管,倒置于盛有澄清石灰水的水槽中,则最后在试管内收集到的气 可逆反应2HI(g)=H2(g)+I2(g),达到平衡,增大HI的物质的量,HI的分解率和体积分数不变,为什么呢?,能不能解释清楚啊 在一个玻璃罩中有两只长短蜡烛那只先灭?为什么 将一充满二氧化碳气体的试管倒扣于盛有澄清石灰水的水槽中,观察到的现象有( ),(),原因有( ). door除了门还有什么意思新概念英语初阶第27课 响度跟距离发声体的__有关,响度的单位是__ 如何殺死蚊子 在水平桌面上,放上两只蜡烛,罩上玻璃罩,哪只蜡烛先灭? 冰熔化过程的特点是_______. 蚊子死蚊子 在桌上放1高1低蜡烛,点燃后罩上玻璃罩,问哪只蜡烛先灭为什么说出来 响度跟发声体的什么有关 蚊子为什么会死 在水平桌面上放两支蜡烛,罩上玻璃罩,哪个蜡烛先灭? 响度与发声体的什么及距发声体的什么有关?急. 如题,声音的响度与声音的材料有无关系音调呢? 在平桌面上放两根高低不同的蜡烛,罩上玻璃罩,哪根先灭可是我做实验的时候是长的先灭的,有什么原因使他们不同时间灭? 汽车常用检测设备有哪些? 声音传播的距离与音调和响度有关.那么哪个更重要,有没有什么公式? 放两根蜡烛,一根长一根短,同时罩上玻璃罩那根先息,为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么?为什么? 汽车钢材检测项目有哪些?所涉及到的检测设备有哪些?公司筹建汽车钢材检测实验室, 在同样距离内固体和气体传播的声音哪个响度大?RT,最好同时说明理由,那土地呢? door是什么意思 溶质对溶液的熔沸点有何影响 钠钾泵把葡萄糖从细胞膜外送到细胞膜外的能量是从哪来的? 声音的响度越大,传播的越远,这句话对吗? 为什么 溶质加入水后,溶液的熔沸点发生上什么变化? 细胞膜上的钠钾泵会机能逆转吗钠钾泵会通过水解ATP,使钠排出,钾进入细胞,这是它正常的机理.是否会出现钠钾泵把钠再泵回细胞内,把钾泵出细胞,同时合成ATP?如果细胞出现问题,比方说,细胞 声音传播距离与响度无关,为什么越远越听不清不懂 常用的化学溶剂有哪些?他们的熔沸点是多少? 细胞膜上钠钾泵所需能量主要来自哪个途径啊?选择题a葡萄糖的有氧氧化.b糖原异生c糖酵解d蛋白质分解e脂质氧化红细胞膜和普通细胞膜上钠钾泵能量来源有区别吗 音调越大声音传的越远,还是响度越大声音传播的越远.警报声之所以能传播很远的距离,是因为它的( )A.音调高 B.响度大 C.音色美 D.节奏快 一长一短两支蜡烛放在同一玻璃罩中燃烧,哪个先灭? 用盐酸除去铁钉上的铁锈,溶液变成黄色,不一会又观察到什么? 如果把长短不一的两支蜡烛点燃后用玻璃罩罩上,请问哪支先熄灭呢? 一长一短两支蜡烛在同一玻璃罩中燃烧,哪个先灭?答案是;条件改变,均有可能熄灭,为什么? 声音的高低称为_________,是由________决定的. 用玻璃罩罩住一支长蜡烛与一支短蜡烛,哪一支会先熄灭?玻璃罩小的话,玻璃罩大的话,(老师没有说明玻璃罩的大小)如何写实验报告?(第一分实验报告,希望能写的准确些)求达人解答请写 响度还与距离声源的什么有关 声音的高低由什么决定 如果不慎将油汤洒到衣服上,可以用什么方法除去?说一下你的依据得原理 响度还与声源的什么有关? 声音的高低和强弱由什么决定RT声音的高低叫什么?由发声体的什么决定,声音的强弱叫什么?它与发声体的什么有关 door什么意思 声音的响度不仅跟声源的——有关,还跟声源的——有关. 是不是声音的传播速度越快,响度就越大?为什么?
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘