Genel Bakış
MediaPipe, GPU bilgi işlem ve oluşturma işlemleri için hesap makinesi düğümlerini destekler. Ayrıca, birden çok GPU düğümünün birleştirilmesinin yanı sıra, CPU tabanlı hesap makinesi düğümleriyle karıştırılmasına da olanak tanır. Mobil platformlarda çeşitli GPU API'leri (ör. OpenGL ES, Metal ve Vulkan) vardır. MediaPipe, tek bir API'ler arası GPU soyutlama sunmaya çalışmaz. Bağımsız düğümler farklı API'ler kullanılarak yazılabilir. Böylece, gerektiğinde platforma özgü özelliklerden yararlanabilirler.
GPU desteği, mobil platformlarda iyi performansın (özellikle gerçek zamanlı videoların) sağlanması için gereklidir. MediaPipe, geliştiricilerin aşağıdaki işlemlerde GPU kullanımını destekleyen GPU ile uyumlu hesaplayıcılar yazmasını sağlar:
- Yalnızca toplu işlem değil, cihaz üzerinde gerçek zamanlı işleme
- Yalnızca analiz değil, video oluşturma ve efektler
Aşağıda, MediaPipe'te GPU desteği için tasarım ilkeleri verilmiştir
- GPU tabanlı hesap makineleri, grafiğin herhangi bir yerinde bulunabilmelidir ve ekranda oluşturma için kullanılmaları gerekmez.
- Kare verilerinin GPU tabanlı bir hesap makinesinden diğerine aktarılması hızlı olmalı ve pahalı kopyalama işlemlerine neden olmamalıdır.
- CPU ve GPU arasında kare verisi aktarımı, platformun izin verdiği kadar verimli olmalıdır.
- Farklı platformlar en iyi performans için farklı teknikler gerektirebilir. Bu nedenle, API arka planda işlerin uygulanma şeklinde esneklik sağlamalıdır.
- Hesap makinesinin, çalışmasının tamamı veya bir kısmı için GPU kullanımı konusunda maksimum esnekliğe izin verilmeli, gerektiğinde CPU ile birleştirilmelidir.
OpenGL ES Desteği
MediaPipe, Android/Linux üzerinde sürüm 3.2'ye ve ES 3.0'a kadar OpenGL ES'yi destekler iOS'te. MediaPipe ayrıca iOS'te Metal'i de destekler.
Aşağıdakilerin çalışması için OpenGL ES 3.1 veya sonraki sürümleri gerekir (Android/Linux sistemlerinde) makine öğrenimi çıkarım hesaplayıcıları ve grafikleri
MediaPipe, grafiklerin birden çok GL bağlamında OpenGL çalıştırmasına olanak tanır. Örneğin, daha yavaş bir GPU çıkarım yolunu birleştiren grafiklerde çok yararlı olabilir (ör. 10'da FPS) kullanarak (ör. 30 FPS'de): tek bir GL bağlamından itibaren bir sıralı komut kuyruğuna karşılık gelir ve her ikisi için de aynı bağlam kullanılır oluşturma kare hızını düşürür.
MediaPipe'in birden fazla bağlam kullanmasının çözümü, iletişime geçmelerini sağlar. Örnek bir senaryoda, hem oluşturma hem de çıkarım yollarına gönderilir ve oluşturma işleminin, çıkarımdan en son çıkışa erişir.
Bir OpenGL bağlamına aynı anda birden fazla iş parçacığı tarafından erişilemez. Ayrıca, aynı iş parçacığında etkin GL bağlamının değiştirilmesi bazı Android cihazlarda kullanabilirsiniz. Bu nedenle, yaklaşımımız herkesin ihtiyaçlarına görebilirsiniz. Her iş parçacığı, GL komutları yayınlayarak seri komut sırası oluşturur bu işlem, daha sonra GPU tarafından eşzamansız olarak yürütülür.
GPU Hesap Makinesinin Ömrü
Bu bölümde, GPU'ya ait Process yönteminin temel yapısı açıklanmaktadır.
GlBasit Hesaplayıcı temel sınıftan türetilen hesap makinesi. GPU hesaplayıcı
Örnek olarak LuminanceCalculator
gösterilmektedir. Yöntem
LuminanceCalculator::GlRender
, GlSimpleCalculator::Process
numarasından çağrılıyor.
// 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();
}
Yukarıda belirtilen tasarım ilkeleri şu tasarımla sonuçlandı: seçenekleri sunar:
- Resim verilerini temsil eden ve GPU kullanımı için optimize edilmiş
GpuBuffer
adlı bir GPU veri türümüz vardır. Bu veri türünün tam içerikleri opak ve platforma özgüdür. - GPU'dan yararlanmak isteyen her hesaplayıcının
GlCalculatorHelper
sınıfının bir örneğini oluşturup sahip olduğu, bileşime dayalı alt düzey bir API. Bu sınıf, OpenGL bağlamını yönetmek, giriş ve çıkışlar için dokular ayarlamak vb. işlemler için platformdan bağımsız bir API sunar. - Resim filtrelerini uygulayan basit hesap makinelerinin
GlSimpleCalculator
ve özel OpenGL kodlarıyla yalnızca birkaç sanal yöntemi geçersiz kılmasının gerektiği, alt sınıfa dayalı üst sınıf bir API. Tüm tesisat işleri ise üst sınıf tarafından halledilir. - Tüm GPU tabanlı hesap makineleri arasında paylaşılması gereken veriler, grafik hizmeti olarak uygulanmış ve
GlCalculatorHelper
sınıfı tarafından yönetilen harici bir giriş olarak sağlanır. - Hesap makinesine özgü yardımcıların ve paylaşılan bir grafik hizmetinin kombinasyonu, GPU kaynağını yönetme konusunda bize büyük esneklik sağlar: Her hesap makinesi için ayrı bir bağlama sahip olabilir, tek bir bağlamı paylaşabilir, bir kilit veya diğer temel senkronizasyon öğelerini paylaşabiliriz vb. bunların hepsi yardımcı tarafından yönetilir ve bağımsız hesaplayıcılardan gizlenebilir.
GpuBuffer - ImageFrame Dönüştürücüler
GpuBufferToImageFrameCalculator
ve ImageFrameToGpuBufferCalculator
adında iki hesap makinesi sunuyoruz. Bu hesap makineleri ImageFrame
ile GpuBuffer
arasında dönüştürme yaparak GPU ve CPU hesap makinelerini birleştiren grafiklerin oluşturulmasına olanak tanır. Bunlar hem iOS hem de Android'de desteklenir
Mümkün olduğunda bu hesaplayıcılar, kopyalama yapmadan CPU ile GPU arasında veri paylaşmak için platforma özel işlevler kullanır.
Aşağıdaki şemada, kameradan video kaydeden, bunu bir MediaPipe grafiğinde çalıştıran ve sonucu ekranda gerçek zamanlı olarak oluşturan bir mobil uygulamadaki veri akışı gösterilmektedir. Kesikli çizgi, MediaPipe grafiğinde hangi bölümlerin uygun olduğunu gösterir. Bu uygulama, OpenCV kullanarak CPU üzerinde bir Canny kenar algılama filtresi çalıştırır ve GPU'yu kullanarak bunu orijinal videonun üzerine yerleştirir.
Kameradan alınan video kareleri, grafiğe GpuBuffer
paketler halinde aktarılır. İlgili içeriği oluşturmak için kullanılan
giriş akışına paralel olarak iki hesap makinesi tarafından erişilir.
GpuBufferToImageFrameCalculator
, tamponu bir ImageFrame
biçimine dönüştürür.
Bu daha sonra, gri tonlamalı bir dönüştürücü ve bir canny filtresi üzerinden (hem
çalıştırıldığından) emin olun. Bu işlemin çıkışı daha sonra bir
GpuBuffer
tekrar. Çok girişli bir GPU hesaplayıcı olan GlOverlayCalculator
Hem orijinal GpuBuffer
hem de kenar dedektöründen çıkanı girin,
ve gölgelendirici kullanarak üst üste bindirebilir. Çıkış, daha sonra
geri arama hesaplayıcısı kullanan bir uygulamayla oluşturulan bir uygulamadır; uygulama,
ekrana getirelim.