Vështrim i përgjithshëm
MediaPipe mbështet nyjet llogaritëse për llogaritjen dhe paraqitjen e GPU, dhe lejon kombinimin e shumë nyjeve GPU, si dhe përzierjen e tyre me nyjet llogaritëse të bazuara në CPU. Ekzistojnë disa API GPU në platformat celulare (p.sh. OpenGL ES, Metal dhe Vulkan). MediaPipe nuk përpiqet të ofrojë një abstraksion të vetëm GPU ndër-API. Nyjet individuale mund të shkruhen duke përdorur API të ndryshme, duke i lejuar ata të përfitojnë nga veçoritë specifike të platformës kur është e nevojshme.
Mbështetja e GPU-së është thelbësore për performancën e mirë në platformat celulare, veçanërisht për videot në kohë reale. MediaPipe u mundëson zhvilluesve të shkruajnë kalkulatorë të pajtueshëm me GPU që mbështesin përdorimin e GPU për:
- Përpunim në kohë reale në pajisje, jo vetëm përpunim grupor
- Paraqitja dhe efektet e videos, jo vetëm analiza
Më poshtë janë parimet e projektimit për mbështetjen e GPU në MediaPipe
- Llogaritësit e bazuar në GPU duhet të jenë në gjendje të ndodhin kudo në grafik dhe jo domosdoshmërisht të përdoren për paraqitjen në ekran.
- Transferimi i të dhënave të kornizës nga një kalkulator i bazuar në GPU në një tjetër duhet të jetë i shpejtë dhe të mos shkaktojë operacione të shtrenjta kopjimi.
- Transferimi i të dhënave të kornizës midis CPU dhe GPU duhet të jetë aq efikas sa lejon platforma.
- Për shkak se platforma të ndryshme mund të kërkojnë teknika të ndryshme për performancën më të mirë, API duhet të lejojë fleksibilitet në mënyrën se si gjërat zbatohen prapa skenave.
- Një kalkulator duhet t'i lejohet fleksibilitet maksimal në përdorimin e GPU-së për të gjithë ose një pjesë të funksionimit të tij, duke e kombinuar atë me CPU-në nëse është e nevojshme.
Mbështetje OpenGL ES
MediaPipe mbështet OpenGL ES deri në versionin 3.2 në Android/Linux dhe deri në ES 3.0 në iOS. Përveç kësaj, MediaPipe gjithashtu mbështet Metal në iOS.
Kërkohet OpenGL ES 3.1 ose më i ri (në sistemet Android/Linux) për përdorimin e llogaritësve dhe grafikëve të konkluzioneve të mësimit të makinerisë.
MediaPipe lejon grafikët të ekzekutojnë OpenGL në kontekste të shumta GL. Për shembull, kjo mund të jetë shumë e dobishme në grafikët që kombinojnë një shteg konkluzionesh më të ngadaltë të GPU-së (p.sh. në 10 FPS) me një shteg më të shpejtë të paraqitjes së GPU-së (p.sh., në 30 FPS): meqenëse një kontekst GL korrespondon me një radhë komandash sekuenciale, përdorimi i të njëjtit kontekst për të dyja detyrat do të reduktonte shpejtësinë e kuadrit të interpretimit.
Një sfidë që zgjidh përdorimi i konteksteve të shumta nga MediaPipe është aftësia për të komunikuar mes tyre. Një skenar shembull është ai me një video hyrëse që dërgohet në të dy shtigjet e interpretimit dhe të konkluzioneve, dhe interpretimi duhet të ketë akses në daljen më të fundit nga përfundimi.
Një kontekst OpenGL nuk mund të aksesohet nga shumë thread në të njëjtën kohë. Për më tepër, ndërrimi i kontekstit aktiv GL në të njëjtën lidhje mund të jetë i ngadaltë në disa pajisje Android. Prandaj, qasja jonë është që të kemi një fije të dedikuar për kontekst. Çdo thread lëshon komanda GL, duke krijuar një radhë komandash serike në kontekstin e saj, e cila më pas ekzekutohet nga GPU në mënyrë asinkrone.
Jeta e një kalkulatori GPU
Ky seksion paraqet strukturën bazë të metodës Process të një kalkulatori GPU që rrjedh nga klasa bazë GlSimpleCalculator. Llogaritësi GPU LuminanceCalculator tregohet si shembull. Metoda LuminanceCalculator::GlRender thirret nga 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();
}
Parimet e projektimit të përmendura më sipër kanë rezultuar në zgjedhjet e mëposhtme të projektimit për mbështetjen e MediaPipe GPU:
- Ne kemi një lloj të dhënash GPU, të quajtur
GpuBuffer, për përfaqësimin e të dhënave të imazhit, të optimizuara për përdorim GPU. Përmbajtja e saktë e këtij lloji të të dhënave është e errët dhe specifike për platformën. - Një API e nivelit të ulët bazuar në përbërje, ku çdo kalkulator që dëshiron të përdorë GPU-në krijon dhe zotëron një shembull të klasës
GlCalculatorHelper. Kjo klasë ofron një API agnostike të platformës për menaxhimin e kontekstit OpenGL, vendosjen e teksturave për hyrjet dhe daljet, etj. - Një API e nivelit të lartë bazuar në nënklasifikimin, ku kalkulatorë të thjeshtë që zbatojnë filtra të imazhit nënklasaohen nga
GlSimpleCalculatordhe duhet vetëm të anashkalojnë disa metoda virtuale me kodin e tyre specifik OpenGL, ndërsa superklasa kujdeset për të gjitha pajisjet hidraulike. - Të dhënat që duhet të ndahen midis të gjithë kalkulatorëve të bazuar në GPU ofrohen si një hyrje e jashtme që zbatohet si një shërbim grafik dhe menaxhohet nga klasa
GlCalculatorHelper. - Kombinimi i ndihmësve specifikë të kalkulatorit dhe një shërbimi grafik të përbashkët na lejon fleksibilitet të madh në menaxhimin e burimit GPU: ne mund të kemi një kontekst të veçantë për kalkulator, të ndajmë një kontekst të vetëm, të ndajmë një bllokim ose primitivë të tjerë sinkronizimi, etj. -- dhe e gjithë kjo menaxhohet nga ndihmësi dhe fshihet nga llogaritësit individualë.
Konvertuesit GpuBuffer në ImageFrame
Ne ofrojmë dy kalkulatorë të quajtur GpuBufferToImageFrameCalculator dhe ImageFrameToGpuBufferCalculator . Këta kalkulatorë konvertohen midis ImageFrame dhe GpuBuffer , duke lejuar ndërtimin e grafikëve që kombinojnë kalkulatorët GPU dhe CPU. Ato mbështeten si në iOS ashtu edhe në Android
Kur është e mundur, këta kalkulatorë përdorin funksione specifike të platformës për të ndarë të dhënat midis CPU-së dhe GPU-së pa kopjuar.
Diagrami i mëposhtëm tregon rrjedhën e të dhënave në një aplikacion celular që kap video nga kamera, e drejton atë përmes një grafiku MediaPipe dhe e jep daljen në ekran në kohë reale. Vija e ndërprerë tregon se cilat pjesë janë brenda grafikut të duhur MediaPipe. Ky aplikacion ekzekuton një filtër të zbulimit të skajeve Canny në CPU duke përdorur OpenCV dhe e mbivendos atë në krye të videos origjinale duke përdorur GPU.

Kornizat video nga kamera futen në grafik si pako GpuBuffer . Rrjedha e hyrjes aksesohet nga dy kalkulatorë paralelisht. GpuBufferToImageFrameCalculator e konverton buferin në një ImageFrame , i cili më pas dërgohet përmes një konverteri në shkallë gri dhe një filtri të thjeshtë (të dyja të bazuara në OpenCV dhe që funksionojnë në CPU), prodhimi i të cilit më pas konvertohet përsëri në një GpuBuffer . Një kalkulator GPU me shumë hyrje, GlOverlayCalculator, merr si hyrje si GpuBuffer in origjinal ashtu edhe atë që del nga detektori i skajeve dhe i mbivendos ato duke përdorur një shader. Dalja më pas dërgohet përsëri në aplikacion duke përdorur një kalkulator të kthimit të thirrjes dhe aplikacioni e jep imazhin në ekran duke përdorur OpenGL.