Session 719: Core Image: Performance, Prototyping, and Pythonhtml
相信絕大多數 iOS 開發者對 Core Image 都不陌生,做爲系統標配的、異常強大的圖像處理庫,在絕大多數場景下都能知足 App 的圖像處理需求。並且,目前 Core Image 已經支持在 iOS 上作自定義 filter,很有趕超 GPUImage 的態勢(GPUImage 是目前 iOS 作圖像處理事實上的標杆)。加上 iOS12 蘋果打算 deprecate OpenGL 和 OpenGL ES, 推廣 Metal。那和 Metal 聯繫緊密的 Core Image 無疑更有勝算。算法
這個 Session 講的內容主要包括三個部分:swift
在講中間緩存以前,須要先複習一下 Core Image。在 Core Image 中,咱們可以對圖像鏈式的執行 Filter,以下圖所示:緩存
經過 Filter 的組合,咱們能夠實現一些複雜的圖像處理效果。創建 Filter 鏈的方法能夠參考以下代碼( 節選自 Core Image Programming Guide):架構
func applyFilterChain(to image: CIImage) -> CIImage {
// The CIPhotoEffectInstant filter takes only an input image
let colorFilter = CIFilter(name: "CIPhotoEffectProcess", withInputParameters:
[kCIInputImageKey: image])!
// Pass the result of the color filter into the Bloom filter
// and set its parameters for a glowy effect.
let bloomImage = colorFilter.outputImage!.applyingFilter("CIBloom",
withInputParameters: [
kCIInputRadiusKey: 10.0,
kCIInputIntensityKey: 1.0
])
// imageByCroppingToRect is a convenience method for
// creating the CICrop filter and accessing its outputImage.
let cropRect = CGRect(x: 350, y: 350, width: 150, height: 150)
let croppedImage = bloomImage.cropping(to: cropRect)
return croppedImage
}
複製代碼
整個過程很直觀,咱們將圖片喂到第一個 Filter,而後獲得第一個 Filter 的 outputImage
,而後再把該對象喂到第二個 Filter……以此類推創建 Filter 鏈。app
Core Image 的 Lazy框架
值得注意的一點是,當上述代碼執行時,圖像處理並無發生,只是 Core Image 內部進行了一些關係的創建,只有當圖像須要被渲染的時候,纔會實際去執行各個 Filter 的圖像處理過程。機器學習
由於有 Lazy 的特性,因此 Core Image 上最重要的一個優化就是 "自動鏈接(Filter Concatenation)", 由於最終圖像處理的過程都發生在全部 Filter 成鏈以後。因此 Core Image 能夠將鏈式的多個 Filter 合併 成一個來執行,省去沒必要要的開銷。以下圖所示:ide
中間緩存工具
如今回過頭來看這樣一個場景:
三個 Filter,第一個計算很耗時,而第三個的參數可讓用戶手動調節。這意味着每次用戶調節後都須要從新計算這三個 Filter。但其實前兩份 Filter 的參數是不變的,也就是說前兩個 Filter 的運算過程和結果都是不隨着用戶調整第三個 Filter 的參數改變而改變的。這裏重複的計算是否有可能進行優化呢?
咱們很容易就想到,咱們只須要把前兩次運算的結果 cache 下來就能夠了,以下圖所示:
可是上文提到,Core Image 會把 Filter 鏈自動合併爲一個 Filter,咱們如何訪問中間結果呢?
蘋果在 iOS12的 Core Image 中,給 CIImage
新增了一箇中間緩存的屬性(insertingIntermediate
), 來解決這個問題,以下圖所示:
咱們但願保存第二個 Filter 的結果,只須要在第二個 Filter 的 outputImage
調用 insertingIntermediate()
來生成一個新的 CIImage
傳到後面的流程便可。這樣第三個 Filter 的參數調整就不會致使前兩個 Filter 的重複計算。
怎麼作的呢? 其實就是自動合併的邏輯會根據 insertingIntermediate
進行調整。以下圖所示:
Core Image 的 CIContext
能夠設置是否要打開 cacheIntermediate
, 但此次新增的 insertingIntermediate
有更高的優先級。具體一些使用上的建議能夠參考下圖:
iOS 上支持自定義 Filter,自定義 Filter 使用 Kernal 語言進行開發(一種相似 GLSL 的腳本語言)。目前一共有兩種開發 CIKernel 程序的模式:
第一種是傳統的基於 CIKernal 開發語言進行編寫,而後編譯成 Metal 或者 GLSL 的方式,第二種是直接使用 Metal Shading 語言進行開發,而後在 build 期間就生成二進制的庫,執行階段 load 以後直接轉換爲 GPU 的指令。
目前由於蘋果主推 MPS(Metal Performance Shader), 因此方式一已經被標記爲 deprecated。
使用 Metal 來開發 CIKernel 的優點:
半精度浮點是純運算性質方面的優化,在 A11 芯片上運算更快,並且由於用到的寄存器小因此也有較大的優化空間。
接下來重點介紹一下按組讀寫。
假設咱們對左圖紅框像素作一個3x3的卷積運算,結果爲存入右邊的綠色框。顯而易見,對於每一個新的像素,都須要讀取輸入圖像9次像素值。
但若是是按組讀寫,以下圖所示。咱們一次性讀取16個像素來計算並寫入右邊的四個像素,那咱們整個過程當中寫了4次,讀取了16次。每一個新像素平均需讀取的數量爲4,比上述的單像素須要9次顯著下降。
按組讀寫的原理是很簡單的,接下來介紹一下若是咱們有一個以前使用 CIKernal Language 開發的 kernal,若是修改使其可以使用按組讀寫這樣高速的優化。
假設咱們的 kernal 以下圖所示:
第一步,轉換爲 metal:
第二步, 改造爲按組讀寫的模式。核心就是使用了 s.gatherX
來實現。
在使用了按組讀寫和半浮點經典的優化後,基本均可以獲得2倍的性能提高。
通常來說,一個濾鏡典型的研發流程是首先在電腦上進行快速原型的測試,以後再移植到生產環境,電腦上有大量的工具(OpenCV、SciPy、Numpy、Tensorflow 等等)來進行快速原型開發,而生產環境的技術棧倒是 Core Image, vImage,Metal等徹底不一樣的技術架構棧,這每每會致使一個問題:在電腦上原型測得好好的,結果到手機上效果卻撲街了。
蘋果爲了解決這個問題,發佈了一個神器 —— PyCoreImage。
初次看到這個名詞是否是感到很是穿越? 但其實很明顯,就是能夠在 Python 中調用 Core Image 的接口。
咱們在 prototype 的時候使用 Python + PyCoreImage 這樣的方式,那就最大程度的模擬了真實的運行環境,基本上移植到手機上效果也不會打折。並且最關鍵的是,只要學一個框架就行了啊,多的學不完啊!!!!
在使用 PyCoreImage 時,最關鍵是要了解 PyObjc 的用法,PyObjc 在 OS X 10.5 發佈,實現了在 Python 能夠調用 Objective-C 的代碼,其中最主要的轉換規則就是冒號變下劃線,具體能夠參考圖中的例子。
說回 PyCoreImage,其中的原理其實大概也能夠想到,以下圖所示,PyCoreImage 經過 PyObjC 和 macOS 的 Core Image 進行交互,並將結果輸出回 NumPy。
下圖中的代碼首先導入了一個圖片,而後對其應用高斯模糊的 Filter,而後將結果輸出到變量 bar
中。
剩下的更多關於 PyCoreImage 的用法能夠參考 Session 的 ppt,這裏再也不贅述。
圖像處理和計算機發展至今,已經大量經過使用機器學習和深度學習來提高算法的效果。Core Image 也對機器學習提供了很是有好的支持。
Core Image 如今能夠直接將圖片 apply 到一個 CoreML 的模型裏,相對於給 Core Image 的 Filter 連接上了深度學習的能力。
iOS12 中的 Core Image 提供了 CICoreMLModelFilter
類來將 CoreML 的 model 封裝成 Core Image 可以識別的 Filter 格式。
下圖是一個 ML 領域的經典應用的例子(風格遷移)
不過如今在網上還徹底搜不到 CICoreMLModelFilter
,(大霧
對於機器學習而言,訓練集的完整性、覆蓋度可以很大程度上決定最後模型的精確程度。可是現實狀況是,咱們每每沒有那麼多訓練集,在這樣的狀況下,學術界通常都採用對現有訓練集的圖片進行相應的變化來起到擴充數據集的做用。這類技術統稱數據填補(Data Augmentation
Core Image 對於這類任務天生支持的很好,支持包括如下幾種類型的變化:
如下是幾種使用 Core Image 的不一樣 Filter 來將一張圖變多多張訓練圖片的例子:
這個 Session 帶來的內容整體來講仍是激動人心的,雖然有的同窗可能以爲比較小,沒有那種顛覆式的創新,但對於從事圖像領域工做的同窗而言,毫無疑問這幾個工做都給人一種 mind opener 的感受,切實的反應了蘋果對於多媒體、用戶體驗這兩個領域很是超前的思考。