Core Image是蘋果官方提供的圖像處理框架,通過豐富的built-in(內置)或自定義Filter(過濾器)高效處理靜態圖片、動態圖片或視頻。開發者還可以通過構造Filter鏈或自定義Core Image Kernel來實現更豐富的效果。
在WWDC20中,蘋果官方針對Core Image技術在以下三方面做了優化:Core Image對視頻/動圖的支持、基於Metal構建Core Image (CI) Kernel以及Core Image的Debug支持。
這三方面會在下文逐一提到,文末筆者也會淺談Core Image在手淘圖片庫中的應用可能以及對Core Image技術的展望。
優化Core Image對視頻/動圖的支持
創建CIContext
創建 CIContext 時,需要遵循一個 view 一個 context 的原則。由於視頻的每一幀都會發生變化,將CIContext的cacheIntermediates屬性設置爲false可以大大減少內存消耗。
如果在使用Core Image時將同時運用Metal(作爲輸入或輸出),通過設置MTLCommandQueue屬性創建CIContext將會是較好選擇。在不使用MTLCommandQueue的情況下,每一個Metal或CoreImage執行的任務都在不同隊列中並以wait命令分隔開,導致任務執行效率低。通過設置MTLCommandQueue創建的CIContext和相應的Metal任務在同一隊列中,能提高app的運行效率。
編寫Core Image Kernel(在Metal中實現)
爲了將效果處理得更豐富,通過Metal來實現自定義CI Kernel是個高效的選擇。蘋果官方提供了的許多方便部署的內置工具(都通過Metal實現),如內置CI濾鏡。通過Metal實現自定義CI Kernel,不僅app的runtime編譯時間將會大大減少(這段工作會移至app構建完成後進行),開發者還能獲得高性能語言特性(如gather-reads、group-writes、半精度浮點數)、高效開發體驗(如縮進檢查、縮進高光)等功能。
選擇合適的View類
如果要對視頻/動圖應用特效,靜態內容View如UIImageView或NSImageView應當被避免。AVPlayerView和MetalKit View(MTKView)是個兩個不錯的選擇。前者爲簡單選擇,後者爲進階選擇。
使用AVPlayerView時,需要創建AVMutableVideoComposition對象,CI濾鏡在block中執行圖像處理任務。在進行斷點debug時,通過點擊CIImage對象地址右側的眼睛圖示可以瀏覽CI濾鏡處理流程的詳細信息。
官方提供的案例中,Core Image還將10位的HDR視頻幀數據自動從HLG轉化成了Core Image working space。
使用MTKView時,開發者需要以frame和device作爲參數重載init方法。VIew對應的CIContext也將在init函數中被創建。如果我們在macOS中開發支持HDR的view,color-Pixel-Format屬性需要被設定爲rgba16Float,wants-Extended-Dynamic-Range-Content屬性需要被設定爲true。設定完init方法後,開發者需要實現draw-in view方法。需要注意的是,此處並未直接將Metal材質傳入CIRenderDestination函數,而是創建了一個會返回texture的block。這使得CIContext能在前面的幀尚未完成時將Metal工作入隊。之後該方法會執行渲染任務(至指定目的地)並創建command buffer將當前繪製結果渲染至view。
本人也親自嘗試了通過Core Image處理視頻的整個流程。以下案例使用CIVortexDistortion濾鏡對視頻進行逐幀處理並渲染,展示內容包含核心代碼、原視頻、CI濾鏡處理後視頻以及斷點測試的濾鏡逐幀處理圖示。
let filepath: String? = Bundle.main.path(forResource: "test_video", ofType: "MOV")
let fileURL = URL.init(fileURLWithPath: filepath!)
let asset = AVAsset(url: fileURL)
let item = AVPlayerItem(asset: asset)
item.videoComposition = AVMutableVideoComposition(asset: asset) { request in
let filter = CIFilter(name: "CIVortexDistortion")
filter?.setValue(request.sourceImage, forKey: kCIInputImageKey)
filter?.setValue(NSNumber(400), forKey: "inputAngle")
filter?.setValue(NSNumber(1200), forKey: "inputRadius")
filter?.setValue(CIVector(x: 700, y: 400), forKey: "inputCenter")
let out = filter?.outputImage
request.finish(with: out ?? request.sourceImage, context: nil)
}
avPlayer = AVPlayer(playerItem: item)
基於Metal構建Core Image Kernel
使用CI Kernel有諸多優勢,包括上文提及的縮短runtime編譯時間、高性能語言特性(如gather-reads、group-writes、半精度浮點數)、高效開發體驗(如縮進檢查、縮進高光)。基於Metal構建CI Kernel有5步流程,會在下文進行逐一介紹。
在項目中增加自定義構建規則
蘋果官方推薦在項目target中增加兩項自定義構建規則。第一個構建規則針對以“.ci.metal”爲後綴名的文件。該構建規則會創建一個以“.ci.air”爲後綴名的二進制輸出文件。
第二個構建規則針對以“.ci.air”爲後綴名的文件(上一個構建規則的輸出結果)。該構建規則會在app的資源文件夾內創建以“.ci.metallib”爲後綴名的輸出文件。
在項目中增加.ci.metal資源
在Xcode提供的創建面板中選擇Metal File即可。開發者對Metal File進行命名時需要以“.ci”作爲後綴名,這樣項目中新生成的文件會以“.ci.metal”作爲後綴名。
編寫Metal Kernel
便攜Metal Kernel需要include CoreImage.h頭文件,用來使用Metal和Core Image提供的各種類。官方提供的範例編寫了一個CIColorKernel,輸入參數爲coreimage::samle_t對象(表示輸入圖片的一個像素)、time和coreimage::destination對象,返回float4像素。
蘋果官方爲開發者提供了描述CI Kernel中Metal Shader語言的文檔,詳情見「 Metal Shading Language for Core Image Kernels」① 。
加載Kernel並應用於新圖像(基於Swift)
Kernel會被CI濾鏡的子類使用。蘋果官方推薦開發者在實例化濾鏡的CIKernel對象時使用靜態屬性(static property),這種情況下加載metallib資源的工作僅會執行一次(在首次需要時)。CI濾鏡的子類也必須重載輸出圖片的屬性,Kernel將在getter中進行圖像處理並創建新圖像。
Core Image的Debug支持
蘋果官方在WWDC20詳細介紹了Debug特性:CI_PRINT_TREE。
什麼是CI_PRINT_TREE
CI_PRINT_TREE的基礎框架與Xcode提供的Core Image Quick Look支持相同。Core Image Quick Look爲開發者提供了快捷可視化的Core Image圖片(詳見上文圖三),而CI_PRINT_TREE支持幾種不同的模式和選項用來查看Core Image如何優化和渲染圖像。
如何啓用CI_PRINT_TREE
蘋果官方提供了CI_PRINT_TREE的兩種啓動方式。最常用的方法是編輯Xcode target scheme,在Arugments窗體下的環境變量列表中加入CI_PRINT_TREE並設置值。另一種方法是在Terminal.app中通過命令行啓動CI_PRINT_TREE(需要在執行應用程序前設定)。
如何控制CI_PRINT_TREE
CI_PRINT_TREE的字符串格式爲“<graph type> <output type> <options>”
- graph type: 表示Core Image render的若干stage,包括type-1初始圖像(有助於查看被使用的色彩空間)、type-2優化後的圖像(有助於查看core image對render的優化效果)、type-4級聯圖像(有助於查看各stage如何級聯於GPU程序,以便了解render需要多少中間緩存)以及type-7(輸出圖像type1、2和4)。
- output type: 輸出格式可以是pdf或png。在macOS上trees會被存儲在臨時項目文件夾,在iOS上trees會被存儲在文檔(Documents)目錄下。如果output type沒有確定,core image會把tree以緊湊文本格式輸出在標準輸出(stdout)。通過設置CI_LOG_FILE=“oslog”,文本也可以前往Console.app(在iOS開發中更爲方便)。
- options: 對於CI_PRINT_TREE,開發者可以設定額外的選項。如通過設定context==name來限制輸出(僅輸出名字相同的context),或是通過設定frame-n來框定具體輸出context的哪一幀。更多option及詳情請見圖十一。設定option對debug能提供很大幫助,但也需謹慎使用,因爲生產這些文件需要額外的時間和內存。
如何獲得CI_PRINT_TREE文件
在macOS中,開發者只需要進入“/tmp”文件夾就能找到生成的CI_PRINT_TREE文件。需要注意的是沙盒應用會使用特有的臨時存儲文件夾。
在iOS中,開發者需要將Custom iOS Target Properties中的“Application supports iTunes file sharing”項設爲YES(圖十三)。這樣生成的CI_PRINT_TREE文件可以在連接中的iOS設備上被找到並拖拽至macOS存儲中。
如何解釋CI_PRINT_TREE文件
讀CI_PRINT_TREE時,需要遵循以下規則:
- 輸入在底層,輸出在頂層
- 綠色節點代表捲曲內核(warp kernel),紅色節點代表顏色內核(color kernel)
- 在樹的初始位置(initial tree)很容易找到顏色搭配節點(colormatch nodes),裏面記錄了搭配前後的色彩空間名稱。蘋果官方提供的案例爲ITUR_2100_HLG_to_workingspace,即HLG色彩空間轉化爲Core Image線性色彩空間。
- 每個節點會顯示Region of Interest(ROI),表示該節點在render中被使用的範圍。
如果開發者在CI_PRINT_TREE控制字符串中選擇type-4並在option中設定dump-intermediates,產生的級聯圖片會展示中間緩存的每一次pass(除了output pass)及其耗時、像素點數量和像素點格式(用來查找耗時大、佔內存大的pass)。這對render內追蹤錯誤非常有幫助。如果樹中沒有展示中間圖,那麼說明這張圖在先前渲染的時候已被緩存,因此Core Image沒有渲染它的必要。
Core Image在手淘圖片庫中的應用可能
手淘圖片庫中的CDN圖片適配處理庫(TBCDNImage)的核心目的是爲不同終端設備、網絡環境下的圖片展示提供最優解。目前考慮的維度主要是終端設備硬件和網絡狀態,考慮的參數則是圖片尺寸、壓縮比率、銳化等圖片屬性。隨着蘋果在Core Image、端智能(CoreML)、硬件支持(自研芯片)等方面進行技術提升,手淘的CDN圖片適配處理庫可以考慮增加“圖片內容”作爲新的維度,增加亮度、對比度、濾鏡、圖片種類等新參數。以下爲部分應用場景:
- 識別亮度較暗的圖片,提升亮度做CDN圖片適配處理
- 判斷圖片內容種類(如美食),根據不同內容種類的圖片增加適合的濾鏡做CDN圖片適配處理
- 根據移動終端設備屏幕亮度(或深/淺色模式)修改圖片色調做CDN圖片適配處理,達到護眼效果
對Core Image技術的展望
總結全文,WWDC20對Core Image技術的提升主要在三方面:
- 優化CI對視頻/動圖的支持,包括開發流程簡化、逐幀處理性能提升等。
- 允許開發者更自由的構建Core Image Kernel,使CI的特效處理更加豐富
- 針對CI開發流程提供更高效的Debug支持
隨着蘋果未來自研芯片的底層硬件支持將提供視頻流流暢的逐幀處理與渲染。筆者認爲Core Image技術將會在以下場景有較大應用價值:
- 直播濾鏡/特效功能原生化(擺脫自研或第三方API),實現質量更高的實時濾鏡渲染
- 視頻拍攝增加濾鏡功能(如淘寶或鹹魚的商品視頻錄製)
參考:
① https://developer.apple.com/metal/MetalCIKLReference6.pdf
② https://github.com/duzhaoquan/ImagesVideoFilters
本文轉載自公衆號淘系技術(ID:AlibabaMTT)。
原文鏈接: