Përshëndetje Botë! në Android

Hyrje

Kjo Hello World! tutoriali përdor MediaPipe Framework për të zhvilluar një aplikacion Android që ekzekuton një grafik MediaPipe në Android.

Çfarë do të ndërtoni

Një aplikacion i thjeshtë kamere për zbulimin e skajeve të Sobel në kohë reale aplikohet në një transmetim video të drejtpërdrejtë në një pajisje Android.

edge_detection_android_gpu_gif

Konfigurimi

  1. Instaloni MediaPipe Framework në sistemin tuaj, shikoni udhëzuesin e instalimit të Framework për detaje.
  2. Instaloni Android Development SDK dhe Android NDK. Shihni se si ta bëni këtë edhe te [Udhëzuesi i instalimit të kornizës].
  3. Aktivizo opsionet e zhvilluesit në pajisjen tënde Android.
  4. Konfiguro Bazel në sistemin tënd për të ndërtuar dhe vendosur aplikacionin Android.

Grafiku për zbulimin e skajeve

Ne do të përdorim grafikun e mëposhtëm, 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"
}

Një vizualizimi i grafikut është paraqitur më poshtë:

edge_detection_mobile_gpu

Ky grafik ka një transmetim të vetëm hyrje të quajtur input_video për të gjitha kornizat hyrëse që do të ofrohen nga kamera e pajisjes suaj.

Nyja e parë në grafik, LuminanceCalculator , merr një paketë të vetme (kornizë imazhi) dhe zbaton një ndryshim në ndriçimin duke përdorur një shader OpenGL. Korniza e imazhit që rezulton dërgohet në rrjedhën e daljes luma_video .

Nyja e dytë, SobelEdgesCalculator aplikon zbulimin e skajeve për paketat hyrëse në rrjedhën luma_video dhe nxjerr rezultatet në rrjedhën e daljes output_video .

Aplikacioni ynë Android do të shfaqë kornizat e imazhit në dalje të transmetimit output_video .

Konfigurimi fillestar minimal i aplikacionit

Fillimisht fillojmë me një aplikacion të thjeshtë Android që shfaq "Hello World!" në ekran. Ju mund ta kaloni këtë hap nëse jeni njohur me ndërtimin e aplikacioneve Android duke përdorur bazel .

Krijoni një direktori të re ku do të krijoni aplikacionin tuaj Android. Për shembull, kodi i plotë i këtij udhëzuesi mund të gjendet në mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic . Ne do t'i referohemi kësaj rruge si $APPLICATION_PATH në të gjithë laboratorin e kodeve.

Vini re se në rrugën drejt aplikacionit:

  • Aplikacioni quhet helloworld .
  • $PACKAGE_PATH e aplikacionit është com.google.mediapipe.apps.basic . Kjo përdoret në copat e kodit në këtë tutorial, prandaj mos harroni të përdorni $PACKAGE_PATH tuajin kur kopjoni/përdorni copat e kodit.

Shto një skedar activity_main.xml$APPLICATION_PATH/res/layout . Kjo shfaq një TextView në ekranin e plotë të aplikacionit me vargun Hello World! :

<?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>

Shtoni një MainActivity.java të thjeshtë në $APPLICATION_PATH që ngarkon përmbajtjen e paraqitjes activity_main.xml siç tregohet më poshtë:

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);
  }
}

Shto një skedar manifesti, AndroidManifest.xml$APPLICATION_PATH , i cili nis MainActivity në fillimin e aplikacionit:

<?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>

Në aplikacionin tonë ne po përdorim një temë Theme.AppCompat në aplikacion, ndaj na duhen referenca të përshtatshme teme. Shto colors.xml te $APPLICATION_PATH/res/values/ :

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

Shto styles.xml te $APPLICATION_PATH/res/values/ :

<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>

Për të ndërtuar aplikacionin, shtoni një skedar BUILD$APPLICATION_PATH dhe ${appName} dhe ${mainActivity} në manifest do të zëvendësohen nga vargjet e specifikuara në BUILD siç tregohet më poshtë.

android_library(
    name = "basic_lib",
    srcs = glob(["*.java"]),
    manifest = "AndroidManifest.xml",
    resource_files = glob(["res/**"]),
    deps = [
        "@maven//:androidx_appcompat_appcompat",
        "@maven//:androidx_constraintlayout_constraintlayout",
    ],
)

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",
    ],
)

Rregulli android_library shton varësi për MainActivity , skedarët e burimeve dhe AndroidManifest.xml .

Rregulli android_binary , përdor bibliotekën basic_lib Android të krijuar për të ndërtuar një APK binar për instalim në pajisjen tuaj Android.

Për të ndërtuar aplikacionin, përdorni komandën e mëposhtme:

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

Instaloni skedarin e krijuar APK duke përdorur adb install . Për shembull:

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

Hapni aplikacionin në pajisjen tuaj. Duhet të shfaqë një ekran me tekstin Hello World! .

bazel_hello_world_android

Përdorimi i kamerës nëpërmjet CameraX

Lejet e kamerës

Për të përdorur kamerën në aplikacionin tonë, duhet t'i kërkojmë përdoruesit të sigurojë akses në kamerë. Për të kërkuar lejet e kamerës, shtoni sa vijon në AndroidManifest.xml :

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

Ndryshoni versionin minimal të SDK në 21 dhe synoni versionin SDK në 27 në të njëjtin skedar:

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

Kjo siguron që përdoruesi të kërkohet të kërkojë leje për kamerën dhe na mundëson të përdorim bibliotekën CameraX për qasje në kamerë.

Për të kërkuar lejet e kamerës, ne mund të përdorim një mjet të ofruar nga komponentët MediaPipe Framework, përkatësisht PermissionHelper . Për ta përdorur atë, shtoni një varësi "//mediapipe/java/com/google/mediapipe/components:android_components" në rregullin mediapipe_libBUILD .

Për të përdorur PermissionHelperMainActivity , shtoni rreshtin e mëposhtëm në funksionin onCreate :

PermissionHelper.checkAndRequestCameraPermissions(this);

Kjo nxit përdoruesin me një dialog në ekran për të kërkuar leje për të përdorur kamerën në këtë aplikacion.

Shtoni kodin e mëposhtëm për të trajtuar përgjigjen e përdoruesit:

@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() {}

Tani për tani do ta lëmë bosh metodën startCamera() . Kur përdoruesi i përgjigjet kërkesës, MainActivity do të rifillojë dhe onResume() do të thirret. Kodi do të konfirmojë që lejet për përdorimin e kamerës janë dhënë dhe më pas do të nisë kamerën.

Rindërtoni dhe instaloni aplikacionin. Tani duhet të shihni një kërkesë që kërkon qasje në kamerë për aplikacionin.

Qasja në kamerë

Me lejet e kamerës të disponueshme, ne mund të nisim dhe të marrim korniza nga kamera.

Për të parë kornizat nga kamera do të përdorim një SurfaceView . Çdo kornizë nga kamera do të ruhet në një objekt SurfaceTexture . Për t'i përdorur këto, së pari duhet të ndryshojmë paraqitjen e aplikacionit tonë.

Hiqni të gjithë bllokun e kodit TextView nga $APPLICATION_PATH/res/layout/activity_main.xml dhe shtoni kodin e mëposhtëm në vend të tij:

<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>

Ky bllok kodi ka një FrameLayout të ri të quajtur preview_display_layout dhe një TextView të vendosur brenda tij, të quajtur no_camera_access_preview . Kur nuk jepen lejet e aksesit të kamerës, aplikacioni ynë do të shfaqë TextView me një mesazh vargu, të ruajtur në variablin no_camera_access . Shtoni rreshtin e mëposhtëm në skedarin $APPLICATION_PATH/res/values/strings.xml :

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

Kur përdoruesi nuk i jep leje kamerës, ekrani tani do të duket kështu:

missing_camera_permission_android

Tani, ne do të shtojmë objektet SurfaceTexture dhe SurfaceViewMainActivity :

private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;

Në funksionin onCreate(Bundle) , shtoni dy rreshtat e mëposhtëm përpara se të kërkoni lejet e kamerës:

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

Dhe tani shtoni kodin që përcakton setupPreviewDisplayView() :

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

Ne përcaktojmë një objekt të ri SurfaceView dhe e shtojmë atë në objektin preview_display_layout FrameLayout në mënyrë që të mund ta përdorim atë për të shfaqur kornizat e kamerës duke përdorur një objekt SurfaceTexture të quajtur previewFrameTexture .

Për të përdorur previewFrameTexture për marrjen e kornizave të kamerës, ne do të përdorim CameraX . Framework ofron një mjet të quajtur CameraXPreviewHelper për të përdorur CameraX . Kjo klasë përditëson një dëgjues kur kamera niset nëpërmjet onCameraStarted(@Nullable SurfaceTexture) .

Për të përdorur këtë mjet, modifikoni skedarin BUILD për të shtuar një varësi nga "//mediapipe/java/com/google/mediapipe/components:android_camerax_helper" .

Tani importoni CameraXPreviewHelper dhe shtoni linjën e mëposhtme në MainActivity :

private CameraXPreviewHelper cameraHelper;

Tani, ne mund të shtojmë zbatimin tonë në startCamera() :

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);
    });
}

Kjo krijon një objekt të ri CameraXPreviewHelper dhe shton një dëgjues anonim në objekt. Kur cameraHelper sinjalizon se kamera ka filluar dhe ekziston një surfaceTexture për të rrëmbyer kornizat, ne e ruajmë atë surfaceTexture si previewFrameTexture dhe e bëjmë të dukshme previewDisplayView në mënyrë që të mund të fillojmë të shohim korniza nga previewFrameTexture .

Megjithatë, përpara se të nisim kamerën, duhet të vendosim se cilën kamerë duam të përdorim. CameraXPreviewHelper trashëgon nga CameraHelper i cili ofron dy opsione, FRONT dhe BACK . Ne mund ta kalojmë vendimin nga skedari BUILD si meta të dhëna në mënyrë që të mos kërkohet ndryshim i kodit për të ndërtuar një version tjetër të aplikacionit duke përdorur një kamerë tjetër.

Duke supozuar se duam të përdorim kamerën BACK për të kryer zbulimin e skajeve në një skenë të drejtpërdrejtë që shohim nga kamera, shtoni meta të dhënat në AndroidManifest.xml :

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

dhe specifikoni zgjedhjen në BUILD në rregullin binar android helloworld me një hyrje të re në manifest_values :

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

Tani, në MainActivity për të tërhequr meta të dhënat e specifikuara në manifest_values , shtoni një objekt ApplicationInfo :

private ApplicationInfo applicationInfo;

Në funksionin onCreate() , shtoni:

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

Tani shtoni rreshtin e mëposhtëm në fund të funksionit startCamera() :

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

Në këtë pikë, aplikacioni duhet të ndërtohet me sukses. Megjithatë, kur të ekzekutoni aplikacionin në pajisjen tuaj, do të shihni një ekran të zi (edhe pse lejet e kamerës janë dhënë). Kjo ndodh sepse edhe pse ruajmë variablin surfaceTexture të ofruar nga CameraXPreviewHelper , previewSurfaceView nuk e përdor ende daljen e tij dhe nuk e shfaq atë në ekran.

Meqenëse duam të përdorim kornizat në një grafik MediaPipe, nuk do të shtojmë kod për të parë daljen e kamerës drejtpërdrejt në këtë tutorial. Në vend të kësaj, ne kalojmë përpara se si mund të dërgojmë kornizat e kamerës për përpunim në një grafik MediaPipe dhe të shfaqim daljen e grafikut në ekran.

Konfigurimi ExternalTextureConverter

Një SurfaceTexture kap kornizat e imazhit nga një transmetim si një teksturë OpenGL ES. Për të përdorur një grafik MediaPipe, kornizat e kapura nga kamera duhet të ruhen në një objekt të rregullt me ​​teksturë Open GL. Framework ofron një klasë, ExternalTextureConverter për të kthyer imazhin e ruajtur në një objekt SurfaceTexture në një objekt të rregullt teksture OpenGL.

Për të përdorur ExternalTextureConverter , na duhet gjithashtu një EGLContext , i cili krijohet dhe menaxhohet nga një objekt EglManager . Shtoni një varësi në skedarin BUILD për të përdorur EglManager , "//mediapipe/java/com/google/mediapipe/glutil" .

MainActivity , shtoni deklaratat e mëposhtme:

private EglManager eglManager;
private ExternalTextureConverter converter;

Në funksionin onCreate(Bundle) , shtoni një deklaratë për të inicializuar objektin eglManager përpara se të kërkoni lejet e kamerës:

eglManager = new EglManager(null);

Kujtoni që ne përcaktuam funksionin onResume()MainActivity për të konfirmuar se lejet e kamerës janë dhënë dhe telefononi startCamera() . Përpara këtij kontrolli, shtoni rreshtin e mëposhtëm në onResume() për të inicializuar objektin converter :

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

Ky converter tani përdor GLContext të menaxhuar nga eglManager .

Ne gjithashtu duhet të anashkalojmë funksionin onPause()MainActivity në mënyrë që nëse aplikacioni shkon në një gjendje të ndërprerë, ne e mbyllim converter siç duhet:

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

Për të nxjerrë daljen e previewFrameTextureconverter , shtoni bllokun e mëposhtëm të kodit në setupPreviewDisplayView() :

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) {}
     });

Në këtë bllok kodi, ne shtojmë një SurfaceHolder.Callback të personalizuar për të previewDisplayView dhe zbatojmë funksionin surfaceChanged(SurfaceHolder holder, int format, int width, int height) për të llogaritur një madhësi të përshtatshme të ekranit të kornizave të kamerës në ekranin e pajisjes dhe për të lidhur objekti previewFrameTexture dhe dërgoni kornizat e madhësisë së displaySize të llogaritur te converter .

Tani jemi gati të përdorim kornizat e kamerës në një grafik MediaPipe.

Përdorimi i një grafiku MediaPipe në Android

Shtoni varësitë përkatëse

Për të përdorur një grafik MediaPipe, duhet të shtojmë varësi në kornizën MediaPipe në Android. Fillimisht do të shtojmë një rregull ndërtimi për të ndërtuar një cc_binary duke përdorur kodin JNI të kornizës MediaPipe dhe më pas do të ndërtojmë një rregull cc_library për të përdorur këtë binar në aplikacionin tonë. Shtoni bllokun e mëposhtëm të kodit në skedarin tuaj BUILD :

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,
)

Shtoni varësinë ":mediapipe_jni_lib" në rregullin e ndërtimit të mediapipe_lib në skedarin BUILD .

Më pas, duhet të shtojmë varësi specifike për grafikun MediaPipe që duam të përdorim në aplikacion.

Së pari, shtoni varësi në të gjithë kodin e kalkulatorit në rregullin e ndërtimit libmediapipe_jni.so :

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

Grafikët MediaPipe janë skedarë .pbtxt , por për t'i përdorur ato në aplikacion, duhet të përdorim rregullin e ndërtimit të mediapipe_binary_graph për të gjeneruar një skedar .binarypb .

Në rregullin e ndërtimit binar të androidit helloworld , shtoni objektivin mediapipe_binary_graph specifik për grafikun si një aktiv:

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

Në rregullin e ndërtimit të assets , mund të shtoni gjithashtu asete të tjera, si modelet TensorFlowLite të përdorura në grafikun tuaj.

Përveç kësaj, shtoni manifest_values shtesë për vetitë specifike të grafikut, që do të merren më vonë në MainActivity :

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",
},

Vini re se binaryGraphName tregon emrin e skedarit të grafikut binar, të përcaktuar nga fusha output_name në objektivin mediapipe_binary_graph . inputVideoStreamName dhe outputVideoStreamName janë emri i transmetimit të videos hyrëse dhe dalëse të specifikuar në grafik përkatësisht.

Tani, MainActivity duhet të ngarkojë kornizën MediaPipe. Gjithashtu, korniza përdor OpenCV, kështu që MainActvity duhet të ngarkojë gjithashtu OpenCV . Përdorni kodin e mëposhtëm në MainActivity (brenda klasës, por jo brenda ndonjë funksioni) për të ngarkuar të dy varësitë:

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

Përdorni grafikun në MainActivity

Së pari, ne duhet të ngarkojmë aktivin që përmban .binarypb të përpiluar nga skedari .pbtxt i grafikut. Për ta bërë këtë, ne mund të përdorim një mjet MediaPipe, AndroidAssetUtil .

Inicializoni menaxherin e aseteve në onCreate(Bundle) përpara se të inicializoni eglManager :

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

Tani, ne duhet të konfigurojmë një objekt FrameProcessor që dërgon kornizat e kamerës të përgatitura nga converter në grafikun MediaPipe dhe ekzekuton grafikun, përgatit daljen dhe më pas përditëson previewDisplayView për të shfaqur daljen. Shtoni kodin e mëposhtëm për të deklaruar FrameProcessor :

private FrameProcessor processor;

dhe inicializoni atë në onCreate(Bundle) pas inicializimit të eglManager :

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

processor duhet të konsumojë kornizat e konvertuara nga converter për përpunim. Shtoni rreshtin e mëposhtëm në onResume() pas inicializimit të converter :

converter.setConsumer(processor);

processor duhet të dërgojë daljen e tij në previewDisplayView Për ta bërë këtë, shtoni përkufizimet e funksioneve të mëposhtme në SurfaceHolder.Callback tonë të personalizuar:

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

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

Kur krijohet SurfaceHolder , ne kishim SurfaceVideoSurfaceOutputprocessor . Kur shkatërrohet, ne e heqim atë nga VideoSurfaceOutput e processor .

Dhe kaq! Tani duhet të jeni në gjendje të ndërtoni dhe ekzekutoni me sukses aplikacionin në pajisje dhe të shihni zbulimin e skajeve Sobel që funksionon në një furnizim të drejtpërdrejtë të kamerës! urime!

edge_detection_android_gpu_gif

Nëse keni hasur në ndonjë problem, ju lutemi shikoni kodin e plotë të tutorialit këtu .