ภาพรวม
MediaPipe รองรับโหนดเครื่องคำนวณสำหรับการประมวลผลและการแสดงผล GPU และอนุญาตให้รวมโหนด GPU หลายโหนด รวมถึงการผสมโหนดเครื่องคำนวณแบบ CPU ได้ มี API ของ GPU อยู่หลายรายการบนแพลตฟอร์มอุปกรณ์เคลื่อนที่ (เช่น OpenGL ES, Metal และ Vulkan) MediaPipe ไม่พยายามเสนอการแอบสแตรก GPU แบบข้าม API เพียงรายการเดียว แต่ละโหนดสามารถเขียนโดยใช้ API ที่ต่างกัน ทำให้สามารถใช้ประโยชน์จากฟีเจอร์เฉพาะแพลตฟอร์มได้เมื่อจำเป็น
การรองรับ GPU เป็นสิ่งจำเป็นเพื่อประสิทธิภาพที่ดีในแพลตฟอร์มอุปกรณ์เคลื่อนที่ โดยเฉพาะสำหรับวิดีโอแบบเรียลไทม์ MediaPipe ช่วยให้นักพัฒนาซอฟต์แวร์เขียนเครื่องคำนวณที่เข้ากันได้กับ GPU ซึ่งรองรับการใช้ GPU สำหรับสิ่งต่อไปนี้
- การประมวลผลแบบเรียลไทม์ในอุปกรณ์ ไม่ใช่แค่การประมวลผลแบบกลุ่ม
- การแสดงผลและเอฟเฟกต์วิดีโอ ไม่ใช่แค่การวิเคราะห์
หลักการออกแบบสำหรับการรองรับ GPU ใน MediaPipe มีดังนี้
- เครื่องคำนวณแบบ GPU ควรจะปรากฏที่ใดก็ได้ในกราฟ และไม่จำเป็นต้องใช้เพื่อการแสดงผลบนหน้าจอ
- การโอนข้อมูลเฟรมจากเครื่องคำนวณที่ใช้ GPU ไปยังเครื่องคำนวณอีกเครื่องควรรวดเร็วและไม่ก่อให้เกิดการคัดลอกซึ่งมีราคาแพง
- การโอนข้อมูลเฟรมระหว่าง CPU และ GPU ควรมีประสิทธิภาพเท่าที่แพลตฟอร์มอนุญาต
- เนื่องจากแพลตฟอร์มที่แตกต่างกันอาจต้องใช้เทคนิคที่แตกต่างกันเพื่อประสิทธิภาพที่ดีที่สุด API จึงควรให้ความยืดหยุ่นในการดำเนินสิ่งต่างๆ ในเบื้องหลัง
- เครื่องคำนวณควรมีความยืดหยุ่นสูงสุดในการใช้ 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 หลายรายการได้ ตัวอย่างเช่น รายการนี้ อาจมีประโยชน์อย่างมากในกราฟที่รวมเส้นทางการอนุมาน GPU ที่ช้ากว่า (เช่น ที่ 10 FPS) ที่มีเส้นทางการแสดงผล GPU ที่เร็วขึ้น (เช่น ที่ 30 FPS): นับตั้งแต่บริบท GL หนึ่ง ตรงกับคิวคำสั่งตามลำดับหนึ่งรายการ โดยใช้บริบทเดียวกันสำหรับทั้ง จะทำให้อัตราเฟรมในการแสดงผลลดลง
ความท้าทายอย่างหนึ่งของ MediaPipe ในการแก้โจทย์ปัญหาหลายบริบทคือความสามารถในการ สื่อสารระหว่างกัน ตัวอย่างสถานการณ์คือหนึ่งที่มีวิดีโออินพุต ที่ส่งไปยังทั้งเส้นทางการแสดงผลและอนุมาน และการแสดงผลจะต้องมี เข้าถึงเอาต์พุตล่าสุดจากการอนุมานได้
ไม่สามารถเข้าถึงบริบท OpenGL ด้วยชุดข้อความหลายรายการพร้อมกัน นอกจากนี้ การเปลี่ยนบริบท GL ที่ใช้งานอยู่ในเทรดเดียวกันอาจทํางานได้ช้า อุปกรณ์ Android บางรุ่น ดังนั้นแนวทางของเราคือการสร้างชุดข้อความเฉพาะขึ้นมา 1 รายการ ต่อบริบท เทรดแต่ละรายการออกคำสั่ง GL เพื่อสร้างคิวคำสั่งแบบอนุกรม ในบริบท ซึ่งจะดำเนินการโดย GPU แบบไม่พร้อมกัน
อายุการใช้งานของเครื่องคำนวณ GPU
ส่วนนี้จะแสดงโครงสร้างพื้นฐานของวิธีการประมวลผลของ GPU
เครื่องคำนวณที่ได้มาจาก GlSimpleCalculator คลาสพื้นฐาน เครื่องคำนวณ GPU
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();
}
หลักการออกแบบที่กล่าวถึงข้างต้นได้ส่งผลให้เกิดการออกแบบดังต่อไปนี้ ตัวเลือกสำหรับการรองรับ GPU ของ MediaPipe
- เรามีประเภทข้อมูล GPU ที่เรียกว่า
GpuBuffer
สำหรับแสดงข้อมูลรูปภาพ ซึ่งเพิ่มประสิทธิภาพสำหรับการใช้งาน GPU เนื้อหาเฉพาะของประเภทข้อมูลนี้จะไม่ชัดเจนและเจาะจงแพลตฟอร์ม - API ระดับต่ำที่อิงตามองค์ประกอบ ซึ่งเครื่องคำนวณที่ต้องการใช้ GPU จะสร้างและเป็นเจ้าของอินสแตนซ์ของคลาส
GlCalculatorHelper
คลาสนี้มี API ที่เข้าใจได้โดยไม่จำเป็นต้องเข้าใจแพลตฟอร์มสำหรับการจัดการบริบท OpenGL, การตั้งค่าพื้นผิวสำหรับอินพุตและเอาต์พุต ฯลฯ - API ระดับสูงที่อิงกับคลาสย่อย ซึ่งเป็นเครื่องคำนวณแบบง่ายที่ใช้คลาสย่อยของตัวกรองรูปภาพจาก
GlSimpleCalculator
และต้องการลบล้างเมธอดเสมือน 2 รายการด้วยโค้ด OpenGL ที่เจาะจงเท่านั้น ในขณะที่ Superclass มีหน้าที่จัดการท่อประปาทั้งหมด - ข้อมูลที่จำเป็นต้องแชร์ระหว่างเครื่องคำนวณที่ใช้ GPU ทั้งหมดจะได้รับเป็นอินพุตภายนอกที่ใช้เป็นบริการกราฟและจัดการโดยคลาส
GlCalculatorHelper
- การใช้ตัวช่วยสำหรับเครื่องคำนวณโดยเฉพาะและบริการกราฟที่ใช้ร่วมกันช่วยให้เราสามารถจัดการทรัพยากร GPU ได้อย่างยืดหยุ่นมากขึ้น เราสามารถแยกบริบทต่อเครื่องคำนวณ แชร์บริบทเดียว แชร์ล็อกหรือการซิงค์พื้นฐานอื่นๆ ฯลฯ ทั้งหมดนี้ได้รับการจัดการโดยตัวช่วยและซ่อนไว้จากเครื่องคำนวณแต่ละเครื่อง
ตัวแปลง GpuBuffer เป็น ImageFrame
เรามีเครื่องคำนวณ 2 แบบที่เรียกว่า GpuBufferToImageFrameCalculator
และ ImageFrameToGpuBufferCalculator
เครื่องคำนวณเหล่านี้จะแปลงค่าระหว่าง ImageFrame
และ GpuBuffer
ทำให้สามารถสร้างกราฟที่รวมเครื่องคำนวณ GPU และ CPU เข้าด้วยกันได้ รองรับทั้ง iOS และ Android
เมื่อเป็นไปได้ เครื่องคำนวณเหล่านี้จะใช้ฟังก์ชันเฉพาะแพลตฟอร์มเพื่อแชร์ข้อมูลระหว่าง CPU และ GPU โดยไม่ต้องคัดลอก
แผนภาพด้านล่างแสดงโฟลว์ข้อมูลในแอปพลิเคชันบนอุปกรณ์เคลื่อนที่ที่บันทึกวิดีโอจากกล้อง เรียกใช้ผ่านกราฟ MediaPipe และแสดงผลผลลัพธ์บนหน้าจอแบบเรียลไทม์ เส้นประจะระบุว่าส่วนใดอยู่ในกราฟ MediaPipe ที่เหมาะสม แอปพลิเคชันนี้เรียกใช้ตัวกรองการตรวจหาขอบแบบ Canny บน CPU โดยใช้ OpenCV และวางซ้อนบนวิดีโอต้นฉบับโดยใช้ GPU
ระบบจะส่งเฟรมวิดีโอจากกล้องเข้าสู่กราฟเป็นแพ็กเก็ต GpuBuffer
เครื่องคิดเลข 2 เครื่องสามารถเข้าถึงสตรีมอินพุตพร้อมกันได้
GpuBufferToImageFrameCalculator
แปลงบัฟเฟอร์เป็น ImageFrame
ซึ่งจะส่งผ่านตัวแปลงโทนสีเทาและตัวกรองแบบไร้สาระ (ทั้ง
บน OpenCV และทำงานบน CPU) ซึ่งเอาต์พุตจะถูกแปลงเป็น
GpuBuffer
อีกครั้ง GlOverlayCalculator เครื่องคำนวณ GPU แบบมัลติอินพุต
ป้อนทั้ง GpuBuffer
ต้นฉบับและที่มาจากตัวตรวจจับขอบ
และวางซ้อนด้วย
ตัวปรับแสงเงา จากนั้นระบบจะส่งผลลัพธ์กลับไปยัง
โดยใช้เครื่องคำนวณ Callback และแอปพลิเคชันจะแสดงรูปภาพ
กับหน้าจอโดยใช้ OpenGL