- 操作系統:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 編程語言:C++11
描述
G-API 是一個異構框架,提供了統一的 API 來使用多個支持的后端編程圖像處理流水線。
關鍵的設計理念是在指定使用哪些內核和設備時保持流水線代碼本身的平臺中立性,這通過在圖編譯(配置)時使用額外的參數來實現。這一要求導致了如下架構:
-
API層 – 這是頂層,實現了G-API的公共接口、其構建塊及其語義。當用戶使用G-API構建一個流水線時,他直接與此層交互,用戶操作的實體(如cv::GMat或cv::GComputation)由這一層提供。
-
圖編譯器層 – 這是中間層,它將用戶的計算展開成圖,然后對其應用一系列轉換(例如優化)。這一層建立在ADE框架之上。
-
后端層 – 這是最底層,列出了多個后端。與上述兩層相比,后端與低級平臺細節高度耦合,每個后端代表一個特定的平臺。后端對來自圖編譯器的處理過的圖進行操作,并針對特定平臺或設備最優地執行這個圖。
API層
API層是用戶在定義和使用流水線(在G-API術語中稱為計算)時直接交互的部分。API層定義了一組G-API動態對象,這些對象可以用作圖中的輸入、輸出和中間數據對象:
- cv::GMat
- cv::GScalar
- cv::GArray(模板類)
API層還指定了可以應用于這些數據對象的一系列操作——即所謂的內核。有關G-API默認提供的操作的詳細信息,請參見G-API的核心和imgproc命名空間。
G-API不限于這些操作——用戶可以使用特殊的宏G_TYPED_KERNEL()輕松定義自己的內核。
API層還負責在創建流水線時對操作參數進行編組和存儲。除了上述提到的G-API動態對象外,操作還可以接受任意參數(更多詳情見此處),因此API層會在執行時捕獲這些值并內部存儲。
最后,cv::GComputation和cv::GCompiled是API層中剩余的重要組件。前者將一系列G-API表達式封裝到一個對象(圖)中,而后者是圖編譯的產物(詳見此章節)。
圖編譯器層
每個G-API計算在執行之前都會被編譯。編譯過程通過兩種方式觸發:
- 隱式編譯,當使用cv::GComputation::apply()時觸發。在這種情況下,圖編譯緊接著立即執行。
- 顯式編譯,當使用cv::GComputation::compile()時觸發。這種情況下,將返回一個cv::GCompiled對象,該對象可以像C++函數對象一樣被調用。
第一種方式適用于輸入數據格式事先未知的情況——例如,當數據來自任意輸入文件時。第二種方式推薦用于部署(生產)場景,在這些場景中,輸入數據特征通常是預定義的。
圖編譯過程建立在ADE框架之上。最初,根據API層捕獲的表達式生成一個二分圖。這個圖包含兩種類型的節點:數據和操作。圖總是以一個或多個數據節點開始和結束,其間是操作節點。每個操作節點都有輸入和輸出,它們都是數據節點。
在初始圖生成之后,它實際上會經過一系列稱為“passes”的圖變換處理。ADE框架充當編譯pass管理引擎,而passes是專門為G-API編寫的。
存在不同的passes來檢查圖的有效性、細化操作和數據的細節、基于親和性或用戶指定的區域化[TBD]將節點組織成集群(“Islands”),等等。后端也能夠在編譯過程中注入特定于后端的passes,更多關于這方面的信息請參見專門的章節。
圖編譯的結果是一個編譯后的對象,由類cv::GCompiled表示。無論是否有顯式或隱式的編譯請求(見上文),都會創建一個新的cv::GCompiled對象。實際的圖執行是在cv::GCompiled內發生的,并由參與圖編譯的后端決定。
另請參閱:
- cv::GComputation::apply()
- cv::GComputation::compile()
- cv::GCompiled
后端層
上述圖表列出了兩個后端:OpenCV和Fluid。OpenCV被稱為“參考后端”,它使用傳統的OpenCV函數實現G-API操作。這個后端對于在熟悉的開發系統上進行原型設計非常有用。Fluid是一個插件,用于在CPU上高效執行緩存——它實現了不同的執行策略,并使用其特有的內核。Fluid后端允許在CPU上運行時減少內存占用并提高內存局部性。
可能還有更多的后端可用,例如Halide、OpenCL等——G-API提供了一個統一的內部API來開發后端,因此任何愛好者或公司都可以自由地在新平臺或加速器上擴展G-API。在OpenCV基礎設施方面,每個新的后端都是一個新的獨立OpenCV模塊,當作為OpenCV的一部分構建時,它擴展了G-API。
圖執行
圖的執行方式由編譯時選擇的后端定義。實際上,每個后端在圖編譯過程的最終階段生成自己的執行腳本,即創建可執行(編譯)對象時。例如,在OpenCV后端中,這個腳本只是需要調用的OpenCV函數的拓撲排序序列;對于Fluid后端,情況類似——是每次迭代處理輸入行的代理(Agents)的拓撲排序列表。
圖執行通過兩種方式觸發:
- 通過cv::GComputation::apply(),為給定的輸入數據即時編譯圖;
- 通過cv::GCompiled::operator()(),當圖已被預編譯時使用。
這兩種方法都是多態的,接受變長參數列表,并在運行時執行有效性檢查。如果傳遞的數據對象的數量、形狀和格式與預期不符,則會拋出運行時異常。G-API還提供了類型包裝器,將這些檢查移到編譯時——參見cv::GComputationT<>。
G-API圖執行聲明為無狀態的——這意味著已編譯的函數對象(cv::GCompiled)就像一個純粹的C++函數一樣工作,并對相同的輸入參數集提供相同的結果。
這兩種執行方法都接受N+M個參數,其中N是輸入數量,M是在其上定義cv::GComputation的輸出數量。請注意,雖然在定義中使用了G-API類型(如cv::GMat等),但執行方法接受持有實際數據的傳統OpenCV數據類型(如cv::Mat)——參見參數編組中的表格。
另請參閱:
實現細節
內核API