Functional Programming in Swift(二)

原文首發(fā)于我的blog:https://chengwey.com

Chapter 3 Wrapping Core Image

本文是《Functional Programming in Swift》中第三章的筆記夫否,如果你感興趣照筑,請購買英文原版针余。

上一節(jié)討論了高階函數(shù)的概念,并展示了函數(shù)作為參數(shù)傳遞夺刑。本節(jié)我們展示如何使用高階函數(shù)來封裝一些面向?qū)ο蟮?API 。Core Image 是一個很強(qiáng)大圖像處理框架,我們就用他來開刀重寫一個 Filter贮泞。

<h2 id='TheFilterType'>1. The Filter Type</h2>
我們使用 Core Image中 的 CIFilter 類來創(chuàng)建 image filters资昧。具體過程為:

  • 1.初始化一個 CIFilter 對象實例
  • 2.提供一個輸入 image酬土,使用字典進(jìn)行封裝,key 為 kCIInputImageKey
  • 3.獲取結(jié)果為一個字典格带,使用 kCIOutputImageKey 得到最終 image
  • 4.然后將該 image 繼續(xù)作為下一個 filter 的輸入?yún)?shù)

我們改寫的 API 將封裝這些 key-value撤缴,提供更安全、便利的 API 給用戶叽唱。我們首先定義自己的 Filter 類型:

typealias Filter = CIImage -> CIImage

<h2 id='BuildingFilters'>2. Building Filters</h2>
我們有了基本的 Filter 類型腹泌,就可以開始定義一些特定的 filter 了,我們可以定義一些函數(shù)尔觉,根據(jù)不同的參數(shù)凉袱,輸出不同的 filter。具體函數(shù)形式如下:

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

Blur

首先來定義高斯模糊濾鏡侦铜,該濾鏡只需要一個模糊半徑(blur radius)做參數(shù)

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

這個 blur 函數(shù)的返回值是 Filter 類型专甩,該類型正是我們之前定義的,使用一個CIImage 類型的 image 做輸入钉稍,并返回一個新的 image涤躲。這個例子我們簡單封裝了 Core Image 中的 API,我們可以使用這種方式來創(chuàng)建自己的 filter functions贡未。

Color Overlay

接著我們來實現(xiàn)一個圖層顏色种樱,Core Image 沒有默認(rèn)的相關(guān)濾鏡,但我們可以通過組合現(xiàn)有 filters 的方式來實現(xiàn)俊卤。我們具體構(gòu)建這個 color overlay 需要使用兩個現(xiàn)成的 Filter:

  • color generator filter (CIConstantColorGenerator)
  • source-over compositing filter (CISourceOverCompositing)
func colorGenerator(color: UIColor) -> Filter {
  return { _ in
    let parameters = [kCIInputColorKey: color]
    let filter = CIFilter(name: "CIConstantColorGenerator",
                        withInputParameters: parameters)
    return filter.outputImage
  }
}

該函數(shù)與之前的高斯模糊函數(shù)只有一點(diǎn)不同嫩挤,color generator filter 不檢查input imgage,因此我們使用了一個無名參數(shù)"_"來強(qiáng)調(diào) image 參數(shù)被無視忽略掉了消恍。

接著我們繼續(xù)定義 composite filter:

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

這里岂昭,我們修剪了輸出 image 的尺寸,使其和輸入 image 尺寸匹配狠怨,這不是必要步驟约啊,但卻是一個更好的選項邑遏。最后我們合并這兩個filter:

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

該函數(shù)中的 overlay 可以看做是由 UIColor ->(CIImage -> CIImage) 生成的一個image,然后傳入一個 CIImage ->(CIImage -> CIImage) 函數(shù)得到最終結(jié)果恰矩。

<h2 id='ComposingFilters'>3. Composing Filters</h2>
現(xiàn)在有了 blur和color overlay filter记盒,我們可以把他們合在一起用:首先 blur 一張圖片,然后把紅色圖層覆蓋在圖片上:

先載入一張圖片

let url = NSURL(string: "http://tinyurl.com/m74sldb"); 
let image = CIImage(contentsOfURL: url)

我們可以用鏈?zhǔn)浇Y(jié)構(gòu)將所有的 filter 連起來

let blurRadius = 5.0
let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2) 
let blurredImage = blur(blurRadius)(image)
let overlaidImage = colorOverlay(overlayColor)(blurredImage)

Function Composition

當(dāng)然外傅,我們也可以用一行代碼來調(diào)用兩個 filter:

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

不過這樣可讀性就變得比較差孽鸡,更好的方式是自定義一個 filter 合成函數(shù):

func composeFilters(filter1: Filter, filter2: Filter) -> Filter { 
    return { img in filter2(filter1(img)) }
}

有了上面的composeFilters函數(shù),我們就可以任意合成我們想要的 filter 了栏豺,比如我們可以合成一個 myFilter1 濾鏡:

let myFilter1 = composeFilters(blur(blurRadius),
                               colorOverlay(overlayColor))
let result1 = myFilter1(image)

讓我們來更進(jìn)一步增加可讀性彬碱,swift 允許我們自定義操作符,我們來定義一個 “>>>” 運(yùn)算符:

infix operator >>> { associativity left } //表運(yùn)算符順序
func >>> (filter1: Filter, filter2: Filter) -> Filter { 
    return { img in filter2(filter1(img)) }
}

//使用
let myFilter2 = blur(blurRadius) >>> colorOverlay(overlayColor)
let result2 = myFilter2(image)

<h2 id='TheoreticalBackgroundCurrying'>4. TheoreticalBackground:Currying</h2>
這一章奥洼,我們看到了有兩種方式來定義一個帶兩個參數(shù)的 function:

//第一種形式為常見的:
func add1(x: Int, y: Int) -> Int { 
    return x + y
}

//第二種方式:
func add2(x: Int) -> (Int -> Int) { 
    return { y in return x + y }
}

add2帶一個參數(shù) x巷疼,返回一個閉包,然后繼續(xù)帶一個參數(shù) y灵奖。二者調(diào)用方式也不同:

  • add1(1, 2)
  • add2(1)(2)

swift中的函數(shù)箭頭 “->” 是右相關(guān)的嚼沿,也就是說類型 “ A -> B -> C ” 可以看做是 “ A -> (B -> C) ”。add1和add2 展示了一個接受多個參數(shù)的函數(shù)轉(zhuǎn)換成一系列只接受單個參數(shù)的函數(shù)瓷患,這個過程就稱為柯理化(add2 是 add1 的柯理化版本)

還有第三種版本 curry functions

func add3(x: Int)(y: Int) -> Int { 
    return x + y
}

// 調(diào)用
add3(1)(y: 2)
>3

這里需要主要的就是骡尽,調(diào)用時必須明確提供第二參數(shù)的參數(shù)名(這里是y)

為什么要使用“柯理化”呢,目前我們都是將函數(shù)作為參數(shù)傳給另一個函數(shù)擅编。如果我們使用“非柯理化”的函數(shù)攀细,比如 add1,我們就要一次提供所有的參數(shù)爱态。而對于“柯理化”的函數(shù)谭贪,比如 add2,我們可以選擇:提供1個或2個參數(shù)锦担。我們本章創(chuàng)建的 filter 都是“柯理化”的俭识,他們都有一個附加的 image 做參數(shù)。這種方式寫出的 filter洞渔,我們也很容易使用操作符 ">>>" 來組合他們套媚。當(dāng)然,我們也可以寫出“非柯理化”的filter磁椒,但最后產(chǎn)生的code將變得笨重堤瘤。

<h2 id='Discussion'>5. Discussion</h2>
本章再一次展示了如何將復(fù)雜的code簡化成許 code snippets,而這些 code snippets 又能通過 function 很方便地進(jìn)行重組衷快。還展示了高階函數(shù)在實際案例中的應(yīng)用宙橱。

本章通過這種方式設(shè)計的 API 有這么幾個優(yōu)勢:

  • Safety 不會因為未定義的 keys 或類型轉(zhuǎn)換失敗而產(chǎn)生 runtime error
  • Modularity 很方便地使用操作符 >>> 進(jìn)行組合姨俩,這樣做允許你將復(fù)雜的 filter 拆分成功能單一蘸拔、小巧师郑、可重用的子filter。加之调窍,組合后的filters和他的子模塊擁有相同的類型宝冕,所以你可以交替使用他們。
  • Clarity 盡管之前你可能沒有使用過 Core Image邓萨,但你不出5分鐘就能學(xué)會我們定義的這套 API:使用 function 來裝配這些簡單的 filters地梨。為了得到最終結(jié)果,你不需要知道各種“Key”( eg: kCIInputImageKey or kCIInputRadiusKey...etc )缔恳,你甚至不需要看文檔就能學(xué)會如何使用這套API宝剖。

我們的 API 展示了一連串 functions 可以定義、組合成一個復(fù)雜的 filter歉甚,而其中每一個 filter 都是是安全万细、孤立且可以重用的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纸泄,一起剝皮案震驚了整個濱河市赖钞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聘裁,老刑警劉巖雪营,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異衡便,居然都是意外死亡献起,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門镣陕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來征唬,“玉大人,你說我怎么就攤上這事茁彭∽芎” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵理肺,是天一觀的道長摄闸。 經(jīng)常有香客問我,道長妹萨,這世上最難降的妖魔是什么年枕? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮乎完,結(jié)果婚禮上熏兄,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好摩桶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布桥状。 她就那樣靜靜地躺著,像睡著了一般硝清。 火紅的嫁衣襯著肌膚如雪辅斟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天芦拿,我揣著相機(jī)與錄音士飒,去河邊找鬼。 笑死蔗崎,一個胖子當(dāng)著我的面吹牛酵幕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缓苛,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼裙盾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了他嫡?” 一聲冷哼從身側(cè)響起番官,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钢属,沒想到半個月后徘熔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淆党,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年酷师,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片染乌。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡山孔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荷憋,到底是詐尸還是另有隱情台颠,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布勒庄,位于F島的核電站串前,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏实蔽。R本人自食惡果不足惜荡碾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望局装。 院中可真熱鬧坛吁,春花似錦劳殖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至女坑,卻和暖如春填具,著一層夾襖步出監(jiān)牢的瞬間统舀,已是汗流浹背匆骗。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留誉简,地道東北人碉就。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像闷串,于是被迫代替她去往敵國和親瓮钥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Core Image是一個強(qiáng)大的框架烹吵,它能夠讓你輕松地對圖像進(jìn)行過濾碉熄。你能夠通過修改圖像的飽和度、色調(diào)或曝光率來獲...
    木易林1閱讀 1,137評論 0 1
  • 許多UIView的子類肋拔,如一個UIButton或一個UILabel锈津,它們知道怎么繪制自己。遲早凉蜂,你也將想要做一些自...
    shenzhenboy閱讀 1,644評論 2 8
  • 前言 最近在研究 Core Image 自定義 Filter 相關(guān)內(nèi)容琼梆,重新學(xué)習(xí)了 Core Image,對 Co...
    泥孩兒0107閱讀 774評論 0 4
  • 很多畢業(yè)剛出來的小姑娘們,都不太懂化妝纫雁,剛出社會煌往,想要漂亮但也沒多少經(jīng)濟(jì)基礎(chǔ)。先是化眼睛還是眉毛轧邪,用什么產(chǎn)品好携冤,什...
    我獨(dú)一你無二閱讀 885評論 0 5
  • 一寸 幸福總讓女人眼神就算低頭也煙波流蕩闲勺, 最素顏的臉上藏不住桃花暗香綻放曾棕, 原來...
    塰謿閱讀 457評論 0 8