Bildanalyse

Beim Anwendungsfall Bildanalyse erhält Ihre Anwendung ein über die CPU zugängliches Bild, auf dem Sie Bildverarbeitung, maschinelles Sehen oder Inferenzen durch maschinelles Lernen ausführen können. Die Anwendung implementiert die Methode analyze(), die für jeden Frame ausgeführt wird.

Informationen zum Einbinden des ML Kits von Google in die CameraX-App finden Sie unter ML Kit Analyzer.

Betriebsmodi

Wenn die Analysepipeline der Anwendung nicht mit den Framerate-Anforderungen von CameraX mithalten kann, kann CameraX so konfiguriert werden, dass Frames auf eine der folgenden Arten entfernt werden:

  • Nicht blockierend (Standardeinstellung): In diesem Modus speichert der Executor immer das neueste Image in einem Image-Zwischenspeicher (ähnlich einer Warteschlange mit einer Tiefe von eins), während die Anwendung das vorherige Image analysiert. Wenn CameraX ein neues Bild empfängt, bevor die Anwendung die Verarbeitung abgeschlossen hat, wird das neue Bild in demselben Zwischenspeicher gespeichert und das vorherige Bild überschrieben. Beachten Sie, dass ImageAnalysis.Builder.setImageQueueDepth() in diesem Szenario keine Auswirkungen hat und der Zwischenspeicherinhalt immer überschrieben wird. Sie können diesen nicht blockierenden Modus aktivieren, indem Sie setBackpressureStrategy() mit STRATEGY_KEEP_ONLY_LATEST aufrufen. Weitere Informationen zu Auswirkungen des Executors finden Sie in der Referenzdokumentation zu STRATEGY_KEEP_ONLY_LATEST.

  • blocking: In diesem Modus kann der interne Executor der internen Image-Warteschlange mehrere Images hinzufügen und beginnt erst dann mit dem Löschen von Frames, wenn die Warteschlange voll ist. Die Blockierung betrifft die gesamte Kamerageräte: Wenn das Kameragerät mehrere gebundene Anwendungsfälle hat, werden alle diese Anwendungsfälle blockiert, während CameraX diese Bilder verarbeitet. Wenn beispielsweise sowohl die Vorschau als auch die Bildanalyse an ein Kameragerät gebunden sind, wird die Vorschau ebenfalls blockiert, während CameraX die Bilder verarbeitet. Sie können den Blockierungsmodus aktivieren, indem Sie STRATEGY_BLOCK_PRODUCER an setBackpressureStrategy() übergeben. Sie können die Tiefe der Bildwarteschlange auch mithilfe von ImageAnalysis.Builder.setImageQueueDepth() konfigurieren.

Mit einem Analysetool für niedrige Latenz und hoher Leistung, bei dem die Gesamtzeit für die Analyse eines Bildes kürzer als die Dauer eines CameraX-Frames (z. B. 16 ms für 60 fps) ist, sorgen beide Betriebsmodi für ein reibungsloses Gesamterlebnis. Der Blockiermodus kann in einigen Szenarien weiterhin hilfreich sein, z. B. beim Umgang mit sehr kurzen Systemjitter.

Bei einer hohen Latenz und einem Hochleistungs-Analysetool ist der Blockierungsmodus mit einer längeren Warteschlange erforderlich, um die Latenz auszugleichen. Die Anwendung kann jedoch weiterhin alle Frames verarbeiten.

Mit einer hohen Latenz und einem zeitaufwendigen Analysetool (das Analysetool kann nicht alle Frames verarbeiten), ist ein nicht blockierender Modus möglicherweise die bessere Wahl, da Frames für den Analysepfad entfernt werden müssen, aber bei anderen gleichzeitig gebundenen Anwendungsfällen weiterhin alle Frames angezeigt werden können.

Implementierung

So verwenden Sie die Bildanalyse in Ihrer Anwendung:

Sofort nach der Bindung sendet CameraX Bilder an Ihr registriertes Analysegerät. Rufen Sie nach Abschluss der Analyse ImageAnalysis.clearAnalyzer() auf oder heben Sie die Verknüpfung mit dem Anwendungsfall ImageAnalysis auf, um die Analyse zu beenden.

Anwendungsfall „ImageAnalysis erstellen“

ImageAnalysis verbindet das Analysetool (ein Bildnutzer) mit CameraX, einem Bildersteller. Anwendungen können ImageAnalysis.Builder verwenden, um ein ImageAnalysis-Objekt zu erstellen. Mit dem ImageAnalysis.Builder kann die Anwendung Folgendes konfigurieren:

In Anwendungen kann entweder die Auflösung oder das Seitenverhältnis festgelegt werden, aber nicht beides. Die genaue Ausgabeauflösung hängt von der von der Anwendung angeforderten Größe (oder Seitenverhältnis) und den Hardwarefunktionen ab und kann von der angeforderten Größe oder vom angeforderten Seitenverhältnis abweichen. Informationen zum Algorithmus zum Auflösungsabgleich finden Sie in der Dokumentation zu setTargetResolution().

In einer Anwendung können die Ausgabebildpixel als YUV- (Standard) oder RGBA-Farbräume konfiguriert werden. Beim Festlegen eines RGBA-Ausgabeformats wandelt CameraX die Bilder intern vom YUV in den RGBA-Farbraum um und packt Bildbits in der ByteBuffer der ersten Ebene von ImageProxy (die anderen beiden Ebenen werden nicht verwendet) mit der folgenden Reihenfolge:

ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...

Bei komplizierten Bildanalysen, bei denen das Gerät die Framerate nicht mithalten kann, können Sie CameraX so konfigurieren, dass Frames mit den im Abschnitt Betriebsmodi dieses Themas beschriebenen Strategien ausgelassen werden.

Analysetool erstellen

Anwendungen können Analysetools erstellen, indem die ImageAnalysis.Analyzer-Schnittstelle implementiert und analyze(ImageProxy image) überschrieben wird. In jedem Analyzer erhalten die Anwendungen ein ImageProxy, das ein Wrapper für Media.Image ist. Das Bildformat kann mit ImageProxy.getFormat() abgefragt werden. Das Format ist einer der folgenden Werte, die die Anwendung ImageAnalysis.Builder bereitstellt:

  • ImageFormat.RGBA_8888, wenn die App OUTPUT_IMAGE_FORMAT_RGBA_8888 angefordert hat.
  • ImageFormat.YUV_420_888, wenn die App OUTPUT_IMAGE_FORMAT_YUV_420_888 angefordert hat.

Informationen zu Farbraumkonfigurationen und zum Abrufen der Pixelbyte finden Sie unter Anwendungsfall "ImageAnalysis erstellen".

In einem Analysetool sollte die Anwendung folgende Aktionen ausführen:

  1. Analysiere einen bestimmten Frame so schnell wie möglich, am besten innerhalb des vorgegebenen Framerate-Zeitlimits (z. B. weniger als 32 ms bei 30 fps). Wenn die Anwendung einen Frame nicht schnell genug analysieren kann, sollten Sie einen der unterstützten Verfahren zum Löschen von Frames in Betracht ziehen.
  2. Lassen Sie ImageProxy durch Aufrufen von ImageProxy.close() in CameraX los. Die Schließen-Funktion (Media.Image.close()) des umschlossenen Media.Image sollte nicht aufgerufen werden.

Anwendungen können das umschlossene Media.Image direkt in ImageProxy verwenden. Rufen Sie nur nicht Media.Image.close() für das umschlossene Bild auf, da dies den Bildfreigabemechanismus in CameraX beeinträchtigen würde. Verwenden Sie stattdessen ImageProxy.close(), um die zugrunde liegende Media.Image für CameraX freizugeben.

Analysetool für ImageAnalysis konfigurieren

Nachdem Sie ein Analysetool erstellt haben, können Sie es mit ImageAnalysis.setAnalyzer() registrieren und mit der Analyse beginnen. Wenn Sie mit der Analyse fertig sind, verwenden Sie ImageAnalysis.clearAnalyzer(), um das registrierte Analysetool zu entfernen.

Für die Bildanalyse kann nur ein aktives Analysetool konfiguriert werden. Durch das Aufrufen von ImageAnalysis.setAnalyzer() wird das registrierte Analyse-Tool ersetzt, falls es bereits vorhanden ist. Anwendungen können vor oder nach der Bindung des Anwendungsfalls jederzeit ein neues Analysetool festlegen.

ImageAnalysis an einen Lebenszyklus binden

Es wird dringend empfohlen, den ImageAnalysis mit der Funktion ProcessCameraProvider.bindToLifecycle() an einen vorhandenen AndroidX-Lebenszyklus zu binden. Die Funktion bindToLifecycle() gibt das ausgewählte Camera-Gerät zurück, mit dem erweiterte Einstellungen wie die Belichtung und andere Elemente abgestimmt werden können. Weitere Informationen zur Steuerung der Kameraausgabe findest du in diesem Leitfaden.

Im folgenden Beispiel werden alle vorherigen Schritte kombiniert, um Anwendungsfälle für CameraX ImageAnalysis und Preview an einen lifeCycle-Inhaber zu binden:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    // enable the following line if RGBA output is needed.
    // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
    .setTargetResolution(Size(1280, 720))
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()
imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy ->
    val rotationDegrees = imageProxy.imageInfo.rotationDegrees
    // insert your code here.
    ...
    // after done, release the ImageProxy object
    imageProxy.close()
})

cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)

Java

ImageAnalysis imageAnalysis =
    new ImageAnalysis.Builder()
        // enable the following line if RGBA output is needed.
        //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
        .setTargetResolution(new Size(1280, 720))
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .build();

imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
    @Override
    public void analyze(@NonNull ImageProxy imageProxy) {
        int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
            // insert your code here.
            ...
            // after done, release the ImageProxy object
            imageProxy.close();
        }
    });

cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);

Weitere Informationen

Weitere Informationen zu CameraX finden Sie in den folgenden zusätzlichen Ressourcen.

Codelab

  • Erste Schritte mit CameraX
  • Codebeispiel

  • CameraX-Beispiel-Apps