iOS圖形處理概論:OpenGL ES,Metal,Core Graphics,Core Image,GPUImage,OpenCV等

陳滿iOS發表於2019-03-04

前言

對於剛接觸iOS圖形相關框架的小白,有一些圖形框架在字面上和功能上非常容易混淆。這裡旨在總結一下各種框架,區分它們的概念和功能,以作日後進一步細分學習的指引。因而,本文並不會針對具體框架作詳解,只作區分引導,讀者可自行選擇方向繼續深造。為此,筆者總結了一張各種框架關係圖,如下所示:

iOS圖形處理相關概念集合

總的來說,iOS與圖形影像處理相關的框架都在這裡了:

    1. 介面圖形框架 — UIKit
    1. 核心動畫框架 — Core Animation
    1. 蘋果封裝的圖形框架 — Core Graphics & Quartz 2D
    1. 傳統跨平臺圖形框架 — OpenGL ES
    1. 蘋果最新力推的圖形框架 — Metal
    1. 適合圖片的蘋果濾鏡框架 — Core Image
    1. 適合視訊的第三方濾鏡方案 — GPUImage
    1. 遊戲引擎 — Scene Kit (3D) 和 Sprite Kit (2D)
    1. 計算機視覺在iOS的應用 — OpenCV for iOS

1. 介面圖形框架 — UIKit

UIKit

UIKit是一組Objective-C API,為線條圖形、Quartz影像和顏色操作提供Objective-C 封裝,並提供2D繪製、影像處理及使用者介面級別的動畫。

UIKit包括UIBezierPath(繪製線、角度、橢圓及其它圖形)、UIImage(顯示影像)、UIColor(顏色操作)、UIFont和UIScreen(提供字型和螢幕資訊)等類以及在點陣圖圖形環境、PDF圖形環境上進行繪製和 操作的功能等, 也提供對標準檢視的支援,也提供對列印功能的支援。

UIKit與Core Graphics的關係

在UIKit中,UIView類本身在繪製時自動建立一個圖形環境,即Core Graphics層的CGContext型別,作為當前的圖形繪製環境。在繪製時可以呼叫 UIGraphicsGetCurrentContext 函式獲得當前的圖形環境,例如:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    NSLog(@"%s",__func__);
    //1.獲取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    //2.描述路徑
    UIBezierPath * path = [UIBezierPath bezierPath];
    //起點
    [path moveToPoint:CGPointMake(10, 10)];
    //終點
    [path addLineToPoint:CGPointMake(100, 100)];
    //設定顏色
    [[UIColor whiteColor]setStroke];
    //3.新增路徑
    CGContextAddPath(contextRef, path.CGPath);
    //顯示路徑
    CGContextStrokePath(contextRef);
    
}
複製程式碼

這段程式碼就是在UIView的子類中呼叫 UIGraphicsGetCurrentContext 函式獲得當前的圖形環境,然後向該圖形環境新增路徑,最後繪製。

2. 核心動畫框架 — Core Animation

Core Animation

Core Animation 是一套Objective-C API,實現了一個高效能的複合引擎,並提供一個簡單易用的程式設計介面,給使用者UI新增平滑運動和動態反饋能力。

Core Animation 是 UIKit 實現動畫和變換的基礎,也負責檢視的複合功能。使用Core Animation可以實現定製動畫和細粒度的動畫控制,建立複雜的、支援動畫和變換的layered 2D檢視。

Core Animation 不屬於繪製系統,但它是以硬體複合和操作顯示內容的基礎設施。這個基礎設施的核心是layer物件,用來管理和操作顯示內容。在 iOS 中 每一個檢視都對應Core Animation的一個層物件,與檢視一樣,層之間也組織為層關係樹。一個層捕獲檢視內容為一個被影像硬體容易操作的點陣圖。在多數應用中層作為管理檢視的方式使用,但也可以建立獨立的層到一個層關係樹中來顯示檢視不夠支援的顯示內容。

OpenGL ES的內容也可以與Core Animation內容進行整合。

為了使用Core Animation實現動畫,可以修改 層的屬性值 來觸發一個action物件的執行,不同的action物件實現不同的動畫。

Core Animatio相關基類及子類

Core Animation 提供了一下一組應用可以採用的類來提供對不同動畫型別的支援:

  • CAAnimation 是一個抽象公共基類,CAAnimation採用CAMediaTiming 和CAAction協議為動畫提供時間(如週期、速度、重複次數等)和action行為(啟動、停止等)。
  • CAPropertyAnimation 是 CAAnimation的抽象子類,為動畫提供一個由一個key路徑規定的層屬性的支援;
  • CABasicAnimation 是CAPropertyAnimation的具體子類,為一個層屬性提供簡單插入能力。
  • CAKeyframeAnimation 也是CAPropertyAnimation的具體子類,提供key幀動畫支援。

3. 蘋果封裝的圖形框架 — Core Graphics & Quartz 2D

Core Graphics

Core Graphics是一套C-based API, 支援向量圖形,線、形狀、圖案、路徑、剃度、點陣圖影像和pdf 內容的繪製。

Quartz 2D

Quartz 2D是Core Graphics中的2D 繪製呈現引擎。Quartz是資源和裝置無關的,提供路徑繪製,anti-aliased呈現,剃度填充圖案,影像,透明繪製和透明層、遮蔽和陰影、顏色管理,座標轉換,字型、offscreen呈現、pdf文件建立、顯示和分析等功能。

Quartz 2D能夠與所有的圖形和動畫技術(如Core Animation, OpenGL ES, 和 UIKit 等)一起使用。

Quartz 2D採用paint模式進行繪製。

圖形環境Context

Quartz 2D中使用的圖形環境也由一個類CGContext表示。

在Quartz 2D中可以把一個圖形環境作為一個繪製目標。當使用Quartz 2D進行繪製時,所有裝置特定的特性被包含在你使用的特定型別的圖形環境中,因此通過給相同的影像操作函式提供不同的影像環境你就能夠畫相同的影像到不同的裝置上,因此做到了影像繪製的裝置無關性。

圖形環境Context是個比較抽象的東西,它不僅僅是一個可以繪製的圖層,還包含為當前圖層設定的引數,如陰影,線條粗細,繪製模式等。可以類比成一個新建的Photoshop圖層以及當前筆觸,顏色等配置。

對於移動平臺,有三種常見的圖形環境Context:

  • 點陣圖上下文(A bitmap graphics context):一般用於繪製圖片或者自定義控制元件。

    • View Graphics Context: 由UIView自動建立,你重寫UIView drawRect方法時,你的內容會畫在這個上下文上。
    • Bitmap Graphics Context: 繪製在該上下文的內容會以點陣形式儲存在一塊記憶體中。簡單說,就是為圖片開闢一塊記憶體,然後在裡面畫東西,上下文幫你把圖片記憶體抽象成一個Context(圖層)了。
  • PDF上下文(A PDF graphics context):用於生成pdf檔案。

  • 圖層上下文(A layer context):用於離屏繪製( offscreen drawing)。

Quartz 2D提供的主要類包括:
  • CGContext:表示一個圖形環境;
  • CGPath:使用向量圖形來建立路徑,並能夠填充和stroke;
  • CGImage:用來表示點陣圖;
  • CGLayer:用來表示一個能夠用於重複繪製和offscreen繪製的繪製層;
  • CGPattern:用來表示Pattern,用於重複繪製;
  • CGShading和 CGGradient:用於繪製剃度;
  • CGColor 和 CGColorSpace;用來進行顏色和顏色空間管理;
  • CGFont, 用於繪製文字;
  • CGPDFContentStream、CGPDFScanner、CGPDFPage、CGPDFObject,CGPDFStream, CGPDFString等用來進行pdf檔案的建立、解析和顯示。

4. 傳統跨平臺圖形框架 — OpenGL ES

OpenGL ES

OpenGL ES是一套多功能開放標準的用於嵌入系統的C-based的圖形庫,用於2D和3D資料的視覺化。OpenGL被設計用來轉換一組圖形呼叫功能到底層圖形硬體(GPU),由GPU執行圖形命令,用來實現複雜的圖形操作和運算,從而能夠高效能、高幀率利用GPU提供的2D和3D繪製能力。

OpenGL ES規範本身不定義繪製表面和繪製視窗,因此ios為了使用它必須提供和建立一個OpenGL ES 的呈現環境,建立和配置儲存繪製命令結果的framebuffer 及建立和配置一個或多個呈現目標。

EAGL

在 iOS中使用EAGL提供的EAGLContext類 來實現和提供一個呈現環境,用來保持OpenGL ES使用到的硬體狀態。 EAGL是一個Objective-C API,提供使OpenGL ES與Core Animation和UIKIT整合的介面。

在呼叫任何OpenGL ES 功能之前必須首先初始化一個EAGLContext 物件。每一個IOS應用的每一個執行緒都有一個當前context,在呼叫OpenGL ES函式時,使用或改變此context中的狀態。

EAGLContext 的類方法setCurrentContext: 用來設定當前執行緒的當前context。EAGLContext 的類方法currentContext 返回當前執行緒的當前context。在切換相同執行緒的兩個上下文之前,必須呼叫glFlush函式來確保先前已提交的命令被提交到圖形硬體中。

GLKit

可以採用不同的方式使用OpenGL ES以便呈現OpenGL ES內容到不同的目標:GLKit和CAEAGLLayer。

為了建立全螢幕的檢視或使OpenGL ES內容與UIKit檢視整合,可以使用GLKit。在使用GLKit時,GLKit提供的類GLKView類本身實現呈現目標及建立和維護一個framebuffer。

GLKit是一組Objective-C 類,為使用OpenGL ES 提供一個物件導向介面,用來簡化OpenGL ES應用的開發。

CAEAGLLayer

為了使OpenGL ES內容作為一個Core Animation層的部分內容時,可以使用CAEAGLLayer 作為呈現目標,並需要另外建立framebuffer以及自己實現和控制整個繪製流程。

GLKit支援四個3D應用開發的關鍵領域:

1) GLKView 和GLKViewController類提供一個標準的OpenGL ES檢視和相關聯的呈現迴圈。GLKView可以作為OpenGL ES內容的呈現目標,GLKViewController提供內容呈現的控制和動畫。檢視管理和維護一個framebuffer,應用只需在framebuffer進行繪畫即可。

2)GLKTextureLoader 為應用提供從IOS支援的各種影像格式的源自動載入紋理影像到OpenGL ES 影像環境的方式,並能夠進行適當的轉換,並支援同步和非同步載入方式。

3)數學運算庫,提供向量、矩陣、四元數的實現和矩陣堆疊操作等OpenGL ES 1.1功能。

4)Effect效果類提供標準的公共著色效果的實現。能夠配置效果和相關的頂點資料,然後建立和載入適當的著色器。GLKit 包括三個可配置著色效果類:GLKBaseEffect實現OpenGL ES 1.1規範中的關鍵的燈光和材料模式, GLKSkyboxEffect提供一個skybox效果的實現, GLKReflectionMapEffect 在GLKBaseEffect基礎上包括反射對映支援。

5. 蘋果最新力推的圖形框架 — Metal

Metal框架支援GPU硬體加速、高階3D圖形渲染以及大資料並行運算。且提供了先進而精簡的API來確保框架的細粒度(fine-grain),並且在組織架構、程式處理、圖形呈現、運算指令以及指令相關資料資源的管理上都支援底層控制。其核心目的是儘可能的減少CPU開銷,而將執行時產生的大部分負載交由GPU承擔

編寫基於底層圖形 API 的渲染引擎時,除了 Metal 以外的其他選擇還有 OpenGL 和 OpenGL ES。OpenGL 不僅支援包括 OSX,Windows,Linux 和 Android 在內的幾乎所有平臺,還有大量的教程,書籍和最佳實踐指南等資料。目前,Metal 的資源非常有限,並且僅限於搭載了 64 位處理器的 iPhone 和 iPad。但另外一方面,因為 OpenGL 的限制,其效能與 Metal 相比並不佔優勢,畢竟後者是專門用來解決這些問題的。

如果想要一個 iOS 上高效能的平行計算庫,答案非常簡單。Metal 是唯一的選擇。OpenGL 在 iOS 上是私有框架,而 Core Image (使用了 OpenGL) 對這樣的任務來說既不夠強大又不夠靈活。

6. 適合圖片的蘋果濾鏡框架 — Core Image

Core Image 是 iOS5 新加入到 iOS 平臺的一個影像處理框架,提供了強大高效的影像處理功能, 用來對基於畫素的影像進行操作與分析, 內建了很多強大的濾鏡(Filter) (目前數量超過了180種), 這些Filter 提供了各種各樣的效果, 並且還可以通過 濾鏡鏈 將各種效果的 Filter疊加 起來形成強大的自定義效果。

一個 濾鏡 是一個物件,有很多輸入和輸出,並執行一些變換。例如,模糊濾鏡可能需要輸入影像和一個模糊半徑來產生適當的模糊後的輸出影像。

一個 濾鏡鏈 是一個連結在一起的濾鏡網路,使得一個濾鏡的輸出可以是另一個濾鏡的輸入。以這種方式,可以實現精心製作的效果。

iOS8 之後更是支援自定義 CIFilter,可以定製滿足業務需求的複雜效果。

Core Image 的 API 主要就是三類:

  • CIImage 儲存影像資料的類,可以通過UIImage,影像檔案或者畫素資料來建立,包括未處理的畫素資料。
  • CIFilter 表示應用的濾鏡,這個框架中對圖片屬性進行細節處理的類。它對所有的畫素進行操作,用一些鍵-值設定來決定具體操作的程度。
  • CIContext 表示上下文,如 Core Graphics 以及 Core Data 中的上下文用於處理繪製渲染以及處理託管物件一樣,Core Image 的上下文也是實現對影像處理的具體物件。可以從其中取得圖片的資訊。

Core Image 的另外一個優勢,就是可以根據需求選擇 CPU 或者 GPU 來處理。

// 建立基於 CPU 的 CIContext 物件 (預設是基於 GPU,CPU 需要額外設定引數)
context = [CIContext contextWithOptions: [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:kCIContextUseSoftwareRenderer]];

// 建立基於 GPU 的 CIContext 物件
context = [CIContext contextWithOptions: nil];

// 建立基於 GPU 的 CIContext 物件
EAGLContext *eaglctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
context = [CIContext contextWithEAGLContext:eaglctx];
複製程式碼

7. 適合視訊的第三方濾鏡方案 — GPUImage

GPUImage 優勢:
最低支援 iOS 4.0,iOS 5.0 之後就支援自定義濾鏡。
在低端機型上,GPUImage 有更好的表現。(這個我沒用真正的裝置對比過,GPUImage 的主頁上是這麼說的)
GPUImage 在視訊處理上有更好的表現。
GPUImage 的程式碼完成公開,實現透明。
可以根據自己的業務需求,定製更加複雜的管線操作。可定製程度高。

8. 遊戲引擎 — Scene Kit (3D) 和 Sprite Kit (2D)

對於尋找遊戲引擎的開發者來說,Metal 不是最佳選擇。蘋果官方的的 Scene Kit (3D) 和 Sprite Kit (2D) 是更好的選擇。這些 API 提供了包括物理模擬在內的更高階別的遊戲引擎。

另外還有功能更全面的 3D 引擎,例如 Epic 的 Unreal Engine 或 Unity,二者都是跨平臺的。使用這些引擎,你無需直接使用 Metal 的 API,就可以從 Metal 中獲益。

2D渲染 — SpriteKit

SpriteKit 讓開發者可以開發高效能、省電節能的 2D 遊戲。在 iOS 8 中,我們新添了多項增強功能,這將使 2D 遊戲體驗更加精彩。這些新技術有助於使遊戲角色的動作更加自然,並讓開發者可以更輕鬆地在遊戲中加入力場、檢測碰撞和生成新的燈光效果。

3D渲染 — SceneKit

SceneKit 專為休閒 3D 遊戲而設計,可讓開發者渲染 3D 遊戲場景。SceneKit 內建了物理引擎、粒子發生器和各種易用工具,可以輕鬆快捷地為 3D 物體編寫動作。不僅如此,它還與 SpriteKit 完全整合,所以開發者可以直接在 3D 遊戲中加入 SpriteKit 的素材。

9. 計算機視覺在iOS的應用 — OpenCV for iOS

OpenCV 的 API 是 C++ 的。它由不同的模組組成,這些模組中包含範圍極為廣泛的各種方法,從底層的影像顏色空間轉換到高層的機器學習工具。這裡提供一個入門PDF文件 下載入口

使用 C++ API 並不是絕大多數 iOS 開發者每天都做的事,你需要使用 Objective-C++ 檔案來呼叫 OpenCV 的函式。 也就是說,你不能在 Swift 或者 Objective-C 語言內呼叫 OpenCV 的函式。 這篇 OpenCV 的 iOS 教程告訴你只要把所有用到 OpenCV 的類的檔案字尾名改為 .mm 就行了,包括檢視控制器類也是如此。這麼幹或許能行得通,卻不是什麼好主意。正確的方式是給所有你要在 app 中使用到的 OpenCV 功能寫一層 Objective-C++ 封裝。這些 Objective-C++ 封裝把 OpenCV 的 C++ API 轉化為安全的 Objective-C API,以方便地在所有 Objective-C 類中使用。

走封裝的路子,你的工程中就可以只在這些封裝中呼叫 C++ 程式碼,從而避免掉很多讓人頭痛的問題,比如直接改檔案字尾名會因為在錯誤的檔案中引用了一個 C++ 標頭檔案而產生難以追蹤的編譯錯誤。

OpenCV 宣告瞭名稱空間 cv,因此 OpenCV 的類的前面會有個 cv:: 字首,就像 cv::Mat、 cv::Algorithm 等等。你也可以在 .mm 檔案中使用 using namespace cv 來避免在一堆類名前使用 cv::字首。但是,在某些類名前你必須使用名稱空間字首,比如 cv::Rect 和 cv::Point,因為它們會跟定義在 MacTypes.h 中的 Rect 和 Point 相沖突。儘管這只是個人偏好問題,我還是偏向在任何地方都使用 cv::以保持一致性。

一般講的OpenCV是基於CPU的,相關資料和支援也是最完善的。當然,也有基於GPU模組,但提供的介面非常坑爹,相當一部分不支援浮點型別(像histogram、integral這類常用的都不支援);又如,遇到閾值判斷的地方,就必須傳回cpu處理,因為gpu函式都是並行處理的,每改寫完一個演算法模組,就測試一下執行效率,有的時候是振奮人心,有的時候則是當頭棒喝——比CPU還慢。詳情可參閱 這裡

10. 參考文獻

  • Core Animations

https://www.sohu.com/a/203987045_468740
https://blog.csdn.net/huangznian/article/details/42919221
https://www.jianshu.com/p/446a6b72f981
https://www.jianshu.com/p/439e158b44de

  • Metal

https://juejin.im/post/59ad5d6551882539255b4809
https://www.jianshu.com/p/ce53d0178f20
https://blog.csdn.net/pizi0475/article/details/50232029
https://baike.baidu.com/item/Metal/10917053?fr=aladdin
https://zhuanlan.zhihu.com/p/24623380?utm_source=tuicool&utm_medium=referral

  • Core Image

https://objccn.io/issue-21-6/
http://colin1994.github.io/2016/10/21/Core-Image-OverView/?utm_source=tuicool&utm_medium=referral
https://blog.csdn.net/jingcheng345413/article/details/54967640
https://www.cnblogs.com/try2do-neo/p/3601546.html

  • Core Graphics

https://www.jianshu.com/p/e7a50dcbe7c8
https://www.jianshu.com/p/55cc1587e618
https://www.jianshu.com/p/494c57f49479
https://my.oschina.net/flyfishbay/blog/1504698

  • OpenCV

https://blog.csdn.net/zhonggaorong/article/details/78191514
http://www.opencv.org.cn/forum.php?mod=viewthread&tid=33549
https://blog.csdn.net/kelvin_yan/article/details/41804357
https://blog.csdn.net/sinat_31135199/article/details/53053188
https://blog.csdn.net/liyuefeilong/article/details/46292339

  • GPUImage

https://blog.csdn.net/fanbird2008/article/details/51707430

  • 其它

https://blog.csdn.net/goohong/article/details/40743883

相關文章