وحدة معالجة الرسومات

نظرة عامة

يتوافق MediaPipe مع عقد الآلة الحاسبة للحوسبة والعرض عبر وحدة معالجة الرسومات، كما يسمح بدمج عدة عُقد لوحدة معالجة الرسومات، بالإضافة إلى خلطها مع عُقد الآلة الحاسبة المستندة إلى وحدة المعالجة المركزية (CPU). تتوفر العديد من واجهات برمجة التطبيقات لوحدة معالجة الرسومات على الأنظمة الأساسية للأجهزة الجوّالة (مثل OpenGL ES وMetal وVulkan). لا تحاول MediaPipe تقديم تجريد وحدة معالجة رسومات واحدة عبر واجهات برمجة التطبيقات. يمكن كتابة العُقد الفردية باستخدام واجهات برمجة تطبيقات مختلفة، ما يسمح لها بالاستفادة من الميزات الخاصة بالنظام الأساسي عند الحاجة.

يُعدّ دعم وحدة معالجة الرسومات ضروريًا لتحقيق أداء جيد على الأنظمة الأساسية للأجهزة الجوّالة، خاصةً للفيديوهات في الوقت الفعلي. تتيح MediaPipe للمطوّرين كتابة آلات حاسبة متوافقة مع وحدة معالجة الرسومات تتيح استخدام وحدة معالجة الرسومات في:

  • المعالجة في الوقت الفعلي على الجهاز فقط، وليس فقط المعالجة على دفعات
  • عرض الفيديو وتأثيراته، وليس فقط التحليل

في ما يلي مبادئ التصميم لدعم وحدة معالجة الرسومات في MediaPipe

  • يجب أن تحدث الحاسبات المستندة إلى وحدة معالجة الرسومات في أي مكان في الرسم البياني، وليس بالضرورة أن تُستخدم للعرض على الشاشة.
  • يجب أن يكون نقل بيانات الإطار من حاسبة مستندة إلى وحدة معالجة الرسومات إلى آلة حاسبة أخرى سريعًا، وألا يتكبد عمليات نسخ مكلفة.
  • يجب أن يكون نقل بيانات الإطار بين وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات (GPU) بالقدر الذي تسمح به المنصة.
  • نظرًا لأن الأنظمة الأساسية المختلفة قد تتطلب أساليب مختلفة لتحقيق أفضل أداء، يجب أن تتيح واجهة برمجة التطبيقات المرونة في طريقة تنفيذ الأمور من وراء الكواليس.
  • من المفترض أن تُتيح الآلة الحاسبة أقصى مرونة في استخدام وحدة معالجة الرسومات في جميع عمليات التشغيل أو جزء منها، بحيث يتم دمجها مع وحدة المعالجة المركزية (CPU) إذا لزم الأمر.

دعم OpenGL ES

يدعم MediaPipe استخدام OpenGL ES حتى الإصدار 3.2 على نظامي التشغيل Android/Linux وما يصل إلى ES 3.0 على iOS. بالإضافة إلى ذلك، يدعم MediaPipe أيضًا استخدام Metal على نظام التشغيل iOS.

يجب توفُّر برنامج OpenGL ES 3.1 أو إصدار أحدث (على أنظمة التشغيل Android/Linux) لتشغيل الآلات الحاسبة والرسومات البيانية لاستنتاجات تعلّم الآلة.

تسمح MediaPipe للرسومات البيانية بتشغيل OpenGL في سياقات GL متعددة. على سبيل المثال، قد يكون هذا مفيدًا جدًا في الرسومات البيانية التي تجمع بين مسار استنتاج وحدة معالجة الرسومات الأبطأ (مثلاً، بمعدّل 10 لقطات في الثانية) ومسار عرض أسرع لوحدة معالجة الرسومات (على سبيل المثال، بمعدّل 30 لقطة في الثانية): نظرًا إلى أنّ سياق GL واحد يتجاوب مع قائمة انتظار أوامر تسلسلية واحدة، سيؤدي استخدام السياق نفسه لكلتا المهمتين إلى تقليل عدد اللقطات في الثانية للعرض.

يتمثل أحد التحديات التي تستخدمها MediaPipe في استخدامها للسياقات المتعددة في القدرة على التواصل بينها. أحد الأمثلة على السيناريو هو فيديو يحتوي على إدخال فيديو يتم إرساله إلى كل من مسارَي العرض والاستنتاج، ويحتاج العرض إلى الوصول إلى أحدث مخرجات من الاستنتاج.

لا يمكن الوصول إلى سياق OpenGL من خلال سلاسل محادثات متعددة في الوقت نفسه. بالإضافة إلى ذلك، قد يكون تبديل سياق GL النشط في سلسلة التعليمات نفسها بطيئًا على بعض أجهزة Android. لذلك، فإن نهجنا هو أن يكون لدينا سلسلة محادثات مخصصة واحدة لكل سياق. تصدر كل سلسلة محادثات أوامر GL، مما ينشئ قائمة انتظار أوامر تسلسلية على سياقها، والتي يتم تنفيذها بعد ذلك بواسطة وحدة معالجة الرسومات بشكل غير متزامن.

مدة عمل الآلة الحاسبة لوحدة معالجة الرسومات

يوضح هذا القسم البنية الأساسية لطريقة المعالجة للآلة الحاسبة لوحدة معالجة الرسومات المستمدة من الفئة الأساسية GlSimpleحاسبة. تعرض الآلة الحاسبة لوحدة معالجة الرسومات LuminanceCalculator كمثال. يتم استدعاء الطريقة LuminanceCalculator::GlRender من GlSimpleCalculator::Process.

// 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:

  • لدينا نوع من بيانات وحدة معالجة الرسومات، يُسمى GpuBuffer، لتمثيل بيانات الصور، وهي محسَّنة لاستخدام وحدة معالجة الرسومات. إن المحتوى الدقيق لنوع البيانات هذا غير شفاف ويتوافق مع النظام الأساسي.
  • واجهة برمجة تطبيقات منخفضة المستوى استنادًا إلى تركيبة التطبيق، حيث تنشئ أي آلة حاسبة تريد الاستفادة من وحدة معالجة الرسومات مثيلاً للفئة GlCalculatorHelper وتملكه. توفر هذه الفئة واجهة برمجة تطبيقات غير متوافقة مع النظام الأساسي لإدارة سياق OpenGL، وإعداد زخارف المدخلات والمخرجات، وما إلى ذلك.
  • واجهة برمجة تطبيقات عالية المستوى تستند إلى التصنيف الفرعي، حيث تنفِّذ الآلات الحاسبة البسيطة الفئة الفرعية لفلاتر الصور من GlSimpleCalculator ولا تحتاج سوى إلى إلغاء طريقتَين افتراضيتَين باستخدام رمز OpenGL المحدّد، بينما تتولّى الفئة الرئيسية مسؤولية السباكة.
  • يتم توفير البيانات التي يجب مشاركتها بين كل الحاسبات المستندة إلى وحدة معالجة الرسومات كإدخال خارجي يتم تنفيذه كخدمة رسم بياني وتديره الفئة GlCalculatorHelper.
  • يتيح لنا الجمع بين أدوات المساعدة الخاصة بالآلة الحاسبة وخدمة الرسم البياني المشتركة مرونة كبيرة في إدارة مورد وحدة معالجة الرسومات: يمكننا الحصول على سياق منفصل لكل آلة حاسبة ومشاركة سياق واحد ومشاركة رمز القفل أو الإعدادات الأساسية الأخرى للمزامنة وما إلى ذلك -- وتتم إدارة كل ذلك من خلال المساعد وإخفاؤه من الآلات الحاسبة الفردية.

محوّلات GpuBuffer إلى ImageFrame

تتوفّر آلتان حاسبتان باسم GpuBufferToImageFrameCalculator وImageFrameToGpuBufferCalculator. تتراوح هذه الآلات الحاسبة بين ImageFrame وGpuBuffer، ما يتيح إنشاء رسومات بيانية تجمع بين الآلات الحاسبة لوحدة معالجة الرسومات ووحدة المعالجة المركزية (CPU). وهذه الإعلانات متوافقة مع أجهزة iOS وAndroid.

تستخدم هذه الحاسبات، عند الإمكان، وظائف خاصة بالنظام الأساسي لمشاركة البيانات بين وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات بدون نسخ.

يوضح المخطّط أدناه تدفق البيانات في تطبيق للأجهزة الجوّالة يلتقط فيديو من الكاميرا ويشغّله من خلال رسم MediaPipe البياني، ويعرض الإخراج على الشاشة في الوقت الفعلي. يشير الخط المتقطع إلى الأجزاء المناسبة داخل الرسم البياني MediaPipe. يشغّل هذا التطبيق فلتر اكتشاف الحواف في Canny على وحدة المعالجة المركزية (CPU) باستخدام OpenCV، ويتراكبه فوق الفيديو الأصلي باستخدام وحدة معالجة الرسومات.

كيفية تفاعل الآلات الحاسبة لوحدة معالجة الرسومات

يتم تضمين إطارات الفيديو من الكاميرا في الرسم البياني على شكل حِزم GpuBuffer. ويتم الوصول إلى تدفق الإدخال باستخدام حاسبتين بالتوازي. يحوّل GpuBufferToImageFrameCalculator المخزن المؤقت إلى ImageFrame، ويتم إرساله بعد ذلك من خلال محوّل تدرّج الرمادي وفلتر Canny (يعتمد كلاهما على OpenCV ويعمل على وحدة المعالجة المركزية)، ثم يتم تحويل مخرجاته إلى GpuBuffer مرة أخرى. آلة حاسبة لوحدة معالجة الرسومات متعددة الإدخالات، GlOverlayMachine، التي تستخدم كل من GpuBuffer الأصلية وتلك التي تخرج من أداة رصد الحواف، وتتراكبها باستخدام أداة تظليل. ثم يتم إرسال الناتج إلى التطبيق باستخدام حاسبة رد الاتصال، وسيعرض التطبيق الصورة على الشاشة باستخدام OpenGL.