Android'de Hello World!

Giriş

Bu Hello World! eğiticisi, Android'de MediaPipe grafiği çalıştıran bir Android uygulaması geliştirmek için MediaPipe Framework'ü kullanır.

Ne oluşturacaksınız?

Android cihazdaki canlı video akışına gerçek zamanlı Sobel kenar algılama özelliği sağlayan basit bir kamera uygulaması.

edge_detection_android_gpu_gif

Kurulum

  1. Sisteminize MediaPipe Framework'ü yükleyin. Ayrıntılar için Çerçeve kurulum kılavuzuna bakın.
  2. Android Geliştirme SDK'sını ve Android NDK'yı yükleyin. Bunu nasıl yapacağınızı [Framework kurulum kılavuzu] bölümünde de bulabilirsiniz.
  3. Android cihazınızda geliştirici seçeneklerini etkinleştirin.
  4. Android uygulamasını derlemek ve dağıtmak için sisteminizde Bazel'i kurun.

Kenar algılama grafiği

Kullanılacak grafik edge_detection_mobile_gpu.pbtxt:

# MediaPipe graph that performs GPU Sobel edge detection on a live video stream.
# Used in the examples in
# mediapipe/examples/android/src/java/com/mediapipe/apps/basic and
# mediapipe/examples/ios/edgedetectiongpu.

# Images coming into and out of the graph.
input_stream: "input_video"
output_stream: "output_video"

# Converts RGB images into luminance images, still stored in RGB format.
node: {
  calculator: "LuminanceCalculator"
  input_stream: "input_video"
  output_stream: "luma_video"
}

# Applies the Sobel filter to luminance images stored in RGB format.
node: {
  calculator: "SobelEdgesCalculator"
  input_stream: "luma_video"
  output_stream: "output_video"
}

Aşağıda grafikle ilgili bir görsel yer almaktadır:

edge_detection_mobile_gpu

Bu grafikte, cihazınızın kamerası tarafından sağlanacak tüm gelen kareler için input_video adlı tek bir giriş akışı vardır.

Grafikteki ilk düğüm (LuminanceCalculator), tek bir paket (görüntü çerçevesi) alır ve OpenGL gölgelendirici kullanarak parlaklık değişikliği uygular. Elde edilen resim çerçevesi, luma_video çıkış akışına gönderilir.

İkinci düğüm olan SobelEdgesCalculator, luma_video akışındaki gelen paketlere uç algılama uygular ve çıkışta output_video çıkış akışı oluşturur.

Android uygulamamız, output_video akışının çıktı resmi çerçevelerini gösterir.

Minimum minimum uygulama kurulumu

İlk olarak, ekranda "Hello World!" yazan basit bir Android uygulamasıyla başlıyoruz. bazel kullanarak Android uygulamaları oluşturma konusunda bilgi sahibiyseniz bu adımı atlayabilirsiniz.

Android uygulamanızı oluşturacağınız yeni bir dizin oluşturun. Örneğin, bu eğitimin tüm kodunu mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic adresinde bulabilirsiniz. Codelab'de bu yol $APPLICATION_PATH olarak kullanılacaktır.

Uygulamaya giden yolda şunları unutmayın:

  • Uygulamanın adı helloworld.
  • Uygulamanın $PACKAGE_PATH özelliği com.google.mediapipe.apps.basic. Bu ad, bu eğiticideki kod snippet'lerinde kullanılmaktadır. Bu nedenle, kod snippet'lerini kopyaladığınızda/kullandığınızda kendi $PACKAGE_PATH öğenizi kullanmayı lütfen unutmayın.

$APPLICATION_PATH/res/layout hedefine bir activity_main.xml dosyası ekleyin. Uygulamanın tam ekranında Hello World! dizesiyle bir TextView görüntülenir:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

$APPLICATION_PATH öğesine, activity_main.xml düzeninin içeriğini aşağıda gösterildiği gibi yükleyen basit bir MainActivity.java ekleyin:

package com.google.mediapipe.apps.basic;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

/** Bare-bones main activity. */
public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
}

$APPLICATION_PATH uygulamasına, AndroidManifest.xml adlı manifest dosyası ekleyin. Uygulama başladığında MainActivity başlatılır:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.mediapipe.apps.basic">

  <uses-sdk
      android:minSdkVersion="19"
      android:targetSdkVersion="19" />

  <application
      android:allowBackup="true"
      android:label="${appName}"
      android:supportsRtl="true"
      android:theme="@style/AppTheme">
      <activity
          android:name="${mainActivity}"
          android:exported="true"
          android:screenOrientation="portrait">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
  </application>

</manifest>

Uygulamamızda Theme.AppCompat teması kullandığımız için uygun tema referanslarına ihtiyacımız var. colors.xml adlı kişiyi $APPLICATION_PATH/res/values/ adlı cihaza ekleyin:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
</resources>

$APPLICATION_PATH/res/values/ için styles.xml ekleyin:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

Uygulamayı derlemek için $APPLICATION_PATH öğesine bir BUILD dosyası ekleyin. Manifest dosyasındaki ${appName} ve ${mainActivity}, aşağıda gösterildiği gibi BUILD politikasında belirtilen dizelerle değiştirilir.

android_library(
    name = "basic_lib",
    srcs = glob(["*.java"]),
    manifest = "AndroidManifest.xml",
    resource_files = glob(["res/**"]),
    deps = [
        "//third_party:android_constraint_layout",
        "//third_party:androidx_appcompat",
    ],
)

android_binary(
    name = "helloworld",
    manifest = "AndroidManifest.xml",
    manifest_values = {
        "applicationId": "com.google.mediapipe.apps.basic",
        "appName": "Hello World",
        "mainActivity": ".MainActivity",
    },
    multidex = "native",
    deps = [
        ":basic_lib",
    ],
)

android_library kuralı; MainActivity, kaynak dosyaları ve AndroidManifest.xml için bağımlılıklar ekler.

android_binary kuralı, Android cihazınıza yüklemek üzere bir ikili program APK'sı oluşturmak için oluşturulan basic_lib Android kitaplığını kullanır.

Uygulamayı derlemek için aşağıdaki komutu kullanın:

bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld

Oluşturulan APK dosyasını adb install kullanarak yükleyin. Örneğin:

adb install bazel-bin/$APPLICATION_PATH/helloworld.apk

Cihazınızda uygulamayı açın. Hello World! metnini içeren bir ekran görünmelidir.

bazel_hello_world_android

Kamera, CameraX üzerinden kullanılıyor

Kamera İzinleri

Uygulamamızda kamerayı kullanmak için kullanıcıdan kameraya erişim izni vermesini istememiz gerekir. Kamera izinleri istemek için şunları ekleyin: AndroidManifest.xml:

<!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

Aynı dosyada minimum SDK sürümünü 21, hedef SDK sürümünü ise 27 olarak değiştirin:

<uses-sdk
    android:minSdkVersion="21"
    android:targetSdkVersion="27" />

Bu, kullanıcıdan kamera izni istemesinin istenmesini ve kamera erişimi için CameraX kitaplığını kullanabilmemizi sağlar.

Kamera izinleri istemek için MediaPipe Framework bileşenleri tarafından sağlanan PermissionHelper adlı bir yardımcı programı kullanabiliriz. Kullanmak için BUILD bölgesindeki mediapipe_lib kuralına "//mediapipe/java/com/google/mediapipe/components:android_components" bağımlılığı ekleyin.

MainActivity içinde PermissionHelper kullanmak için aşağıdaki satırı onCreate işlevine ekleyin:

PermissionHelper.checkAndRequestCameraPermissions(this);

Bu işlem, kullanıcıdan ekranda bir iletişim kutusuyla birlikte bu uygulamada kamerayı kullanmak için izin istemesini ister.

Kullanıcı yanıtını işlemek için aşağıdaki kodu ekleyin:

@Override
public void onRequestPermissionsResult(
    int requestCode, String[] permissions, int[] grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  PermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

@Override
protected void onResume() {
  super.onResume();
  if (PermissionHelper.cameraPermissionsGranted(this)) {
    startCamera();
  }
}

public void startCamera() {}

startCamera() yöntemini şimdilik boş bırakacağız. Kullanıcı isteme yanıt verdiğinde MainActivity devam ettirilir ve onResume() çağrılır. Kod, kamerayı kullanma izinlerinin verildiğini onaylar ve ardından kamerayı başlatır.

Uygulamayı yeniden derleyin ve yükleyin. Şimdi uygulama için kameraya erişim isteyen bir istem görmeniz gerekir.

Kamera Erişimi

Kamera izinleri varsa kameradan kare alıp getirebiliriz.

Kareleri kameradan görüntülemek için SurfaceView kullanacağız. Kameradan gelen her kare bir SurfaceTexture nesnesinde depolanır. Bunları kullanmak için önce uygulamamızın düzenini değiştirmemiz gerekir.

$APPLICATION_PATH/res/layout/activity_main.xml öğesinden TextView kod bloğunun tamamını kaldırın ve onun yerine aşağıdaki kodu ekleyin:

<FrameLayout
    android:id="@+id/preview_display_layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1">
    <TextView
        android:id="@+id/no_camera_access_view"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:gravity="center"
        android:text="@string/no_camera_access" />
</FrameLayout>

Bu kod bloğunun içinde preview_display_layout adında yeni bir FrameLayout ve içinde no_camera_access_preview adlı bir TextView bulunuyor. Kamera erişim izinleri verilmediğinde uygulamamız, no_camera_access değişkeninde depolanan bir dize mesajıyla TextView değerini gösterir. $APPLICATION_PATH/res/values/strings.xml dosyasına aşağıdaki satırı ekleyin:

<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>

Kullanıcı kamera izni vermediğinde ekran artık şu şekilde görünür:

missing_camera_permission_android

Şimdi SurfaceTexture ve SurfaceView nesnelerini MainActivity öğesine ekleyeceğiz:

private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;

onCreate(Bundle) işlevinde, kamera izni istemeden önce aşağıdaki iki satırı ekleyin:

previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();

Şimdi de setupPreviewDisplayView() özelliğini tanımlayan kodu ekleyin:

private void setupPreviewDisplayView() {
  previewDisplayView.setVisibility(View.GONE);
  ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
  viewGroup.addView(previewDisplayView);
}

Yeni bir SurfaceView nesnesi tanımlayıp preview_display_layout FrameLayout nesnesine ekleriz. Böylece, bu nesneyi previewFrameTexture adlı bir SurfaceTexture nesneyle kamera çerçevelerini görüntülemek için kullanabiliriz.

Kamera çerçevelerini almak üzere previewFrameTexture kullanmak için CameraX'i kullanacağız. Çerçeve, CameraX'i kullanmak için CameraXPreviewHelper adlı bir yardımcı program sağlar. Bu sınıf, kamera onCameraStarted(@Nullable SurfaceTexture) üzerinden başlatıldığında dinleyiciyi günceller.

Bu yardımcı programı kullanmak için BUILD dosyasını değiştirerek "//mediapipe/java/com/google/mediapipe/components:android_camerax_helper" ürününe bağımlılık ekleyin.

Şimdi CameraXPreviewHelper dosyasını içe aktarın ve aşağıdaki satırı MainActivity hedefine ekleyin:

private CameraXPreviewHelper cameraHelper;

Şimdi, uygulamamızı startCamera() öğesine ekleyebiliriz:

public void startCamera() {
  cameraHelper = new CameraXPreviewHelper();
  cameraHelper.setOnCameraStartedListener(
    surfaceTexture -> {
      previewFrameTexture = surfaceTexture;
      // Make the display view visible to start showing the preview.
      previewDisplayView.setVisibility(View.VISIBLE);
    });
}

Bu işlem, yeni bir CameraXPreviewHelper nesnesi oluşturur ve nesneye anonim bir işleyici ekler. cameraHelper, kameranın başladığını sinyalini verdiğinde ve kareleri yakalamak için surfaceTexture kullanılabilir olduğunda, surfaceTexture bunu previewFrameTexture olarak kaydeder ve previewFrameTexture içindeki kareleri görmeye başlayabilmemiz için previewDisplayView görünür hale gelir.

Ancak kamerayı başlatmadan önce hangi kamerayı kullanmak istediğimize karar vermemiz gerekiyor. CameraXPreviewHelper, iki seçenek sunan CameraHelper öğesinden devralmaktadır: FRONT ve BACK. Kararı BUILD dosyasından meta veri olarak iletebiliriz. Böylece, farklı bir kamera kullanarak uygulamanın başka bir sürümünü oluşturmak için kod değişikliği yapılmasına gerek kalmaz.

Kameradan görüntülediğimiz canlı bir sahnede kenar algılama işlemi gerçekleştirmek için BACK kamerasını kullanmak istediğimizi varsayarsak meta verileri AndroidManifest.xml bölümüne ekleyin:

      ...
      <meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
  </application>
</manifest>

ve helloworld Android ikili kuralındaki BUILD öğesinde seçimi manifest_values öğesinde yeni bir girişle belirtin:

manifest_values = {
    "applicationId": "com.google.mediapipe.apps.basic",
    "appName": "Hello World",
    "mainActivity": ".MainActivity",
    "cameraFacingFront": "False",
},

Şimdi, manifest_values politikasında belirtilen meta verileri almak için MainActivity uygulamasına bir ApplicationInfo nesnesi ekleyin:

private ApplicationInfo applicationInfo;

onCreate() işlevine şunu ekleyin:

try {
  applicationInfo =
      getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
  Log.e(TAG, "Cannot find application info: " + e);
}

Şimdi aşağıdaki satırı startCamera() işlevinin sonuna ekleyin:

CameraHelper.CameraFacing cameraFacing =
    applicationInfo.metaData.getBoolean("cameraFacingFront", false)
        ? CameraHelper.CameraFacing.FRONT
        : CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);

Bu noktada, uygulama başarıyla derlenmiş olmalıdır. Ancak uygulamayı cihazınızda çalıştırdığınızda (kamera izinleri verilmiş olsa bile) siyah bir ekran görürsünüz. Bunun nedeni, CameraXPreviewHelper tarafından sağlanan surfaceTexture değişkenini kaydetsek bile, previewSurfaceView öğesinin henüz çıkışını kullanmaması ve ekranda göstermemesidir.

Bir MediaPipe grafiğindeki kareleri kullanmak istediğimizden kamera çıkışını doğrudan bu eğiticide görüntülemek için kod eklemeyeceğiz. Bunun yerine, işlenmek üzere kamera çerçevelerini bir MediaPipe grafiğine nasıl göndereceğimize ve grafiğin çıkışını ekranda nasıl görüntüleyebileceğimize geçiyoruz.

ExternalTextureConverter kurulumu

SurfaceTexture, bir akıştaki resim karelerini OpenGL ES dokusu olarak yakalar. MediaPipe grafiği kullanmak için, kameradan çekilen karelerin normal bir Open GL doku nesnesinde depolanması gerekir. Çerçeve, bir SurfaceTexture nesnesinde depolanan resmi normal bir OpenGL doku nesnesine dönüştürmek için ExternalTextureConverter adlı bir sınıf sağlar.

ExternalTextureConverter aracını kullanmak için EglManager nesnesi tarafından oluşturulan ve yönetilen bir EGLContext gerekir. EglManager ve "//mediapipe/java/com/google/mediapipe/glutil" kullanmak için BUILD dosyasına bir bağımlılık ekleyin.

MainActivity içinde aşağıdaki beyanları ekleyin:

private EglManager eglManager;
private ExternalTextureConverter converter;

onCreate(Bundle) işlevinde, kamera izni istemeden önce eglManager nesnesini başlatmak için bir ifade ekleyin:

eglManager = new EglManager(null);

Kamera izinlerinin verildiğini onaylamak için MainActivity öğesinde onResume() işlevini tanımladığımızı ve startCamera() işlevini çağırdığımızı hatırlayın. Bu kontrolden önce converter nesnesini başlatmak için onResume() öğesine aşağıdaki satırı ekleyin:

converter = new ExternalTextureConverter(eglManager.getContext());

Bu converter artık eglManager tarafından yönetilen GLContext kullanıyor.

Ayrıca, uygulama duraklatılmış duruma geçerse converter öğesini düzgün bir şekilde kapatabilmemiz için MainActivity içinde onPause() işlevini geçersiz kılmamız gerekir:

@Override
protected void onPause() {
  super.onPause();
  converter.close();
}

previewFrameTexture çıkışını converter öğesine bağlamak için aşağıdaki kod bloğunu setupPreviewDisplayView() öğesine ekleyin:

previewDisplayView
 .getHolder()
 .addCallback(
     new SurfaceHolder.Callback() {
       @Override
       public void surfaceCreated(SurfaceHolder holder) {}

       @Override
       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
         // (Re-)Compute the ideal size of the camera-preview display (the area that the
         // camera-preview frames get rendered onto, potentially with scaling and rotation)
         // based on the size of the SurfaceView that contains the display.
         Size viewSize = new Size(width, height);
         Size displaySize = cameraHelper.computeDisplaySizeFromViewSize(viewSize);

         // Connect the converter to the camera-preview frames as its input (via
         // previewFrameTexture), and configure the output width and height as the computed
         // display size.
         converter.setSurfaceTextureAndAttachToGLContext(
             previewFrameTexture, displaySize.getWidth(), displaySize.getHeight());
       }

       @Override
       public void surfaceDestroyed(SurfaceHolder holder) {}
     });

Bu kod bloğunda, previewDisplayView öğesine özel bir SurfaceHolder.Callback ekler ve cihaz ekranındaki kamera çerçevelerinin uygun görüntü boyutunu hesaplamak ve previewFrameTexture nesnesini bağlayıp hesaplanan displaySize öğesinin karelerini converter öğesine göndermek için surfaceChanged(SurfaceHolder holder, int format, int width, int height) işlevini uygularız.

Artık kamera çerçevelerini MediaPipe grafiğinde kullanmaya hazırız.

Android'de MediaPipe grafiğini kullanma

Alakalı bağımlılıkları ekleyin

MediaPipe grafiği kullanmak için Android'de MediaPipe çerçevesine bağımlılıklar eklememiz gerekir. İlk olarak MediaPipe çerçevesinin JNI kodunu kullanarak cc_binary derlemek için bir derleme kuralı ekleyecek ve ardından bu ikili programı uygulamamızda kullanacak bir cc_library kuralı oluşturacağız. BUILD dosyanıza şu kod bloğunu ekleyin:

cc_binary(
    name = "libmediapipe_jni.so",
    linkshared = 1,
    linkstatic = 1,
    deps = [
        "//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
    ],
)

cc_library(
    name = "mediapipe_jni_lib",
    srcs = [":libmediapipe_jni.so"],
    alwayslink = 1,
)

":mediapipe_jni_lib" bağımlılığını BUILD dosyasındaki mediapipe_lib derleme kuralına ekleyin.

Daha sonra, uygulamada kullanmak istediğimiz MediaPipe grafiğine özgü bağımlılıkları eklememiz gerekir.

İlk olarak, libmediapipe_jni.so derleme kuralındaki tüm hesap makinesi kodlarına bağımlılık ekleyin:

"//mediapipe/graphs/edge_detection:mobile_calculators",

MediaPipe grafikleri .pbtxt dosyalarıdır ancak bunları uygulamada kullanmak için .binarypb dosyası oluşturmak üzere mediapipe_binary_graph derleme kuralını kullanmamız gerekir.

helloworld Android ikili derleme kuralında, grafiğe özgü mediapipe_binary_graph hedefini öğe olarak ekleyin:

assets = [
  "//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",

assets derleme kuralında, grafiğinizde kullanılan TensorFlowLite modelleri gibi başka öğeler de ekleyebilirsiniz.

Buna ek olarak, grafiğe özel özellikler için daha sonra MainActivity içinde alınacak ek manifest_values ekleyin:

manifest_values = {
    "applicationId": "com.google.mediapipe.apps.basic",
    "appName": "Hello World",
    "mainActivity": ".MainActivity",
    "cameraFacingFront": "False",
    "binaryGraphName": "mobile_gpu.binarypb",
    "inputVideoStreamName": "input_video",
    "outputVideoStreamName": "output_video",
},

binaryGraphName değerinin, ikili grafiğin dosya adını gösterdiğini unutmayın. Bu, mediapipe_binary_graph hedefindeki output_name alanı tarafından belirlenir. inputVideoStreamName ve outputVideoStreamName, grafikte belirtilen giriş ve çıkış video akışı adıdır.

Şimdi MainActivity cihazının MediaPipe çerçevesini yüklemesi gerekir. Ayrıca, çerçeve OpenCV'yi kullandığından MainActvity, OpenCV dosyasını da yüklemelidir. Her iki bağımlılığı da yüklemek için MainActivity içinde (sınıfın içinde ancak herhangi bir işlevin içinde değil) aşağıdaki kodu kullanın:

static {
  // Load all native libraries needed by the app.
  System.loadLibrary("mediapipe_jni");
  System.loadLibrary("opencv_java3");
}

Grafiği MainActivity aracında kullanın

Öncelikle, grafiğin .pbtxt dosyasından derlenen .binarypb öğesini içeren öğeyi yüklememiz gerekir. Bunu yapmak için bir MediaPipe yardımcı programı (AndroidAssetUtil) kullanabiliriz.

eglManager öğesini başlatmadan önce onCreate(Bundle) içinde öğe yöneticisini ilk kullanıma hazırlayın:

// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);

Şimdi, converter tarafından hazırlanan kamera çerçevelerini MediaPipe grafiğine gönderen ve grafiği çalıştıran, çıkışı hazırlayan ve daha sonra, çıktıyı görüntülemek için previewDisplayView öğesini güncelleyen bir FrameProcessor nesnesi oluşturmamız gerekiyor. FrameProcessor özelliğini bildirmek için aşağıdaki kodu ekleyin:

private FrameProcessor processor;

ve eglManager uygulamasını başlattıktan sonra onCreate(Bundle) içinde başlat:

processor =
    new FrameProcessor(
        this,
        eglManager.getNativeContext(),
        applicationInfo.metaData.getString("binaryGraphName"),
        applicationInfo.metaData.getString("inputVideoStreamName"),
        applicationInfo.metaData.getString("outputVideoStreamName"));

processor, işleme için converter öğesinden dönüştürülen kareleri kullanmalıdır. converter başlatıldıktan sonra aşağıdaki satırı onResume() öğesine ekleyin:

converter.setConsumer(processor);

processor, çıkışını previewDisplayView adresine göndermelidir. Bunu yapmak için aşağıdaki işlev tanımlarını özel SurfaceHolder.Callback aracımıza ekleyin:

@Override
public void surfaceCreated(SurfaceHolder holder) {
  processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
  processor.getVideoSurfaceOutput().setSurface(null);
}

SurfaceHolder oluşturulduğunda, processor öğesinin VideoSurfaceOutput ile Surface arasındaki kısmı vardı. İmha edildiğinde processor VideoSurfaceOutput hükümlerinden kaldırırız.

Hepsi bu kadar! Artık uygulamayı cihazda başarıyla derleyip çalıştırabilir ve canlı kamera feed'inde Sobel kenar algılamanın çalıştığını görebilirsiniz. Tebrikler!

edge_detection_android_gpu_gif

Herhangi bir sorunla karşılaşırsanız lütfen eğitimin tam koduna buradan ulaşabilirsiniz.