Bilder mit einem mit AutoML trainierten Modell unter Android mit Labels versehen

Nachdem Sie Ihr eigenes Modell mit AutoML Vision Edge trainiert haben , können Sie es in Ihrer App zum Beschriften von Bildern verwenden.

Bevor Sie beginnen

  1. Falls noch nicht geschehen, fügen Sie Firebase zu Ihrem Android-Projekt hinzu .
  2. Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken zu Ihrer Modul-Gradle-Datei (auf App-Ebene) hinzu (normalerweise 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-automl:18.0.5'
    }
    

1. Laden Sie das Modell

ML Kit führt Ihre AutoML-generierten Modelle auf dem Gerät aus. Sie können ML Kit jedoch so konfigurieren, dass Ihr Modell entweder remote von Firebase, vom lokalen Speicher oder von beiden geladen wird.

Durch das Hosten des Modells auf Firebase können Sie das Modell aktualisieren, ohne eine neue App-Version zu veröffentlichen, und Sie können Remote Config und A/B-Tests verwenden, um verschiedene Modelle dynamisch für verschiedene Benutzergruppen bereitzustellen.

Wenn Sie sich dafür entscheiden, das Modell nur durch Hosten mit Firebase bereitzustellen und es nicht mit Ihrer App zu bündeln, können Sie die anfängliche Downloadgröße Ihrer App reduzieren. Beachten Sie jedoch, dass, wenn das Modell nicht mit Ihrer App gebündelt ist, alle modellbezogenen Funktionen erst verfügbar sind, wenn Ihre App das Modell zum ersten Mal herunterlädt.

Durch die Bündelung Ihres Modells mit Ihrer App können Sie sicherstellen, dass die ML-Funktionen Ihrer App auch dann funktionieren, wenn das von Firebase gehostete Modell nicht verfügbar ist.

Konfigurieren Sie eine von Firebase gehostete Modellquelle

Um das remote gehostete Modell zu verwenden, erstellen Sie ein FirebaseAutoMLRemoteModel Objekt und geben Sie dabei den Namen an, den Sie dem Modell beim Veröffentlichen zugewiesen haben:

Java

// Specify the name you assigned in the Firebase console.
FirebaseAutoMLRemoteModel remoteModel =
    new FirebaseAutoMLRemoteModel.Builder("your_remote_model").build();

Kotlin+KTX

// Specify the name you assigned in the Firebase console.
val remoteModel = FirebaseAutoMLRemoteModel.Builder("your_remote_model").build()

Starten Sie dann die Aufgabe zum Herunterladen des Modells und geben Sie die Bedingungen an, unter denen Sie den Download zulassen möchten. Wenn sich das Modell nicht auf dem Gerät befindet oder eine neuere Version des Modells verfügbar ist, lädt die Aufgabe das Modell asynchron von Firebase herunter:

Java

FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
        .requireWifi()
        .build();
FirebaseModelManager.getInstance().download(remoteModel, conditions)
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Success.
            }
        });

Kotlin+KTX

val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

Viele Apps starten die Download-Aufgabe in ihrem Initialisierungscode, Sie können dies jedoch jederzeit tun, bevor Sie das Modell verwenden müssen.

Konfigurieren Sie eine lokale Modellquelle

So bündeln Sie das Modell mit Ihrer App:

  1. Extrahieren Sie das Modell und seine Metadaten aus dem ZIP-Archiv, das Sie von der Firebase-Konsole heruntergeladen haben. Wir empfehlen Ihnen, die Dateien so zu verwenden, wie Sie sie heruntergeladen haben, ohne Änderungen (einschließlich der Dateinamen).
  2. Fügen Sie Ihr Modell und seine Metadatendateien in Ihr App-Paket ein:

    1. Wenn Ihr Projekt keinen Ordner „Assets“ enthält, erstellen Sie einen, indem Sie mit der rechten Maustaste auf den Ordner app/ klicken und dann auf „Neu > Ordner > Ordner „Assets““ klicken.
    2. Erstellen Sie unter dem Assets-Ordner einen Unterordner für die Modelldateien.
    3. Kopieren Sie die Dateien model.tflite , dict.txt und manifest.json in den Unterordner (alle drei Dateien müssen sich im selben Ordner befinden).
  3. Fügen Sie der build.gradle Datei Ihrer App Folgendes hinzu, um sicherzustellen, dass Gradle die Modelldatei beim Erstellen der App nicht komprimiert:
    android {
        // ...
        aaptOptions {
            noCompress "tflite"
        }
    }
    
    Die Modelldatei wird im App-Paket enthalten und steht ML Kit als Rohobjekt zur Verfügung.
  4. Erstellen Sie ein FirebaseAutoMLLocalModel Objekt und geben Sie den Pfad zur Modellmanifestdatei an:

    Java

    FirebaseAutoMLLocalModel localModel = new FirebaseAutoMLLocalModel.Builder()
            .setAssetFilePath("manifest.json")
            .build();
    

    Kotlin+KTX

    val localModel = FirebaseAutoMLLocalModel.Builder()
            .setAssetFilePath("manifest.json")
            .build()
    

Erstellen Sie aus Ihrem Modell einen Bildbeschrifter

Nachdem Sie Ihre Modellquellen konfiguriert haben, erstellen Sie aus einer davon ein FirebaseVisionImageLabeler Objekt.

Wenn Sie nur über ein lokal gebündeltes Modell verfügen, erstellen Sie einfach einen Labeler aus Ihrem FirebaseAutoMLLocalModel Objekt und konfigurieren Sie den Schwellenwert für die Konfidenzbewertung, den Sie benötigen möchten (siehe Bewerten Sie Ihr Modell ):

Java

FirebaseVisionImageLabeler labeler;
try {
    FirebaseVisionOnDeviceAutoMLImageLabelerOptions options =
            new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
                    .setConfidenceThreshold(0.0f)  // Evaluate your model in the Firebase console
                                                   // to determine an appropriate value.
                    .build();
    labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options);
} catch (FirebaseMLException e) {
    // ...
}

Kotlin+KTX

val options = FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
    .setConfidenceThreshold(0)  // Evaluate your model in the Firebase console
                                // to determine an appropriate value.
    .build()
val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options)

Wenn Sie über ein remote gehostetes Modell verfügen, müssen Sie überprüfen, ob es heruntergeladen wurde, bevor Sie es ausführen. Sie können den Status der Modell-Download-Aufgabe mit der Methode isModelDownloaded() des Modellmanagers überprüfen.

Obwohl Sie dies nur bestätigen müssen, bevor Sie den Labeler ausführen, kann es sinnvoll sein, diese Prüfung bei der Instanziierung des Bild-Labelers durchzuführen, wenn Sie sowohl über ein remote gehostetes Modell als auch über ein lokal gebündeltes Modell verfügen: Erstellen Sie einen Labeler aus dem Remote-Modell, wenn Es wurde heruntergeladen, andernfalls vom lokalen Modell.

Java

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
                FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder optionsBuilder;
                if (isDownloaded) {
                    optionsBuilder = new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(remoteModel);
                } else {
                    optionsBuilder = new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel);
                }
                FirebaseVisionOnDeviceAutoMLImageLabelerOptions options = optionsBuilder
                        .setConfidenceThreshold(0.0f)  // Evaluate your model in the Firebase console
                                                       // to determine an appropriate threshold.
                        .build();

                FirebaseVisionImageLabeler labeler;
                try {
                    labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options);
                } catch (FirebaseMLException e) {
                    // Error.
                }
            }
        });

Kotlin+KTX

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded -> 
    val optionsBuilder =
        if (isDownloaded) {
            FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(remoteModel)
        } else {
            FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
        }
    // Evaluate your model in the Firebase console to determine an appropriate threshold.
    val options = optionsBuilder.setConfidenceThreshold(0.0f).build()
    val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options)
}

Wenn Sie nur über ein remote gehostetes Modell verfügen, sollten Sie modellbezogene Funktionen deaktivieren (z. B. einen Teil Ihrer Benutzeroberfläche ausgrauen oder ausblenden), bis Sie bestätigen, dass das Modell heruntergeladen wurde. Sie können dies tun, indem Sie einen Listener an download() Methode des Modellmanagers anhängen:

Java

FirebaseModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void v) {
              // Download complete. Depending on your app, you could enable
              // the ML feature, or switch from the local model to the remote
              // model, etc.
            }
        });

Kotlin+KTX

FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

2. Bereiten Sie das Eingabebild vor

Erstellen Sie dann für jedes Bild, das Sie beschriften möchten, ein FirebaseVisionImage Objekt mit einer der in diesem Abschnitt beschriebenen Optionen und übergeben Sie es an eine Instanz von FirebaseVisionImageLabeler (im nächsten Abschnitt beschrieben).

Sie können ein FirebaseVisionImage Objekt aus einem media.Image Objekt, einer Datei auf dem Gerät, einem Byte-Array oder einem Bitmap Objekt erstellen:

  • Um ein FirebaseVisionImage Objekt aus einem media.Image Objekt zu erstellen, beispielsweise beim Aufnehmen eines Bildes von der Kamera eines Geräts, übergeben Sie das media.Image Objekt und die Drehung des Bildes an FirebaseVisionImage.fromMediaImage() .

    Wenn Sie die CameraX- Bibliothek verwenden, berechnen die Klassen OnImageCapturedListener und ImageAnalysis.Analyzer den Rotationswert für Sie, Sie müssen also nur die Rotation in eine der ROTATION_ Konstanten von ML Kit konvertieren, bevor Sie FirebaseVisionImage.fromMediaImage() aufrufen:

    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 Drehung des Bildes liefert, können Sie diese aus der Drehung des Geräts und der Ausrichtung des Kamerasensors im Gerät berechnen:

    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 an 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 den App-Kontext und den Datei-URI an FirebaseVisionImage.fromFilePath() . Dies ist nützlich, wenn Sie eine ACTION_GET_CONTENT Absicht verwenden, um den Benutzer aufzufordern, ein Bild aus seiner Galerie-App auszuwählen.

    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()
    }
  • Um ein FirebaseVisionImage Objekt aus einem ByteBuffer oder einem Byte-Array zu erstellen, berechnen Sie zunächst die Bildrotation wie oben für media.Image Eingabe beschrieben.

    Erstellen Sie dann ein FirebaseVisionImageMetadata -Objekt, das die Höhe, Breite, das Farbkodierungsformat und die Drehung des Bildes enthält:

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

    Verwenden Sie 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)
  • So erstellen Sie ein FirebaseVisionImage Objekt aus einem Bitmap Objekt:

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

    Kotlin+KTX

    val image = FirebaseVisionImage.fromBitmap(bitmap)
    Das durch das Bitmap Objekt dargestellte Bild muss aufrecht stehen, ohne dass eine zusätzliche Drehung erforderlich ist.

3. Führen Sie den Bildbeschrifter aus

Um Objekte in einem Bild zu beschriften, übergeben Sie das FirebaseVisionImage Objekt an die Methode processImage() von FirebaseVisionImageLabeler .

Java

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

Kotlin+KTX

labeler.processImage(image)
        .addOnSuccessListener { labels ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

Wenn die Bildbeschriftung erfolgreich ist, wird ein Array von FirebaseVisionImageLabel Objekten an den Erfolgslistener übergeben. Von jedem Objekt können Sie Informationen über ein im Bild erkanntes Merkmal erhalten.

Zum Beispiel:

Java

for (FirebaseVisionImageLabel label: labels) {
    String text = label.getText();
    float confidence = label.getConfidence();
}

Kotlin+KTX

for (label in labels) {
    val text = label.text
    val confidence = label.confidence
}

Tipps zur Verbesserung der Echtzeitleistung

  • Gasrufe an den Detektor. Wenn ein neues Videobild verfügbar wird, während der Detektor läuft, löschen Sie das Bild.
  • Wenn Sie die Ausgabe des Detektors verwenden, um Grafiken auf dem Eingabebild zu überlagern, rufen Sie zunächst das Ergebnis vom ML Kit ab und rendern Sie dann das Bild und überlagern Sie es in einem einzigen Schritt. Auf diese Weise rendern Sie für jeden Eingaberahmen nur einmal auf der Anzeigeoberfläche.
  • Wenn Sie die Camera2-API verwenden, erfassen Sie Bilder im ImageFormat.YUV_420_888 Format.

    Wenn Sie die ältere Kamera-API verwenden, erfassen Sie Bilder im ImageFormat.NV21 Format.