![]() |
OpenCV 4.12.0
開源計算機視覺
|
G-API 背後的核心思想是可移植性 – 使用 G-API 構建的 pipeline 必須是可移植的(或者至少能夠被移植)。這意味著,當為新平臺編譯時,它可以直接執行,或者 G-API 提供必要的工具來使其在那裡執行,而演算法本身幾乎不需要進行任何更改。
這個想法可以透過將核心介面與其實現分離來實現。一旦使用核心介面構建了 pipeline,它就變成了與實現無關的 – 實現細節(即使用哪個核心)在單獨的階段傳遞(圖編譯)。
核心實現層次結構可能如下所示
那麼 pipeline 本身只能用 A、B 等來表達,並且在執行中選擇使用哪個實現就變成了一個外部引數。
G-API 提供了一個宏來定義一個新的核心介面 – G_TYPED_KERNEL()
這個宏是定義新型別的快捷方式。它接受三個引數來註冊一個新型別,並且需要型別主體存在(參見下文)。宏引數是
std::function<>-like 簽名,它定義了核心的 API;核心宣告可以被視為函式宣告 – 在這兩種情況下,都必須按照定義的方式使用新實體。
核心簽名定義了核心的使用語法 – 在圖構建期間它需要哪些引數。實現還可以使用此簽名將其派生到後端特定的回撥簽名中(參見下一章)。
核心可以接受任何型別的值,並且 G-API 動態型別以特殊方式處理。所有其他型別對 G-API 都是不透明的,並且按原樣傳遞給核心的 outMeta() 或執行回撥中。
核心的返回值只能是 G-API 動態型別 – cv::GMat、cv::GScalar 或 cv::GArray<T>。如果一個操作有多個輸出,它應該被包裝到一個 std::tuple<> 中(它只能包含提到的 G-API 型別)。不支援任意輸出數的操作。
一旦定義了一個核心,它就可以在帶有特殊的、G-API 提供的 "::on()" 方法的 pipeline 中使用。此方法具有與核心中定義的相同的簽名,因此以下程式碼
是一個完全合法的構造。不過,這個例子有些冗長,所以通常核心宣告帶有一個 C++ 函式包裝器(“工廠方法”),它可以啟用可選引數、更緊湊的語法、Doxygen 註釋等。
所以現在它可以像這樣使用
在當前版本中,核心宣告主體(大括號內的所有內容)必須包含一個靜態函式 outMeta()。此函式建立操作的輸入和輸出元資料之間的函式依賴關係。
元資料是核心操作的資料的資訊。由於非 G-API 型別對 G-API 是不透明的,因此 G-API 僅關心 G* 資料描述符(即 cv::GMat 等的維度和格式)。
outMeta() 也是如何將核心簽名轉換為派生回撥的一個示例 – 請注意,在此示例中,outMeta() 簽名完全遵循核心簽名(在宏中定義),但不同 – 核心期望 cv::GMat,而 outMeta() 接受並返回 cv::GMatDesc(cv::GMat 的 G-API 結構元資料)。
outMeta() 的目的是在計算中傳播元資料資訊,從輸入到輸出,並推斷內部(中間的、臨時的)資料物件的元資料。此資訊是進一步 pipeline 最佳化、記憶體分配以及 G-API 框架在圖編譯期間執行的其他操作所必需的。
一旦聲明瞭一個核心,它的介面就可以用於在不同的後端實現此核心的版本。這個概念自然是從面向物件程式設計的“介面/實現”習語中投影出來的:一個介面可以被多次實現,並且核心的不同實現應該可以相互替換,而不會破壞演算法(pipeline)邏輯(里氏替換原則)。
每個後端都定義了自己實現核心介面的方式。儘管如此,這種方式是常規的 – 無論外掛是什麼,它的核心實現都必須從核心介面型別“派生”。
然後,核心實現被組織成核心包。核心包作為編譯引數傳遞給 cv::GComputation::compile(),並提供一些提示給 G-API,說明如何選擇合適的核心(有關這方面的更多資訊,請參見“異構性”[待定])。
例如,前面提到的 Filter2D 在“參考”CPU(OpenCV)外掛中以這種方式實現(注意 – 這是一個簡化的形式,帶有不正確的邊界處理)
請注意 CPU(OpenCV)外掛是如何轉換原始核心簽名的
GCPUFilter2D::run() 比原始核心簽名多接受一個引數。核心開發人員的基本直覺是不要關心 cv::Mat 物件來自哪裡,而不是原始的 cv::GMat – 只需遵循外掛定義的簽名約定即可。G-API 將在執行期間呼叫此方法並提供所有必要的資訊(並按原樣轉發原始不透明資料)。
有時,核心只是 API 級別上的一個單獨的東西。這對於使用者來說很方便,但在特定的實現方面,最好有多個核心(一個子圖)來完成這件事。一個例子是 goodFeaturesToTrack() – 雖然在 OpenCV 後端它可能仍然是一個單獨的核心,但在 Fluid 中它變成了複合的 – Fluid 可以處理 Harris 響應計算,但不能對 STL 向量進行稀疏非極大值抑制和點提取
可以使用通用宏 GAPI_COMPOUND_KERNEL() 定義複合核心實現
區分複合核心與 G-API 高階函式很重要,G-API 高階函式是一個看起來像核心的 C++ 函式,但實際上生成了一個子圖。核心區別在於,複合核心是一個實現細節,核心實現可以是複合的也可以不是(取決於後端能力),而高階函式是 G-API 術語中的“宏”,因此不能充當需要由後端實現的介面。