HUAWEI AR Engine — 2 (Türkçe)

Face Mesh

Kadirtas
Huawei Developers - Türkiye
9 min readOct 19, 2020

--

Merhabalar…

Bu yazımda HUAWEI AR Engine’in sunduğu Face Mesh özelliğini bir demo uygulama geliştirerek elimden geldiğince anlatmaya çalışacağım. Ayrıca, daha önce yine HUAWEI AR Engine’in sunduğu Body Tracking özelliği ve rakipleriyle yaptığım kıyaslama üzerine bilgi edinmek isterseniz bu serinin 1. yazısını da okumanızı tavsiye ederim.

HUAWEI AR Engine’in sunmuş olduğu bu özellik, yüzdeki pozları ve ifadelere karşılık gelen parametre değerlerini gerçek zamanlı olarak hesaplayarak sanal karakterin yüz ifadeleri üzerinde titiz bir kontrol sağlar. Yüz görüntü bilgilerini takip etmek ve elde etmek, yüz ifadelerini gerçek zamanlı olarak anlamak ve yüz ifadelerini çeşitli ifade parametrelerine dönüştürmek için bu özelliği sağlayarak sanal karakterlerin ifadelerinin kontrol edilmesini sağlar.

Ayrıca AR Engine, gözleri, kaşları, göz yuvarlarını, ağzı ve dili kapsayan 64 tür yüz ifadesinin tanınmasını destekler.

Buna ek olarak Face Mesh özelliği ile bir yüzün poz ve ağ modeli verilerini gerçek zamanlı olarak hesaplar. Ağ modeli verileri, yüz hareketlerini hesaba katacak şekilde değişir.

Yüksek hassasiyetli Face Mesh modelleme ve izleme yetenekleri sağlayarak, yüz görüntüsü bilgilerini aldıktan sonra gerçek zamanlı olarak son derece gerçekçi bir örgü modeli sunar. Ağ modeli, doğru gerçek zamanlı yanıt vermesi için yüze göre konumunu ve şeklini değiştirir.

AR Engine, yüz hatlarını tam olarak belirlemek ve genel kullanıcı deneyimini geliştirmek için 4.000'den fazla köşe ve 7.000 üçgen içeren bir ağ sağlar.

Şimdi bir demo uygulama geliştirerek, bu özelliği ve sağladıklarını daha detaylı bir şekilde anlatmaya çalışacağım.

Aşağıdaki şekil HUAWEI AR Engine SDK’nın genel kullanım sürecini göstermektedir. Biz de Activity’nin onResume fonksiyonunda başlatacağımız ARSession ile bu süreci başlatmış olacağız.

Bu uygulamayı geliştirirken aşağıdaki şekilde gördüğünüz Engine Functionality kısmı ile başlayacağız. Sonra render manager sınıfını geliştireceğiz, ardından activity yani UI kısmını yazarak bu yazıyı tamamlayacağız.

Bu demo uygulamayı geliştirirken diğer yazımda olduğu gibi yine rendering için OpenGL kütüphanesinden yararlanacağız. Bunun için FaceRenderManager adında OpenGL’in GLSurfaceView.Renderer interface’ini implement eden bir sınıf oluşturacağız. Bu interface’in onSurfaceCreated metotunda shader’ları oluşturup renderlayacağız. Öncelikle, yüzün geometri özelliklerini çizim kısmından başlayacağız.

1- Face Geometry

Bu bölümde yüz geometri özelliklerinin çizimini yapacağız.

a. Shader Oluşturma ve Ekleme

Öncelikle kullanacağımız vertex shader ve fragment shader programlarımızı tanımlıyoruz.

Bu yazdıklarımız bize yüz işleme sıralı işlemlerinde belirli programlanabilir aşamaları için kod sağlamak için kullanacağımız fragment ve vertex shader programlarımızdır.

Şimdi sırada shader programları oluşturmak var. Bunun için aşağıdaki kodu ekliyoruz. Bu metoda gerekli olan shader türünü ve shader kaynak kodunu parametre olarak vererek çağırdığımızda önce boş bir shader objesi oluşturuyoruz, sonra kaynak kodu verip derliyoruz. Ve geriye başvurulabilecek (referenced) integer bir değer alıyoruz. Bu değer ile sonraki adımlara devam edeceğiz.

Şimdi, bu metodu yüz geometrisini oluşturmak için, hem vertex shader hem de fragment shader oluşturmak için çağıracağız. Bunun için daha önceden yazdığımız FACE_GEOMETRY_VERTEX ve FACE_GEOMETRY_FRAGMENT kodlarını, bu metoda paslayacağız. Ardından oluşturduğumuz program objesine derlenmiş shader’ları, sonrasında bağlamak üzere ekliyoruz. Bu oluşturduğumuz program objesini kullanmak için bağlıyoruz.

Şimdi yazdığımız metodu çağırıp kullanacağımız değerleri tanımlayalım. Bu değerleri tanımladık çünkü frame çizilirken (onDrawFrame metodunda), noktaları çizmek için bu değerleri kullanacağız.

Şimdi, yazdığımız fonksiyonları kullanarak, shader programları oluşturmak da dahil olmak üzere, yüz geometrisiyle ilgili OpenGL ES oluşturmayı başlatacağız. İleride, surface oluşturulduğunda OpenGL ES oluşturmak için bu fonksiyonu, GLSurfaceView.Renderer arayüzünün override ettiğimiz onSurfaceCreated fonksiyonundan çağıracağız ki OpenGL ES oluşturabilelim.

b. Yüz Geometrisini Oluşturmak için OpenGL Başlatma

İlk olarak 2 tane buffer objesi oluşturuyoruz. Bu bufferlar bizim vertice bilgilerimizi ve üçgen bilgilerimizi tutacak. Sonrasında bunlar üzerinden güncelleme yaparak görsel elde edeceğiz.

Vertex özellikleri için oluşturduğumuz buffer objelerinden ilkini array buffer’a bağlıyoruz. Buffer’ın boyutunu belirtiyoruz ve içine şimdilik hiçbir data koymuyoruz. Sonrasında OpenGL’e, DYNAMIC_DRAW ile bu buffer’ın içindeki değerleri sıkça güncelleyeceğimizi ve dolayısıyla bu değerleri optimize etmemesini söylüyoruz. Son olarak, optimizasyon için “unbind“ yapıyoruz.

Sonra aşağıdaki kodu yukarıdaki init fonksiyonuna ekleyerek, oluşturduğumuz buffer objelerinden ikincisini, GL_ELEMENT_ARRAY_BUFFER ile vertex array index bağlama noktasına bağlıyoruz. Bu şekilde dizinin sırasına göre köşeleri birleştirip çizimi yapabileceğiz.

Bunları ekledikten sonra, aşağıdaki kodu da yine init fonksiyonuna ekleyerek bir texture objesi oluşturup, GL_TEXTURE_2D noktasına bağlıyoruz.

Texture objesini bağladığımıza göre, aşağıdaki kodu init() fonksiyonuna ekleyerek artık “ a” bölümünde oluşturduğumuz createProgram() fonksiyonunu çağırarak, shader’ları ekleyip texture parametrelerini ayarlıyoruz.

init() fonksiyonunun son hali:

c. Yüz Görünümünün Çizilmesi

Bu bölümde,yüz çizim işlemleri için yani buffer’daki yüz geometri verisini güncellememiz için bazı adımlar var. Öncelikle yüz geometrisini elde edeceğiz. Bunun için HUAWEI AR Engine’in sunduğu ARFaceGeometry sınıfından yararlanacağız. Bu sınıfı ARSession’ın getAllTrackables() fonksiyonunu kullanarak elde edebiliriz. Bu şekilde kameradaki yüzü/yüzleri elde edebiliyoruz. Örn: ArSession.getAllTrackables(ARFace.class)

ARFaceGeometry sınıfını elde ettikten sonra bu sınıfı kullanarak kameranın gördüğü yüzlerin vertice’lerini, texture koordinatlarını, üçgen sayısını ve üçgen indislerini alacağız. Sonra bu verileri kullanarak, daha önce oluşturmuş olduğumuz ve “mVerticeId” ve “mTriangleId” ile belirttiğimiz buffer objelerinin içlerindeki verileri güncelleyeceğiz.

Şimdi ARFaceGeometry türünde bir objeyi parametre olarak alan, updateFaceGeometryData adındaki fonksiyonu oluşturalım. Ve bir üst paragrafta bahsettiğimiz işlemlerin kodlarını bu fonksiyonun içine yazalım.

Bildiğiniz gibi ekranda 3 boyutlu rendering için model, view ve projection matrisleri gerekiyor. Bu yüzden model için yüzün matrisini oluşturmamız gerekiyor. Bunun için HUAWEI AR Engine’in ARFace sınıfından yararlanacağız. Bu sınıfın getPose() fonksiyonu ile ARPose türündeki objeyi elde edeceğiz. Bu objeden model view matrisini elde ediyoruz. HUAWEI AR Engine’in ARCamera objesinden ise projection matrisini elde edip bunları çarpıyoruz. Bu şekilde model view projection (MVP) verisini güncelleyebileceğimiz matrisi elde ediyoruz. Şimdi bunları aşağıdaki fonksiyon ile gerçekleştirelim.

Yüz çizimin aşamasının son adımı olarak aşağıdaki fonksiyon ile çizimi tamamlıyoruz. Bu aşamaya kadar oluşturduğumuz ve tanımladığımız verileri bu fonksiyonda kullanarak, yüzün geometrik özelliklerinin çizmini gerçekleştireceğiz.

Not: Bu çizim fonksiyonları her frame için çağırılacaktır.

Yüz çizim işlemi için kullanacağımız bu fonksiyonları, GLSurfaceView.Renderer arayüzünün onDrawFrame fonksiyonundan çağırmakta kolaylık olması için, bu fonksiyonları sırasıyla çağıracak ana fonksiyonu tanımlayalım. Bu fonksiyona güncelleme işlemleri için gerekli matrisleri sağlayacak olan ARCamera ve ARFace türündeki objeleri çağıracağımız fonksiyondan göndermeliyiz. Bu objeleri HUAWEI AR Engine’in ArSession sınıfından elde edebiliriz.

ARSession.getAllTrackables(ARFace.class) returns Collection<ARFace.class>

ARSession.update returns ARFrame.class

Bu fonksiyon ile yüz çizimi yapacağımız bölümü tamamladık. Şimdi sırada texture display kısmı var.

2. Texture Görüntüsü

Bu bölümde texture çizimi işlemini yapacağız.

a. Shader Oluşturma ve Ekleme

Öncelikle Face Geometry kısmında yaptığımız çizim işlemindeki gibi kullanacağımız vertex shader ve fragment shader programlarımızı tanımlıyoruz.

Yukarıda yazmış olduğumuz shader programlarını aynen birinci bölümde yaptığımız gibi yükleyip derliyoruz.

Yine birinci bölümde yaptığımız gibi shader’ları oluşturup ekliyoruz. Ardından, oluşturduğumuz programı çizim sırasında glUseProgram() fonksiyonunu çağırarak kullanabilmek için önce glLinkProgram() fonksiyonunu kullanarak bağlıyoruz.

b. Texture Gösterimi için OpenGL Başlatma

Oluşturduğumuz fonksiyonları kullanarak shader’ları yükledikten sonra değerleri tanımlamak için aşağıdaki fonksiyonu ekliyoruz.

Şimdi de texture parametrelerini ayarlayacağımız fonksiyonu yazalım. mExternalTextureId değerini bu fonksiyonu çağıracağımız init() fonksiyonunda tanımlayacağız.

Buraya kadar olan her adım tanımlamalar içindi. Bu yüzden bu adımları, opengl.GLSurfaceView.Renderer interface’inin override ettiğimiz onSurfaceCreated() fonksiyonundan çağırabilmek için, init() fonksiyonunu oluşturalım.

Çizim için kullanacağımız bufferları da aşağıdaki fonksiyon ile tanımlayalım. Bu oluşturduğumuz buffer objelerini onDrawFrame fonksiyonunda kullanacağız.

c. Texture Görüntüsü Çizimi

Her surface değiştiğinde öncelikle, çizimde kullanacağımız projection matrisini güncellememiz gerekiyor. Bunun için aşağıdaki fonksiyonu yazıyoruz ve bu fonksiyonu değişimi algılayacağımız GLSurfaceView.Renderer arayüzünün onSurfaceChanged metotundan çağıracağız.

Şimdi projection matrisimiz ve tüm diğer verilerimizi tanımladığımıza ve güncellediğimize göre çizime geçebiliriz.

Çizim için değişim ile birlikte koordinatları güncelleyip haritanın doğru koordinatlarını elde etmemiz gerekecektir. Bunun için HUAWEI AR Engine’in ARFrame sınıfından yararlanacağız. ARFrame ile önce görüntünün değişip değişmediğini kontrol edeceğiz, sonra kamera tarafından yakalanan arka plan görüntüsünün doğru bir şekilde görüntülenebilmesi için doku eşleme koordinatlarını ayarlamamız gerekir. Bunun için ise aşağıdaki gibi ARFrame.transformDisplayUvCoords fonksiyonunu kullanacağız.

Şimdi bu kodu da ekleyerek onDrawFrame fonksiyonumuzu yazarak arkaplan çizim işleminin süreçlerini tamamlayalım.

Evet şimdi arkaplan çizimini tamamladık. Bu kodları yazmış olduğumuz TextureDisplay sınıfının son hali aşağıdaki gibidir.

3. Yüz Verilerinin Render Yönetimi

Şimdiye kadar yaptığımız her adım yüz çizim işlemleri içindi. Buna veri güncelleme, bufferları oluşturma ve OpenGL için parametre ayarlamaları dahildir. Biz şimdiye kadar tüm bunları yapabilmek için temeli oluşturduk. Bu kısımda ise oluşturduğumuz sınıfları ve fonksiyonları, AR Engine sayesinde elde ettiğimiz verilerle besleyerek renderlama süreçlerini yöneteceğiz.

Bunun için bir sınıf oluşturacağız ve bu sınıfın GLSurfaceView.Renderer arayüzünü implement etmesini sağlayacağız. Bu sınıfın içinde arayüze ait 3 adet fonksiyonu (onSurfaceCreated, onSurfaceChanged, onDrawFrame) override edeceğiz. Ve ayrıca Activity’den sınıfın içindeki değerleri ayarlamak için setter fonksiyonlarını yazacağız.

Önce onSurfaceCreated fonksiyonu ile başlayalım. Bu fonksiyon surface oluşturulduğunda ilk çağırılan fonksiyondur. Bu yüzden burada, daha önce yazmış olduğumuz TextureDisplay ve FaceGeometryDisplay sınıflarını başlatmak için, bu sınıfların init() fonksiyonlarını çağıracağız.

Not: Bu sınıfın üyesi olan değişkenleri daha sonra Activity’den setter fonksiyonlarıyla tanımlayacağız.

Şimdi demo için cihaz rotasyonlarına uyum sağlayacak bir sınıf oluşturmamız gerekiyor. Bu sınıf, cihaz rotasyon yöneticisi olarak kullanılacaktır. Ve Android’in DisplayListener arayüzünü uygulamalıdır. Her fonksiyonun açıklamasını kodda bulabilirsiniz.

Bir cihaz döndürüldüğünde, view boyutu ve cihazın döndürülüp döndürülmediği, AR Motoru tarafından döndürülen geometrik bilgileri doğru şekilde görüntülemek için güncellenmelidir. DisplayRotationManager ile rotation listener sınıfımızı yazdığımıza göre artık onSurfaceChanged fonksiyonumuzda güncellemeleri yapabiliriz.

Bu fonksiyon ile hem texture, hem viewport, hem de viewfinder boyutu ve cihaz dönme durumunu güncelliyoruz.

Verileri güncelleme işleminden sonra onDrawFrame fonksiyonu çalışacaktır.

İlk olarak, sürücüye önceki karenin piksellerini yüklememesini bildirmek için ekranı temizlememiz gerekiyor.

Ayrıca, ARSession’ın boş olup olmadığını kontrol etmeliyiz. Boş ise çizim sürecine devam edemeyiz.

ARSession boş değilse, mevcut cihazın döndürülüp döndürülmediğini kontrol etmeliyiz. Cihaz döndürülürse, DisplayManager sınıfının updateArSessionDisplayGeometry fonksiyonunu kullanarak mevcut ARSession’ın cihaz penceresini güncellemeliyiz.

Kamera akış verilerini depolamak için kullandığımız openGL textureId öğesini ayarlıyoruz. Texture kimliğini ayarladıktan sonra, HUAWEI AR Engine’in kamera önizlemesini texture kimliğine güncellemesi için mSession.update () ‘i çağırmamız gerekir. Yeni frame’i mSession.update () fonksiyonunu çağırarak ARFrame olarak alacağız.

Frame’i güncelledikten sonra hemen texture’da da bu güncellemeyi yapıyoruz. Bunun için elde ettiğimiz ARFrame objesini, daha önceden yazmış olduğumuz TextureDisplay sınıfının onDrawFrame sınıfına gönderiyoruz.

Şimdi sırada ekrandaki yüzleri yakalamak var. Bu bir yüz takip uygulaması olduğu için daha sonra değineceğim şekilde ARSession ile session oluştururken, konfigürasyon aşamasında istediğimiz şeyin, yani algılanacak görüntünün bir ARFace olduğunu uygulamaya bildireceğiz. Bu işlemi yapacağımız için, şu anda ARSession.getAllTrackables() fonksiyonundan ARFace istiyoruz.

Ardından, projection matrisi elde etmek için ARCamera objesini HUAWEI AR Engine’in ARFrame objesinden frame.getCamera() fonksiyonu ile elde ediyoruz. Sonrasında çizim işlemi için bu elde ettiğimiz ARCamera ve ARFace objelerini, daha önceden oluşturduğumuz FaceGeometryDisplay sınıfının onDrawFrame() fonksiyonuna gönderiyoruz.

Override ettiğimiz onDrawFrame fonksiyonunun son hali aşağıdaki gibidir.

Bu sınıfın son işlemi olarak setter fonksiyonlarını tanımlayalım. Ve FaceRenderManager sınıfının son hali aşağıdaki gibi olacaktır.

4. Activity

Bu bölümde, oluşturduğumuz FaceRenderManager sınıfını kullanarak render işlemini activity yaşam döngüsüne dahil edeceğiz.

Öncelikle bir CameraHelper sınıfı oluştaracağız. Bu sınıf, kameranın başlatılması, durdurulması, kameranın açılması ve kapatılması dahil olmak üzere kamera ile ilgili hizmetleri sağlayacaktır.

Şimdi activity için layout’umuza android.opengl.GLSurfaceView view’ini ekleyelim.

Ve Activity’nin onCreate fonksiyonu. Gördüğünüz gibi burada DisplayRotationManager gibi yardımcı sınıflar oluşturduk ve bazı openGL konfigürasyonları yaptık. onCreate fonksiyonunun sonunda mevcut cihazda HUAWEI AR Engine sunucusunun (com.huawei.arengine.service) kurulu olup olmadığını kontrol etmeliyiz. Activity sınıfının tamamını ekleyeceğim ve arEngineAbilityCheck metodunun içeriğini biraz sonra görebilirsiniz.

onResume fonksiyonunda AR Session’ı oluşturdum. Bu, AR Engine’in süreçlerinin onResume fonksiyonundan başladığı anlamına gelir.

Activity’nin onResume fonksiyonunda, öncelikle DisplayListener’ımızı kayıt ediyoruz.

DisplayListener’ı kayıt ettikten sonra, ARSession oluşturuyoruz. ARSession oluşturarak HUAWEI AR Engine’i burada başlatmış oluyoruz.

Bu bir yüz takip uygulaması olduğu için, daha önce de bahsettiğim gibi konfigürasyona ARFaceTrackingConfig vermemiz gerekiyor. Bunu yaparak, HUAWEI AR Engine’e algılaması gereken objenin bir yüz olduğunu bildiriyoruz. Bunun için öncelikle ARConfig objesini oluşturalım. ARConfig objesini elde etmek için aşağıdaki kodu onResume fonksiyonuna ekliyoruz.

Diğer konfigürasyonları yaptıktan sonra, ARSession’a bu konfigürasyon objesini ayarlıyoruz.

Bu işlemden ve bazı hata kontrollerinden sonra ARSession.resume() ile session’ı devam ettiriyoruz.

Son olarak değerleri ayarlıyoruz. Ve onResume() fonksiyonumuzu tamamlamış oluyoruz.

onResume() fonksiyonu:

Genel olarak HUAWEI AR Engine’i uygulamamıza eklemiş olduk. Bu aşamadan sonra onPause gibi fonsiyonlarda session durdurma, view durdurma gibi basamakları atlamamak için aşağıda activity nin son halini görebilirsiniz.

Şimdi uygulamamızı test edelim

Bu yazımda HUAWEI AR Engine’in Face Mesh özelliğini nasıl kullanacağımızı görmüş olduk. Gördüğünüz gibi yazı uzun olsa da diğer augmented reality uygulamalarına kıyasla HUAWEI AR Engine ile AR uygulamaları geliştirmek gerçekten çok kolay. Aslında tek yaptığımız AR Engine’ den elde ettiğimiz verilerle openGL bufferlarını güncellemek.

Umarım yararlı bir yazı olmuştur. Elimden geldiğince öğrenerek anlatmaya çalıştığım bu yazımda eksiklikler, hatalar veya önerileriniz varsa lütfen yorum olarak yazın. Okuduğunuz için teşekkürler.

Bir sonraki yazımda görüşmek üzere…

--

--