نظرة عامة
يتوافق MediaPipe مع العُقد الحاسبة للحوسبة والعرض من خلال وحدة معالجة الرسومات، كما يسمح بدمج عُقد متعددة لوحدة معالجة الرسومات، بالإضافة إلى مزجها مع عُقد الآلة الحاسبة المستندة إلى وحدة المعالجة المركزية (CPU). هناك العديد من واجهات برمجة التطبيقات لوحدة معالجة الرسومات على الأنظمة الأساسية للأجهزة الجوّالة (مثل OpenGL ES وMetal وVulkan). لا تحاول MediaPipe تقديم تجريد واحد لوحدة معالجة الرسومات التي تعمل عبر واجهات برمجة التطبيقات. يمكن كتابة العُقد الفردية باستخدام واجهات برمجة تطبيقات مختلفة، مما يسمح لها بالاستفادة من الميزات الخاصة بالنظام الأساسي عند الحاجة.
يُعدّ التوافق مع وحدة معالجة الرسومات أمرًا ضروريًا للحصول على أداء جيد على الأنظمة الأساسية للأجهزة الجوّالة، لا سيّما للفيديوهات في الوقت الفعلي. يمكّن MediaPipe المطوّرين من كتابة آلات حاسبة متوافقة مع وحدة معالجة الرسومات تتوافق مع استخدام وحدة معالجة الرسومات في الحالات التالية:
- المعالجة في الوقت الفعلي على الجهاز فقط، وليس فقط المعالجة على دفعات
- عرض الفيديو والتأثيراته، وليس التحليل فقط
فيما يلي مبادئ التصميم لدعم وحدة معالجة الرسومات في MediaPipe
- من المفترض أن تظهر الحاسبات المستندة إلى وحدة معالجة الرسومات في أي مكان في الرسم البياني، وألا يتم استخدامها بالضرورة للعرض على الشاشة.
- من المفترض أن يتم نقل بيانات الإطار من حاسبة تعتمد على وحدة معالجة الرسومات إلى أخرى بسرعة، وألا يكلف ذلك عمليات النسخ.
- يجب أن يكون نقل بيانات الإطار بين وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات بالكفاءة التي تسمح بها المنصة.
- نظرًا لأن الأنظمة الأساسية المختلفة قد تتطلب أساليب مختلفة لتحقيق أفضل أداء، يجب أن توفر واجهة برمجة التطبيقات المرونة في طريقة تنفيذ الأشياء خلف الكواليس.
- يجب توفير أكبر قدر من المرونة للآلة الحاسبة في استخدام وحدة معالجة الرسومات في كل عملها أو جزء منه، ودمجها مع وحدة المعالجة المركزية (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 متعددة. على سبيل المثال، مفيدة جدًا في الرسوم البيانية التي تجمع مسار استنتاج أبطأ لوحدة معالجة الرسومات (على سبيل المثال، عند لقطة في الثانية) بمسار عرض أسرع لوحدة معالجة الرسومات (مثلاً، 30 لقطة في الثانية): نظرًا إلى سياق GL واحد مع قائمة انتظار أوامر تسلسلية واحدة، باستخدام نفس السياق لكليهما من المهام إلى تقليل عدد اللقطات في الثانية للعرض.
أحد التحديات التي يحلها استخدام MediaPipe للسياقات المتعددة هو القدرة على وتتواصل عبرها. أحد الأمثلة على السيناريو هو فيديو يحتوي على إدخال فيديو إلى كل من مسار العرض والاستنتاج، وينبغي أن يكون للعرض الوصول إلى أحدث المخرجات من الاستنتاج.
لا يمكن الوصول إلى سياق OpenGL بواسطة سلاسل محادثات متعددة في نفس الوقت. وعلاوة على ذلك، يمكن أن يكون تبديل سياق GL النشط على سلسلة التعليمات نفسها بطيئًا على بعض أجهزة Android. وبالتالي، يكمن نهجنا في أن يكون لدينا سلسلة محادثات واحدة مخصَّصة لكل سياق. تصدر كل سلسلة محادثات أوامر GL، مما يؤدي إلى إنشاء قائمة انتظار أوامر تسلسلية في سياقه، والذي يتم تنفيذه بعد ذلك بواسطة وحدة معالجة الرسومات بشكل غير متزامن.
عمر الآلة الحاسبة لوحدة معالجة الرسومات
يوضح هذا القسم البنية الأساسية لطريقة معالجة وحدة معالجة الرسومات.
الآلة الحاسبة المستمدة من الفئة الأساسية GlSimpleCalculator. الآلة الحاسبة لوحدة معالجة الرسومات
يتم عرض 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
، ما يسمح بإنشاء الرسوم البيانية التي تجمع بين الآلة الحاسبة لوحدة المعالجة المركزية (GPU) ووحدة المعالجة المركزية (CPU). وهي متوافقة مع كل من نظامَي التشغيل iOS وAndroid.
تستخدم هذه الحاسبات، متى أمكن، وظائف خاصة بالنظام الأساسي لمشاركة البيانات بين وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات دون نسخها.
يوضح الرسم التخطيطي أدناه تدفق البيانات في تطبيق جوال يلتقط الفيديو من الكاميرا، ويشغله من خلال رسم MediaPipe، ويعرض الإخراج على الشاشة في الوقت الفعلي. يشير الخط المتقطع المتقطع إلى الأجزاء الموجودة داخل الرسم البياني MediaPipe المناسب. يشغّل هذا التطبيق فلتر Canny Edge على وحدة المعالجة المركزية باستخدام OpenCV، ويركّبه فوق الفيديو الأصلي باستخدام وحدة معالجة الرسومات.
تتم إضافة إطارات الفيديو من الكاميرا إلى الرسم البياني على شكل حزمة GpuBuffer
. تشير رسالة الأشكال البيانية
يتم الوصول إلى تدفق الإدخال بواسطة حاسبتين بالتوازي.
تحوّل GpuBufferToImageFrameCalculator
المخزن المؤقت إلى ImageFrame
،
والذي يتم إرساله بعد ذلك من خلال محوّل تدرُّج الرمادي وفلتر تدرج الأخطاء (كلاهما استنادًا إلى
على OpenCV وتشغيله على وحدة المعالجة المركزية)، التي يتم تحويل مخرجاتها إلى
GpuBuffer
مرة أخرى. تستخدم الآلة الحاسبة لوحدة معالجة الرسومات المتعددة الإدخالات، GlOverlayCalculator، أداة
إدخال كل من GpuBuffer
الأصلي والرقم الصادر من أداة رصد الحواف،
وتركيبها باستخدام أداة تظليل. ثم يتم إرسال المخرجات مرة أخرى إلى
تطبيق باستخدام الآلة الحاسبة لمعاودة الاتصال، وسيعرض التطبيق الصورة
على الشاشة باستخدام OpenGL.