文章来源于《Functional Programing in Swift》,本系列仅仅是观后概括的一些内容

Wrapping Core Image

上一篇文章我们介绍了 高阶函数并且展示了函数是如何作为参数传递给其他函数。在本章中,我们将展示如何使用高阶函数对已有的 面向对象的API 进行函数式包装。

Core Image 是一个非常强大的图形处理的框架,但有些时候 它的API的使用有点笨重。CoreImage的API是松散类型—— image filters are configured using key-value coding.这样容易在类型以及参数的名称发生错误,导致运行时错误。我们新的API将会是安全和模块化的,利用类型来确保没有这样的运行时错误。

The Filter Type

CIFilter 用于创建图像过滤器(filter),当你初始化一个CIFilter对象时,你总是会提供一个输入图像通过 kCIInputImageKey,然后通过kCIOutputImageKey来获得过滤后的结果。然后您还可以用这个结果作为下个过滤器的输入。

我们将试着封装这些键值对的具体细节并提供一个安全、强类型的API给我们的用户。

typealias Filter = CIImage -> CIImage

这是我们要构建的基本类型

Building Filters

既然我们已经定义了过滤器的基本类型,我们可以开始为特定的过滤器定义相关的函数,这些便利的函数的参数需要一个特定filter和返回一个Filter。

func myFilter(/* parameters */) -> Filter

注意这个返回的Filter类型,这也是一个函数。此后,这个将帮组我们组装多个过滤器来实现我们想要的图像效果。

To make our lives a bit easier,我们将扩展CIFilter类 通过convenience initializer以及一个计算属性来取回output image

typealias Parameters = Dictionary<String, AnyObject>
extension CIFilter {
convenience init(name: String, parameters: Parameters) {
self.init(name: name)
setDefaults()
for (key, value: AnyObject) in parameters {
setValue(value, forKey: key)
}
} var outputImage: CIImage {
return self.valueForKey(kCIOutputImageKey) as CIImage
}
}

我们的便利构造器首先会调用我们的指定构造器。这个计算属性outputImage提供了一个简单的方法从filter对象中去获取output image 。通过这种计算属性,使用我们的API的用户再也不需要去关心如何获取这样的操作。

Blur(模糊)

blur filter 只需要一个blur radius 作为它的参数

func blur(radius: Double) -> Filter {
return { image in
let parameters: Parameters = [
kCIInputRadiusKey: radius,
kCIInputImageKey: image
]
let filter = CIFilter(name: "CIGaussianBlur",parameters:parameters)
return filter.outputImage
}
}

blur function 返回一个 function,这个函数的参数是CIImage 类型的image,返回一个新的image。正因为如此,模糊函数的返回值符合我们之前定义的Filter类型

(typealias Filter = CIImage -> CIImage).这个例子我们是在原来已存在Core Image中的filter只是进行了轻包装。我们可以反复使用相同的模式来创建我们自己的过滤功能。

Color Overlay

我们定义一个过滤器:在图像上覆盖我们选定的颜色。在Core Image中默认没有这样的filter,但是我们可以,当然,这也是通过已存在的过滤器组成的。

这两个构建块是我们要用颜色生成器filter(CIConstantColorGenerator)和source-over compositing filter(CISourceOverCompositing)。

func colorGenerator(color: NSColor) -> Filter {
return { _ in
let parameters: Parameters = [kCIInputColorKey: color]
let filter = CIFilter(name:"CIConstantColorGenerator",parameters: parameters)
return filter.outputImage
}
}

这个跟之前我们定义的blur filter相像,但有一点不同:这个constant color generator filter跟 imput image没有联系,因此我们不需要image 这个参数。

func compositeSourceOver(overlay: CIImage) -> Filter {
return { image in
let parameters: Parameters = [
kCIInputBackgroundImageKey: image,
kCIInputImageKey: overlay
]
let filter = CIFilter(name: "CISourceOverCompositing",parameters: parameters)
let cropRect = image.extent()
return filter.outputImage.imageByCroppingToRect(cropRect) }
}

这里我们将output  image的尺寸裁剪成input image的尺寸。这不是必须的,取决于这个我们过滤器的行为。然而,在这个例子中工作的非常好。

最后,我们将两个filter进行组合成color overlay filter:

func colorOverlay(color: NSColor) -> Filter {
return { image in
let overlay = colorGenerator(color)(image)
return compositeSourceOver(overlay)(image)
   }
}

Composing Filters

现在我们使用我们定义的filter运用到实际的图片上:first we blur the image, and then we put a red overlay on top.

let url = NSURL(string: "http://tinyurl.com/m74sldb")
let image = CIImage(contentsOfURL: url) // Now we can apply both filters to these by chaining them together: let blurRadius = 5.0
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let blurredImage = blur(blurRadius)(image)
let overlaidImage = colorOverlay(overlayColor)(blurredImage)

Function Composition

当然上面的filter组合我们可以用一个语句:

let result = colorOverlay(overlayColor)(blur(blurRadius)(image))

但是,代码很快变得不可读。有一个比较好的办法就是自定义一个filter组合的操作:

func composeFilters(filter1: Filter, filter2: Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
let myFilter1 = composeFilters(blur(blurRadius), colorOverlay(overlayColor))
let result1 = myFilter1(image)

我们可以更进一步,使得这个更可读,通过自定义操作符来组合

infix operator >>> { associativity left }

func >>> (filter1: Filter, filter2: Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)

当我们定义>>> 为左结合

Theoretical Background: Currying  柯里化函数

关于柯里化函数的相关知识,不多介绍,下面有相关链接:

swift的柯里化

Discussion

在这章中,我们会发现我们设计的API有这么几个优点:

1. safety----it is almost impossible to create runtime errors arising from undefined keys or failed casts

2. modularity ---it is easy to compose filters using the >>> operator.Doing so allows you to tease apart complex filters into smaller, simpler, reusable components. Additionally, composed filters have the exact same type as their building blocks, so you can use them interchangeably.

3. clarity ---- even if you have never used Core Image, you should be able to assemble simple filters using the functions we have defined. To access the results, you don’t need to know about special dictionary keys, such as kCIOutputImageKey, or worry about initializing certain keys, such as kCIInputImageKey or kCIInputRadiusKey. From the types alone, you can almost figure out how to use the API, even without further documentation

swift之函数式编程(三)的更多相关文章

  1. swift 之函数式编程(一)

    1. 什么是函数式编程? 函数式编程是阿隆佐思想的在现实世界中的实现, 它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及异变物件. 函数式编程的最重要基础是λ演算.而且λ演算的函數可以接受函 ...

  2. swift之函数式编程

    函数式编程初探 最近初学swift,和OC比,发现语言更现代,也有了更多的特性.如何写好swift代码,也许,熟练使用新特性写出更优秀的代码,就是答案.今天先从大的方向谈谈swift中的编程范式-函数 ...

  3. swift之函数式编程(四)

    文章内容来自<Functional Programing in Swift>,具体内容请到书中查阅 Map, Filter, Reduce Functions that take func ...

  4. swift之函数式编程(二)

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  5. 最通俗易懂的方式让你理解 Swift 的函数式编程

    函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...

  6. swift之函数式编程(五)

    文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...

  7. js函数式编程(三)-compose和pointFree

    compose即函数嵌套组合 组合compose在第一篇已经初见端倪,可以感受一下.compose函数的实现用闭包的方法.不完善实现如下: const compose = (f, g) => { ...

  8. Scala 中的函数式编程基础(三)

    主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...

  9. 转:JavaScript函数式编程(三)

    转:JavaScript函数式编程(三) 作者: Stark伟 这是完结篇了. 在第二篇文章里,我们介绍了 Maybe.Either.IO 等几种常见的 Functor,或许很多看完第二篇文章的人都会 ...

随机推荐

  1. Oracle-表的字段增加修改删除操作

    表结构修改 ALTER TABLE SCOTT.TEST RENAME TO TEST1--修改表名 ALTER TABLE SCOTT.TEST RENAME COLUMN NAME TO NAME ...

  2. ASP.Net开发WebAPI跨域访问(CORS)的精简流程

    1: Web.config里有一行: <remove name="OPTIONSVerbHandler" /> 这个要删除. 2: nuget安装Microsoft.A ...

  3. Maven下载、安装和配置(二)

    前言 在上篇博文[项目管理和构建]--Maven简介(一)中我们了解到maven是一种全新的项目构建方式,让我们的开发更加简单,高效.Maven主要做的是两件事: 统一开发规范与工具 统一管理jar包 ...

  4. web自动化1-selenium简介及环境搭建

    selenium 开源软件 支持多浏览器Firefox.Chrome.IE 跨平台Windows.Mac.Linux 多语言 java python Ruby php js 对web支持好,多种API ...

  5. 开源纯C#工控网关+组态软件

    一.   前言 在园子潜水也七八年了.说来惭愧,这么多年虽然一直自称.NET铁杆粉丝,然仅限于回几个不痛不痒的贴,既没有发布过代码,也没有写过文章. 看着.NET和C#在国外风生水起,国内却日趋没落, ...

  6. NOIP2017SummerTraining0710

    个人感受:这套题,题目泄露,没什么好打的,第一题刚开始题目理解错误,后来还行,第二道题,打了一个50还是60分的dp,第三道暴力过了小数据,拿了200分,排名15+. 问题 A: 七天使的通讯 时间限 ...

  7. 点聚合功能---基于ARCGIS RUNTIME SDK FOR ANDROID

    一直不更新博客的原因,如果一定要找一个,那就是忙,或者说懒癌犯了. 基于ArcGIS RunTime SDK for Android的点聚合功能,本来是我之前做过的一个系统里面的一个小部分,今天抽出一 ...

  8. .net窗体程序的基础知识及详细笔记

    第一章:初识Windows程序 1.1:第一个wondows程序 1.1.1:认识windows程序 Form1.cs:窗体文件:程序对窗体编写的代码一般都存放在这个文件(还有拖动控件时的操作和布局, ...

  9. Java历程-初学篇 Day06 循环结构

    前记:永远不要写死循环 一,while循环 先判断,再执行 while(条件){ //代码块; 迭代; } 示例: 二,do while语句 先执行一次,再判断 do{ //代码块; 迭代; }whi ...

  10. 【京东个人中心】——Nodejs/Ajax/HTML5/Mysql爬坑之注册与登录监听

    一.引言 在数据库和静态页面都创建好之后,下面就该接着完成后台Node.js监听注册和登录的部分了.这个部分主要使用的技术是:Node.js的Express框架和ajax异步请求.登录和注册的代码实现 ...