Objekte mit ML Kit unter Android erkennen und verfolgen

Mit ML Kit können Sie Objekte in Videoframes erkennen und verfolgen.

Wenn Sie ML Kit-Bilder übergeben, gibt ML Kit für jedes Bild eine Liste bis zu fünf erkannte Objekte und ihre Position im Bild. Bei der Erkennung -Objekten in Videostreams enthält, hat jedes Objekt eine ID, mit der Sie das Ereignis auf mehrere Bilder hin. Optional können Sie auch das grobe Objekt aktivieren. -Klassifizierung, bei der Objekte mit allgemeinen Kategoriebeschreibungen gekennzeichnet werden.

Hinweis

  1. Fügen Sie Ihrem Android-Projekt Firebase hinzu, falls noch nicht geschehen.
  2. Abhängigkeiten für die ML Kit-Android-Bibliotheken zu Ihrem Modul hinzufügen Gradle-Datei auf App-Ebene (in der Regel app/build.gradle):
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
      implementation 'com.google.firebase:firebase-ml-vision-object-detection-model:19.0.6'
    }

1. Objektdetektor konfigurieren

Um mit der Erkennung und dem Tracking von Objekten zu beginnen, erstellen Sie zunächst eine Instanz von FirebaseVisionObjectDetector und optional die von Ihnen festgelegten Detektoreinstellungen die Standardeinstellung ändern möchten.

  1. Konfigurieren Sie die Objekterkennung für Ihren Anwendungsfall mit einem FirebaseVisionObjectDetectorOptions-Objekt. Sie können Folgendes ändern: Einstellungen:

    Einstellungen für Objektdetektoren
    Erkennungsmodus STREAM_MODE (Standard) | SINGLE_IMAGE_MODE

    In STREAM_MODE (Standardeinstellung) wird der Objektdetektor ausgeführt mit niedriger Latenz, führt aber möglicherweise zu unvollständigen Ergebnissen (z. B. nicht spezifizierten Begrenzungsrahmen oder Kategorielabels) Aufrufe des Detektors. Außerdem gibt es in STREAM_MODE weist der Detektor Objekten Tracking-IDs zu, mit denen Sie um Objekte über Frames hinweg zu verfolgen. Verwenden Sie diesen Modus, wenn Sie oder wenn eine niedrige Latenz wichtig ist, z. B. bei der Verarbeitung Videostreams in Echtzeit.

    In SINGLE_IMAGE_MODE wartet der Objektdetektor, bis das Begrenzungsfeld eines erkannten Objekts und (falls Sie die Klassifizierung aktiviert haben) das Kategorielabel verfügbar sind, bevor ein Ergebnis zurückgegeben wird. Die Erkennungslatenz ist daher möglicherweise höher. Außerdem sind Tracking-IDs in SINGLE_IMAGE_MODE nicht zugewiesen sind. Verwenden Sie diesen Modus, wenn die Latenz nicht kritisch ist und Sie Teilergebnisse zu finden sind.

    Mehrere Objekte erkennen und verfolgen false (Standard) | true

    Erkennung und Verfolgung von bis zu fünf Objekten oder nur den auffälliges Objekt (Standardeinstellung).

    Objekte klassifizieren false (Standard) | true

    Gibt an, ob erkannte Objekte in groben Kategorien klassifiziert werden sollen. Wenn diese Option aktiviert ist, klassifiziert die Objekterkennung Objekte in der folgenden Kategorien: Modeartikel, Lebensmittel, Haushaltswaren, Orte, Pflanzen und unbekannte Gebiete.

    Die Objekterkennungs- und Objekterkennungs-API ist für diese beiden Hauptzwecke optimiert. Cases:

    • Live-Erkennung und Verfolgung des auffälligsten Objekts in der Kamera Sucher
    • Erkennung mehrerer Objekte in einem statischen Bild

    So konfigurieren Sie die API für diese Anwendungsfälle:

    Java

    // Live detection and tracking
    FirebaseVisionObjectDetectorOptions options =
            new FirebaseVisionObjectDetectorOptions.Builder()
                    .setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE)
                    .enableClassification()  // Optional
                    .build();
    
    // Multiple object detection in static images
    FirebaseVisionObjectDetectorOptions options =
            new FirebaseVisionObjectDetectorOptions.Builder()
                    .setDetectorMode(FirebaseVisionObjectDetectorOptions.SINGLE_IMAGE_MODE)
                    .enableMultipleObjects()
                    .enableClassification()  // Optional
                    .build();
    

    Kotlin+KTX

    // Live detection and tracking
    val options = FirebaseVisionObjectDetectorOptions.Builder()
            .setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE)
            .enableClassification()  // Optional
            .build()
    
    // Multiple object detection in static images
    val options = FirebaseVisionObjectDetectorOptions.Builder()
            .setDetectorMode(FirebaseVisionObjectDetectorOptions.SINGLE_IMAGE_MODE)
            .enableMultipleObjects()
            .enableClassification()  // Optional
            .build()
    
  2. Rufen Sie eine Instanz von FirebaseVisionObjectDetector ab:

    Java

    FirebaseVisionObjectDetector objectDetector =
            FirebaseVision.getInstance().getOnDeviceObjectDetector();
    
    // Or, to change the default settings:
    FirebaseVisionObjectDetector objectDetector =
            FirebaseVision.getInstance().getOnDeviceObjectDetector(options);
    

    Kotlin+KTX

    val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector()
    
    // Or, to change the default settings:
    val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options)
    

2. Objektdetektor ausführen

Übergeben Sie Bilder an den FirebaseVisionObjectDetector, um Objekte zu erkennen und zu verfolgen Methode processImage() der Instanz.

Gehen Sie für jeden Frame eines Videos oder Bildes in einer Sequenz so vor:

  1. Erstellen Sie ein FirebaseVisionImage-Objekt aus Ihrem Bild.

    • Um ein FirebaseVisionImage-Objekt aus einem media.Image-Objekt, z. B. beim Aufnehmen eines Bildes von einem des Geräts an und übergib das media.Image-Objekt und die Rotation auf FirebaseVisionImage.fromMediaImage().

      Wenn Sie die Methode CameraX-Bibliothek, den OnImageCapturedListener und ImageAnalysis.Analyzer-Klassen berechnen den Rotationswert Sie müssen also nur die Rotation in eine der ML Kit-Modelle ROTATION_-Konstanten vor dem Aufruf FirebaseVisionImage.fromMediaImage():

      Java

      private class YourAnalyzer implements ImageAnalysis.Analyzer {
      
          private int degreesToFirebaseRotation(int degrees) {
              switch (degrees) {
                  case 0:
                      return FirebaseVisionImageMetadata.ROTATION_0;
                  case 90:
                      return FirebaseVisionImageMetadata.ROTATION_90;
                  case 180:
                      return FirebaseVisionImageMetadata.ROTATION_180;
                  case 270:
                      return FirebaseVisionImageMetadata.ROTATION_270;
                  default:
                      throw new IllegalArgumentException(
                              "Rotation must be 0, 90, 180, or 270.");
              }
          }
      
          @Override
          public void analyze(ImageProxy imageProxy, int degrees) {
              if (imageProxy == null || imageProxy.getImage() == null) {
                  return;
              }
              Image mediaImage = imageProxy.getImage();
              int rotation = degreesToFirebaseRotation(degrees);
              FirebaseVisionImage image =
                      FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
              // Pass image to an ML Kit Vision API
              // ...
          }
      }

      Kotlin+KTX

      private class YourImageAnalyzer : ImageAnalysis.Analyzer {
          private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) {
              0 -> FirebaseVisionImageMetadata.ROTATION_0
              90 -> FirebaseVisionImageMetadata.ROTATION_90
              180 -> FirebaseVisionImageMetadata.ROTATION_180
              270 -> FirebaseVisionImageMetadata.ROTATION_270
              else -> throw Exception("Rotation must be 0, 90, 180, or 270.")
          }
      
          override fun analyze(imageProxy: ImageProxy?, degrees: Int) {
              val mediaImage = imageProxy?.image
              val imageRotation = degreesToFirebaseRotation(degrees)
              if (mediaImage != null) {
                  val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation)
                  // Pass image to an ML Kit Vision API
                  // ...
              }
          }
      }

      Wenn Sie keine Kamerabibliothek verwenden, die Ihnen die Rotation des Bildes anzeigt, anhand der Drehung des Geräts und der Ausrichtung der Kamera Sensor im Gerät:

      Java

      private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      static {
          ORIENTATIONS.append(Surface.ROTATION_0, 90);
          ORIENTATIONS.append(Surface.ROTATION_90, 0);
          ORIENTATIONS.append(Surface.ROTATION_180, 270);
          ORIENTATIONS.append(Surface.ROTATION_270, 180);
      }
      
      /**
       * Get the angle by which an image must be rotated given the device's current
       * orientation.
       */
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      private int getRotationCompensation(String cameraId, Activity activity, Context context)
              throws CameraAccessException {
          // Get the device's current rotation relative to its "native" orientation.
          // Then, from the ORIENTATIONS table, look up the angle the image must be
          // rotated to compensate for the device's rotation.
          int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
          int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
          int sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION);
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          int result;
          switch (rotationCompensation) {
              case 0:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  break;
              case 90:
                  result = FirebaseVisionImageMetadata.ROTATION_90;
                  break;
              case 180:
                  result = FirebaseVisionImageMetadata.ROTATION_180;
                  break;
              case 270:
                  result = FirebaseVisionImageMetadata.ROTATION_270;
                  break;
              default:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  Log.e(TAG, "Bad rotation value: " + rotationCompensation);
          }
          return result;
      }

      Kotlin+KTX

      private val ORIENTATIONS = SparseIntArray()
      
      init {
          ORIENTATIONS.append(Surface.ROTATION_0, 90)
          ORIENTATIONS.append(Surface.ROTATION_90, 0)
          ORIENTATIONS.append(Surface.ROTATION_180, 270)
          ORIENTATIONS.append(Surface.ROTATION_270, 180)
      }
      /**
       * Get the angle by which an image must be rotated given the device's current
       * orientation.
       */
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      @Throws(CameraAccessException::class)
      private fun getRotationCompensation(cameraId: String, activity: Activity, context: Context): Int {
          // Get the device's current rotation relative to its "native" orientation.
          // Then, from the ORIENTATIONS table, look up the angle the image must be
          // rotated to compensate for the device's rotation.
          val deviceRotation = activity.windowManager.defaultDisplay.rotation
          var rotationCompensation = ORIENTATIONS.get(deviceRotation)
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
          val sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          val result: Int
          when (rotationCompensation) {
              0 -> result = FirebaseVisionImageMetadata.ROTATION_0
              90 -> result = FirebaseVisionImageMetadata.ROTATION_90
              180 -> result = FirebaseVisionImageMetadata.ROTATION_180
              270 -> result = FirebaseVisionImageMetadata.ROTATION_270
              else -> {
                  result = FirebaseVisionImageMetadata.ROTATION_0
                  Log.e(TAG, "Bad rotation value: $rotationCompensation")
              }
          }
          return result
      }

      Übergeben Sie dann das media.Image-Objekt und den Rotationswert auf FirebaseVisionImage.fromMediaImage():

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Um ein FirebaseVisionImage-Objekt aus einem Datei-URI zu erstellen, übergeben Sie App-Kontext und Datei-URI zu FirebaseVisionImage.fromFilePath(). Dies ist nützlich, wenn Sie Verwenden Sie den Intent ACTION_GET_CONTENT, um den Nutzer zur Auswahl aufzufordern ein Bild aus ihrer Galerie-App.

      Java

      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

      Kotlin+KTX

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }
    • Wenn Sie ein FirebaseVisionImage-Objekt aus einem ByteBuffer oder einem Byte-Array erstellen möchten, berechnen Sie zuerst die Bilddrehung wie oben für die media.Image-Eingabe beschrieben.

      Erstellen Sie dann ein FirebaseVisionImageMetadata-Objekt. die die Höhe, Breite, Farbcodierung, und Rotation:

      Java

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
              .setWidth(480)   // 480x360 is typically sufficient for
              .setHeight(360)  // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build();

      Kotlin+KTX

      val metadata = FirebaseVisionImageMetadata.Builder()
              .setWidth(480) // 480x360 is typically sufficient for
              .setHeight(360) // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build()

      Verwende den Puffer oder das Array und das Metadatenobjekt, um ein FirebaseVisionImage-Objekt zu erstellen:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Um ein FirebaseVisionImage-Objekt aus einem Bitmap-Objekt:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Das vom Bitmap-Objekt dargestellte Bild muss aufrecht sein und darf nicht zusätzlich gedreht werden.
  2. Übergeben Sie das Bild an die processImage()-Methode:

    Java

    objectDetector.processImage(image)
            .addOnSuccessListener(
                    new OnSuccessListener<List<FirebaseVisionObject>>() {
                        @Override
                        public void onSuccess(List<FirebaseVisionObject> detectedObjects) {
                            // Task completed successfully
                            // ...
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Task failed with an exception
                            // ...
                        }
                    });
    

    Kotlin+KTX

    objectDetector.processImage(image)
            .addOnSuccessListener { detectedObjects ->
                // Task completed successfully
                // ...
            }
            .addOnFailureListener { e ->
                // Task failed with an exception
                // ...
            }
    
  3. Wenn der Aufruf von processImage() erfolgreich ist, wird eine Liste von FirebaseVisionObjects angezeigt. an den Erfolgs-Listener übergeben wird.

    Jedes FirebaseVisionObject enthält die folgenden Attribute:

    Begrenzungsrahmen Eine Rect, die die Position des Objekts im Bild angibt.
    Tracking-ID Eine Ganzzahl, die das Objekt in Bildern identifiziert. Null in SINGLE_IMAGE_MODE
    Kategorie Die grobe Kategorie des Objekts. Wenn die Objekterkennung Klassifizierung aktiviert haben, ist dies immer FirebaseVisionObject.CATEGORY_UNKNOWN
    Zuverlässigkeit Der Konfidenzwert der Objektklassifizierung. Wenn die Klassifizierung für den Objekt-Detektor nicht aktiviert ist oder das Objekt als unbekannt klassifiziert wird, ist das null.

    Java

    // The list of detected objects contains one item if multiple object detection wasn't enabled.
    for (FirebaseVisionObject obj : detectedObjects) {
        Integer id = obj.getTrackingId();
        Rect bounds = obj.getBoundingBox();
    
        // If classification was enabled:
        int category = obj.getClassificationCategory();
        Float confidence = obj.getClassificationConfidence();
    }
    

    Kotlin+KTX

    // The list of detected objects contains one item if multiple object detection wasn't enabled.
    for (obj in detectedObjects) {
        val id = obj.trackingId       // A number that identifies the object across images
        val bounds = obj.boundingBox  // The object's position in the image
    
        // If classification was enabled:
        val category = obj.classificationCategory
        val confidence = obj.classificationConfidence
    }
    

Nutzerfreundlichkeit und Leistung verbessern

Beachten Sie für eine optimale Nutzererfahrung in Ihrer App die folgenden Richtlinien:

  • Die erfolgreiche Objekterkennung hängt von der visuellen Komplexität des Objekts ab. Objekte mit einer geringen Anzahl visueller Merkmale müssen möglicherweise einen größeren Teil des Bildes einnehmen, um erkannt zu werden. Sie sollten Nutzenden dabei helfen, die sich gut für die Art von Objekten eignet, die Sie erkennen möchten.
  • Wenn Sie bei der Klassifizierung Objekte erkennen möchten, die nicht fallen in die unterstützten Kategorien einzuordnen, spezielle Behandlungen für unbekannte Objekte.

Sehen Sie sich auch die [ML Kit Material Design Showcase-App][showcase-link]{: .external } und die Material Design Sammlung Muster für Funktionen, die auf maschinellem Lernen basieren

Wenn Sie den Streaming-Modus in einer Echtzeitanwendung verwenden, sollten Sie um die besten Frame-Rates zu erzielen:

  • Verwenden Sie im Streamingmodus nicht die Mehrfachobjekterkennung, da die meisten Geräte dies angemessene Framerates zu erzielen.

  • Deaktivieren Sie die Klassifizierung, wenn Sie sie nicht benötigen.

  • Drosselung von Aufrufen an den Detektor. Wenn ein neuer Videoframe wenn der Detektor ausgeführt wird, lassen Sie den Frame weg.
  • Wenn Sie die Ausgabe des Detektors verwenden, um Grafiken Eingabebild, rufen Sie zuerst das Ergebnis aus ML Kit ab und rendern Sie das Bild in einem Schritt übereinanderlegen. Dadurch rendern Sie auf der Anzeigeoberfläche für jeden Eingabe-Frame nur einmal.
  • Wenn Sie die Camera2 API verwenden, nehmen Sie Bilder in ImageFormat.YUV_420_888-Format.

    Wenn Sie die ältere Camera API verwenden, nehmen Sie Bilder in ImageFormat.NV21-Format.