GPU

總覽

MediaPipe 支援 GPU 運算和算繪的計算機節點,可結合多個 GPU 節點,以及與以 CPU 為基礎的計算工具節點混用。行動平台有多種 GPU API (例如 OpenGL ES、 Metal 和 Vulkan)。MediaPipe 未嘗試提供單一跨 API GPU 抽象化機制。個別節點可以使用不同的 API 編寫,以便視需要運用平台專屬功能。

GPU 支援是行動平台效能的重要關鍵,特別是即時影片。MediaPipe 可讓開發人員編寫支援 GPU 的與 GPU 相容計算機,可用於:

  • 裝置端即時處理,不只是批次處理
  • 影片算繪和效果,不只是分析

以下是 MediaPipe 中 GPU 支援的設計原則

  • 應該能運用 GPU 式計算機在圖表中的任何位置,不一定要用於在螢幕上轉譯。
  • 將影格資料從某 GPU 式計算機轉移至另一個,應該很快就能轉移,而且不會產生高成本的複製作業。
  • 在 CPU 和 GPU 之間傳輸影格資料的方式也應符合平台允許的效率。
  • 由於不同平台可能需要採用不同技術才能達到最佳效能,因此這個 API 應讓幕後實作方式有彈性。
  • 使用 GPU 進行所有或部分作業時,應允許以最大彈性的方式使用計算機,並在必要時將 GPU 與 CPU 合併使用。

OpenGL ES 支援

MediaPipe 支援 Android/Linux 至 3.2 以上版本的 OpenGL ES 和 ES 3.0 最高版本 。此外,MediaPipe 也支援 iOS 上的金屬。

需要 OpenGL ES 3.1 以上版本 (在 Android/Linux 系統上) 才能執行 機器學習推論計算機和圖形

MediaPipe 允許在多個 GL 環境中執行 OpenGL。舉例來說 這在結合較慢的 GPU 推論路徑 (例如 10 時) 的圖表中非常有用 每秒影格數 (FPS) 速度較快的 GPU 轉譯路徑 (例如每秒 30 個影格):自一次 GL 內容以來 對應一個連續命令佇列, 工作就會降低轉譯畫面更新率

MediaPipe 使用多種結構定義可解決這個問題的一個挑戰就是 仍可正常使用其中一個例子是內含影片 同時傳送給轉譯和推論路徑,而且轉譯時必須 存取推論的最新輸出內容

無法同時由多個執行緒存取 OpenGL 內容。 此外,在同一個執行緒上切換使用中的 GL 情境可能會速度過慢 部分 Android 裝置。因此,我們的做法 每個情境。每個執行緒都會發出 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 支援的選項:

  • 我們有 GPU 資料類型 GpuBuffer,用於表示圖片資料,已針對 GPU 用量進行最佳化。這個資料類型的確切內容是不透明且平台專屬。
  • 以組合為基礎的低階 API。任何要使用 GPU 的計算機都會建立並擁有 GlCalculatorHelper 類別的執行個體。這個類別提供跨平台通用的 API,可用於管理 OpenGL 環境、設定輸入和輸出的紋理等。
  • 以子類別為基礎的高階 API,其中簡單的計算機會從 GlSimpleCalculator 實作圖片篩選器子類別,而且只需要用其特定的 OpenGL 程式碼覆寫幾個虛擬方法,父類別則負責處理所有水管作業。
  • 所有 GPU 式計算機之間需要共用的資料,會以外部輸入的形式提供,並以圖表服務的形式實作,並由 GlCalculatorHelper 類別管理。
  • 只要結合計算機專用輔助程式和共用圖形服務,我們就能靈活管理 GPU 資源:我們可以為每個計算器有個別的內容、共用單一背景資訊、共用鎖定或其他同步處理基元等。這些功能都是由輔助程式管理,而且不會顯示在個別計算機中。

從 GpuBuffer 到 ImageFrame 轉換器

我們提供兩個名為 GpuBufferToImageFrameCalculatorImageFrameToGpuBufferCalculator 的計算機。這些計算機可在 ImageFrameGpuBuffer 之間轉換,讓您建構結合 GPU 和 CPU 計算的圖形。iOS 和 Android 都支援這些額外資訊

在可能的情況下,這些計算機會使用平台專屬功能,在 CPU 和 GPU 之間共用資料,而無需複製。

下圖顯示行動應用程式中的資料流程。這個應用程式會擷取相機中的影片、透過 MediaPipe 圖表執行資料,並在螢幕上即時算繪輸出內容。虛線表示 MediaPipe 圖表中的哪些部分正確。這個應用程式會使用 OpenCV 在 CPU 上執行 Canny 邊緣偵測篩選器,並使用 GPU 覆蓋在原始影片上方。

GPU 計算機的互動方式

攝影機的視訊畫面會以 GpuBuffer 封包的形式輸入至圖表中。 輸入串流是由兩個計算機同時存取。 GpuBufferToImageFrameCalculator 會將緩衝區轉換為 ImageFrame。 然後透過灰階轉換器和 Canny 篩選器傳送 並在 CPU 上執行) 之後,其輸出內容會轉換成 GpuBuffer。多輸入 GPU 計算機 GlOverlayCalculator 系統會同時輸入原始 GpuBuffer 和邊緣偵測工具以外的值 並使用著色器疊加這些元素接著,系統會將輸出內容傳回 自訂回呼功能,而應用程式則會 使用 OpenGL。