總覽
MediaPipe 支援用於 GPU 運算和轉譯的計算機節點,並可將多個 GPU 節點結合使用,也可混合使用以 CPU 為基礎的計算機節點。行動平台提供多種 GPU API (例如 OpenGL ES、 Metal 和 Vulkan)。MediaPipe 並未嘗試提供單一跨 API GPU 抽象層。您可使用不同 API 編寫個別節點,以便視需要利用平台專屬功能。
GPU 支援能在行動裝置平台上提供良好效能,尤其對於即時影片來說更是如此。MediaPipe 可讓開發人員編寫與 GPU 相容的計算機,以便支援 GPU 的用途:
- 裝置端即時處理作業,並非僅限於批次處理
- 影片算繪和效果,而不只是分析
以下是 MediaPipe 中 GPU 支援的設計原則
- GPU 型計算機應適用於圖表中的任何位置,不一定用於螢幕轉譯。
- 將影格資料從一個 GPU 型計算器轉移至另一個 GPU 運算的速度應該很快,且不會產生高昂的複製作業。
- 在 CPU 和 GPU 之間轉移影格資料應盡可能達到平台允許的效率。
- 由於不同平台可能需要採用不同的技術才能獲得最佳成效,因此這個 API 應能在背景實作各項作業的彈性。
- 計算機應能提供最大的彈性,以使用 GPU 來處理全部或部分作業;如有需要,請搭配 CPU。
支援 OpenGL ES
MediaPipe 針對 Android/Linux 支援 OpenGL ES 3.2 以上版本;在 iOS 上則支援 ES 3.0 以上的版本。此外,MediaPipe 也支援 iOS 的金屬。
必須使用 OpenGL ES 3.1 以上版本 (在 Android/Linux 系統上),才能執行機器學習推論計算器和圖形。
MediaPipe 可讓圖形在多個 GL 情境中執行 OpenGL。舉例來說,將較慢的 GPU 推論路徑 (例如 10 FPS) 和更快速的 GPU 轉譯路徑 (例如每秒 30 個影格) 結合的圖表,就非常實用,因為兩個 GL 結構定義對應到一個序列指令佇列,而這兩項工作使用相同背景資訊會降低轉譯影格速率。
MediaPipe 使用多種結構定義解決的難題,就是必須相互通訊。例如:具有傳送至轉譯和推論路徑的輸入影片,且轉譯需要存取推論中最新的輸出內容。
多個執行緒無法同時存取 OpenGL 情境,此外,在部分 Android 裝置上,在同一個執行緒上切換使用中的 GL 情境,速度可能相當緩慢。因此,我們的做法是為每個情境設定一個專屬執行緒。每個執行緒都會發出 GL 指令,在結構定義上建構序列指令佇列,然後 GPU 會以非同步方式執行這些指令。
GPU 計算機的壽命
本節說明 GPU 計算機 (源自基本類別 GlSimpleCalculator) 的處理方法基本結構。GPU 計算機 LuminanceCalculator
如範例所示。系統會從 GlSimpleCalculator::Process
呼叫 LuminanceCalculator::GlRender
方法。
// Converts RGB images into luminance images, still stored in RGB format.
// See GlSimpleCalculator for inputs, outputs and input side packets.
class LuminanceCalculator : public GlSimpleCalculator {
public:
absl::Status GlSetup() override;
absl::Status GlRender(const GlTexture& src,
const GlTexture& dst) override;
absl::Status GlTeardown() override;
private:
GLuint program_ = 0;
GLint frame_;
};
REGISTER_CALCULATOR(LuminanceCalculator);
absl::Status LuminanceCalculator::GlRender(const GlTexture& src,
const GlTexture& dst) {
static const GLfloat square_vertices[] = {
-1.0f, -1.0f, // bottom left
1.0f, -1.0f, // bottom right
-1.0f, 1.0f, // top left
1.0f, 1.0f, // top right
};
static const GLfloat texture_vertices[] = {
0.0f, 0.0f, // bottom left
1.0f, 0.0f, // bottom right
0.0f, 1.0f, // top left
1.0f, 1.0f, // top right
};
// program
glUseProgram(program_);
glUniform1i(frame_, 1);
// vertex storage
GLuint vbo[2];
glGenBuffers(2, vbo);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// vbo 0
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), square_vertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, nullptr);
// vbo 1
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), texture_vertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
glVertexAttribPointer(ATTRIB_TEXTURE_POSITION, 2, GL_FLOAT, 0, 0, nullptr);
// draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// cleanup
glDisableVertexAttribArray(ATTRIB_VERTEX);
glDisableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(2, vbo);
return absl::OkStatus();
}
上述設計原則促成了下列 MediaPipe GPU 支援功能的設計選擇:
- 我們有一個名為
GpuBuffer
的 GPU 資料類型,用來表示圖片資料,並針對 GPU 用量進行最佳化。此資料類型的確切內容為不透明,且因平台而異。 - 以組合為基礎的低階 API,任何想使用 GPU 的計算機都會建立及擁有
GlCalculatorHelper
類別的執行個體。這個類別提供各平台通用的 API,可用於管理 OpenGL 環境、設定輸入和輸出的紋理等。 - 以子類別為基礎的高階 API,其中簡易計算機可從
GlSimpleCalculator
實作圖片篩選器子類別,而且只需用其特定的 OpenGL 程式碼覆寫一些虛擬方法,而父類別會處理所有水電問題。 - 必須在所有 GPU 式計算機之間共用的資料會以外部輸入形式提供,以圖形服務的形式實作,由
GlCalculatorHelper
類別管理。 - 結合計算機專用輔助程式和共用的圖形服務,讓我們可以更靈活地管理 GPU 資源:我們可以為每個計算機建立獨立的背景資料、共用單一背景資訊,並共用鎖定或其他同步處理基元等等;這些全都由輔助程式管理,而且隱藏在個別計算機中。
從 GpuBuffer 到 ImageFrame 轉換器
我們提供 GpuBufferToImageFrameCalculator
和 ImageFrameToGpuBufferCalculator
這兩個計算機。這些計算機會在 ImageFrame
和 GpuBuffer
之間轉換,協助建構結合 GPU 和 CPU 計算工具的圖表。iOS 和 Android 皆支援這些格式
這些計算機會盡可能使用平台專屬功能,在 CPU 和 GPU 之間共用資料,而不複製資料。
下圖顯示行動應用程式中的資料流程,可從相機擷取影片,並透過 MediaPipe 圖表執行,並且在螢幕上即時算繪輸出內容。虛線代表 MediaPipe 圖表內部的哪些部分正確。這個應用程式使用 OpenCV 在 CPU 上執行 Canny 邊緣偵測濾鏡,並使用 GPU 將篩選器重疊在原始影片上方。
相機的影片畫面會以 GpuBuffer
封包的形式輸入圖表。輸入串流則由兩個計算器並行存取。GpuBufferToImageFrameCalculator
會將緩衝區轉換為 ImageFrame
,接著透過灰階轉換工具與罐頭篩選器 (兩者都以 OpenCV 和在 CPU 上執行) 傳送,接著再將緩衝區再次轉換為 GpuBuffer
。多輸入 GPU 計算機 GlOverlayCalculator 會取用原始 GpuBuffer
和來自邊緣偵測工具的輸入內容,然後使用著色器重疊這些輸入值。接著使用回呼計算機將輸出內容傳回應用程式,應用程式則使用 OpenGL 將圖片轉譯至螢幕上。