Konfigurationsoptionen

Sie konfigurieren jeden CameraX-Anwendungsfall so, dass unterschiedliche Aspekte der Vorgänge des Anwendungsfalls gesteuert werden.

Im Anwendungsfall der Bilderfassung können Sie beispielsweise ein Zielseitenverhältnis und einen Blitzmodus festlegen. Der folgende Code zeigt ein Beispiel:

Kotlin

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Java

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

Zusätzlich zu den Konfigurationsoptionen stellen einige Anwendungsfälle APIs zur Verfügung, um Einstellungen dynamisch zu ändern, nachdem der Anwendungsfall erstellt wurde. Informationen zur Konfiguration, die für die einzelnen Anwendungsfälle spezifisch ist, finden Sie unter Vorschau implementieren, Bilder analysieren und Bildaufnahme.

KameraXConfig

Der Einfachheit halber verfügt CameraX über Standardkonfigurationen wie interne Executors und Handler, die für die meisten Nutzungsszenarien geeignet sind. Wenn Ihre Anwendung jedoch besondere Anforderungen hat oder es vorzieht, diese Konfigurationen anzupassen, ist CameraXConfig die Schnittstelle für diesen Zweck.

Mit CameraXConfig kann eine Anwendung Folgendes tun:

Nutzungsmodell

Im Folgenden wird die Verwendung von CameraXConfig beschrieben:

  1. Erstellen Sie ein CameraXConfig-Objekt mit Ihren benutzerdefinierten Konfigurationen.
  2. Implementieren Sie die CameraXConfig.Provider-Oberfläche in der Application und geben Sie das CameraXConfig-Objekt in getCameraXConfig() zurück.
  3. Fügen Sie der Datei AndroidManifest.xml die Klasse Application hinzu, wie hier beschrieben.

Im folgenden Codebeispiel wird die CameraX-Protokollierung auf Fehlermeldungen beschränkt:

Kotlin

class CameraApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
           .setMinimumLoggingLevel(Log.ERROR).build()
   }
}

Bewahren Sie eine lokale Kopie des CameraXConfig-Objekts auf, wenn Ihre Anwendung nach dem Festlegen die CameraX-Konfiguration kennen muss.

Kamerabegrenzer

Beim ersten Aufruf von ProcessCameraProvider.getInstance() listet CameraX die Eigenschaften der auf dem Gerät verfügbaren Kameras auf und fragt sie ab. Da CameraX mit den Hardwarekomponenten kommunizieren muss, kann dieser Prozess für jede Kamera sehr viel Zeit in Anspruch nehmen, insbesondere bei Low-End-Geräten. Wenn Ihre Anwendung nur bestimmte Kameras auf dem Gerät verwendet, z. B. die Standard-Frontkamera, können Sie CameraX so einstellen, dass andere Kameras ignoriert werden. Dadurch kann die Startlatenz für die von Ihrer Anwendung verwendeten Kameras reduziert werden.

Wenn das an CameraXConfig.Builder.setAvailableCamerasLimiter() übergebene CameraSelector eine Kamera ausfiltert, verhält sich CameraX so, als wäre diese Kamera nicht vorhanden. Beispielsweise beschränkt der folgende Code die Anwendung auf die Verwendung der Standard-Rückkamera des Geräts:

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

Unterhaltungen

Viele der Plattform-APIs, auf denen CameraX aufgebaut ist, erfordern eine Blockierung der Interprocess Communication (IPC) mit Hardware, deren Reaktion manchmal Hunderte von Millisekunden dauern kann. Aus diesem Grund ruft CameraX diese APIs nur aus Hintergrundthreads auf, sodass der Hauptthread nicht blockiert wird und die Benutzeroberfläche fließend bleibt. CameraX verwaltet diese Hintergrundthreads intern, sodass dieses Verhalten transparent erscheint. Einige Anwendungen erfordern jedoch eine strenge Kontrolle der Threads. Mit CameraXConfig kann eine Anwendung die Hintergrundthreads festlegen, die über CameraXConfig.Builder.setCameraExecutor() und CameraXConfig.Builder.setSchedulerHandler() verwendet werden.

Kamera-Executor

Das Kamera-Executor wird für alle internen API-Aufrufe der Kameraplattform sowie für Callbacks von diesen APIs verwendet. CameraX weist einen internen Executor zu und verwaltet ihn, um diese Aufgaben auszuführen. Wenn Ihre Anwendung jedoch eine strengere Kontrolle von Threads erfordert, verwenden Sie CameraXConfig.Builder.setCameraExecutor().

Planer-Handler

Der Planer-Handler wird verwendet, um interne Aufgaben in festen Intervallen zu planen. Beispielsweise wird versucht, die Kamera zu öffnen, wenn sie nicht verfügbar ist. Dieser Handler führt keine Jobs aus und leitet sie nur an den Kamera-Executor weiter. Es wird manchmal auch auf den Legacy-API-Plattformen verwendet, die ein Handler für Callbacks benötigen. In diesen Fällen werden die Callbacks weiterhin nur direkt an den Kamera-Executor gesendet. CameraX weist einen internen HandlerThread zu und verwaltet ihn, um diese Aufgaben auszuführen. Sie können ihn jedoch mit CameraXConfig.Builder.setSchedulerHandler() überschreiben.

Protokollierung

Mit dem CameraX-Logging können Anwendungen Logcat-Nachrichten filtern. Es kann also sinnvoll sein, ausführliche Meldungen in Ihrem Produktionscode zu vermeiden. CameraX unterstützt vier Protokollierungsebenen, von der ausführlichsten bis zur schwerwiegendsten:

  • Log.DEBUG (Standard)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Ausführliche Beschreibungen dieser Logebenen finden Sie in der Android Log-Dokumentation. Verwenden Sie CameraXConfig.Builder.setMinimumLoggingLevel(int), um die passende Logging-Ebene für Ihre Anwendung festzulegen.

Automatische Auswahl

CameraX bietet automatisch Funktionen, die für das Gerät, auf dem Ihre App ausgeführt wird, spezifisch sind. CameraX ermittelt beispielsweise automatisch die beste Auflösung, wenn Sie keine Auflösung angeben oder die angegebene Auflösung nicht unterstützt wird. All dies wird von der Bibliothek erledigt, sodass Sie keinen gerätespezifischen Code mehr schreiben müssen.

Das Ziel von CameraX ist es, eine Kamerasitzung erfolgreich zu starten. Das bedeutet, dass CameraX je nach Gerätefunktion Kompromisse bei Auflösung und Seitenverhältnissen eingeht. Gründe für einen Kompromiss:

  • Das Gerät unterstützt die angeforderte Auflösung nicht.
  • Das Gerät hat Kompatibilitätsprobleme, z. B. Legacy-Geräte, die bestimmte Auflösungen erfordern, um korrekt zu funktionieren.
  • Auf einigen Geräten sind bestimmte Formate nur bei bestimmten Seitenverhältnissen verfügbar.
  • Das Gerät bevorzugt einen „nächstgelegenen mod16“ für JPEG- oder Videocodierung. Weitere Informationen findest du unter SCALER_STREAM_CONFIGURATION_MAP.

Obwohl CameraX die Sitzung erstellt und verwaltet, prüfen Sie immer die zurückgegebenen Bildgrößen in der Ausgabe des Anwendungsfalls in Ihrem Code und passen Sie sie entsprechend an.

Drehung

Standardmäßig ist die Kameradrehung so eingestellt, dass sie der Drehung des Standarddisplays beim Erstellen des Anwendungsfalls entspricht. In diesem Standardfall erzeugt CameraX Ausgaben, damit die App mit dem Ergebnis der Vorschau übereinstimmt. Sie können die Rotation in einen benutzerdefinierten Wert ändern, um Geräte mit mehreren Displays zu unterstützen. Dazu übergeben Sie die aktuelle Bildschirmausrichtung beim Konfigurieren von Anwendungsfallobjekten oder dynamisch, nachdem sie erstellt wurden.

Ihre App kann die Zielrotation über die Konfigurationseinstellungen festlegen. Anschließend können die Rotationseinstellungen mit den Methoden aus den APIs des Anwendungsfalls (z. B. ImageAnalysis.setTargetRotation()) aktualisiert werden, auch wenn der Lebenszyklus ausgeführt wird. Sie können diese Option verwenden, wenn die Anwendung auf das Hochformat festgelegt ist und somit keine Neukonfiguration beim Drehen erfolgt. Für den Foto- oder Analyseanwendungsfall muss jedoch die aktuelle Drehung des Geräts berücksichtigt werden. Zum Beispiel kann eine Rotationserkennung erforderlich sein, damit Gesichter für die Gesichtserkennung richtig ausgerichtet werden, oder um Fotos auf Quer- oder Hochformat einzustellen.

Daten für erfasste Bilder können ohne Rotationsinformationen gespeichert werden. EXIF-Daten enthalten Rotationsinformationen, damit Galerieanwendungen das Bild nach dem Speichern in der richtigen Ausrichtung anzeigen können.

Damit Vorschaudaten mit der richtigen Ausrichtung angezeigt werden, können Sie mit der Metadatenausgabe von Preview.PreviewOutput() Transformationen erstellen.

Das folgende Codebeispiel zeigt, wie die Rotation für ein Ausrichtungsereignis festgelegt wird:

Kotlin

override fun onCreate() {
    val imageCapture = ImageCapture.Builder().build()

    val orientationEventListener = object : OrientationEventListener(this as Context) {
        override fun onOrientationChanged(orientation : Int) {
            // Monitors orientation values to determine the target rotation value
            val rotation : Int = when (orientation) {
                in 45..134 -> Surface.ROTATION_270
                in 135..224 -> Surface.ROTATION_180
                in 225..314 -> Surface.ROTATION_90
                else -> Surface.ROTATION_0
            }

            imageCapture.targetRotation = rotation
        }
    }
    orientationEventListener.enable()
}

Java

@Override
public void onCreate() {
    ImageCapture imageCapture = new ImageCapture.Builder().build();

    OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
       @Override
       public void onOrientationChanged(int orientation) {
           int rotation;

           // Monitors orientation values to determine the target rotation value
           if (orientation >= 45 && orientation < 135) {
               rotation = Surface.ROTATION_270;
           } else if (orientation >= 135 && orientation < 225) {
               rotation = Surface.ROTATION_180;
           } else if (orientation >= 225 && orientation < 315) {
               rotation = Surface.ROTATION_90;
           } else {
               rotation = Surface.ROTATION_0;
           }

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

Basierend auf der festgelegten Drehung werden in jedem Anwendungsfall die Bilddaten entweder direkt rotiert oder den Nutzern der nicht gedrehten Bilddaten Rotationsmetadaten bereitgestellt.

  • Vorschau: Die Metadatenausgabe wird bereitgestellt, damit die Rotation der Zielauflösung mithilfe von Preview.getTargetRotation() bekannt ist.
  • ImageAnalysis: Die Metadatenausgabe erfolgt so, dass die Koordinaten der Bildzwischenspeicherung relativ zu den Anzeigekoordinaten sind.
  • ImageCapture: Die EXIF-Metadaten, der Zwischenspeicher oder sowohl der Zwischenspeicher als auch die Metadaten werden entsprechend der Rotationseinstellung geändert. Der geänderte Wert hängt von der HAL-Implementierung ab.

Rechteck zuschneiden

Standardmäßig entspricht das Zuschneiderechteck dem vollständigen Pufferrecht. Sie können es mit ViewPort und UseCaseGroup anpassen. Durch das Gruppieren von Anwendungsfällen und das Festlegen des Darstellungsbereichs stellt CameraX sicher, dass die Zuschnittaufzeichnungen aller Anwendungsfälle in der Gruppe auf denselben Bereich im Kamerasensor verweisen.

Das folgende Code-Snippet zeigt, wie diese beiden Klassen verwendet werden:

Kotlin

val viewPort =  ViewPort.Builder(Rational(width, height), display.rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

Java

ViewPort viewPort = new ViewPort.Builder(
         new Rational(width, height),
         getDisplay().getRotation()).build();
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build();
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);

ViewPort definiert das für Endnutzer sichtbare Zwischenspeicherrechteck. Anschließend berechnet CameraX anhand der Eigenschaften des Darstellungsbereichs und der damit verbundenen Anwendungsfälle das größtmögliche Zuschnittrechteck. Um einen WYSIWYG-Effekt zu erzielen, können Sie den Darstellungsbereich normalerweise basierend auf dem Anwendungsfall der Vorschau konfigurieren. Eine einfache Möglichkeit zum Abrufen des Darstellungsbereichs ist die Verwendung von PreviewView.

Die folgenden Code-Snippets zeigen, wie das ViewPort-Objekt abgerufen wird:

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

Im vorherigen Beispiel stimmen die Inhalte der Anwendung von ImageAnalysis und ImageCapture mit dem überein, was der Endnutzer in PreviewView sieht, vorausgesetzt, der Skalierungstyp von PreviewView ist auf den Standardwert FILL_CENTER gesetzt. Nachdem das Zuschneiderecht und die Drehung auf den Ausgabezwischenspeicher angewendet wurden, ist das Bild aus allen Anwendungsfällen gleich, wenn auch mit unterschiedlichen Auflösungen. Weitere Informationen zum Anwenden der Transformationsinformationen finden Sie unter Ausgabe transformieren.

Kameraauswahl

CameraX wählt automatisch das beste Kameragerät für die Anforderungen und Anwendungsfälle Ihrer Anwendung aus. Wenn du ein anderes Gerät als das für dich ausgewählte verwenden möchtest, hast du mehrere Möglichkeiten:

Das folgende Codebeispiel zeigt, wie Sie ein CameraSelector erstellen, um die Geräteauswahl zu beeinflussen:

Kotlin

fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? {
   val cam2Infos = provider.availableCameraInfos.map {
       Camera2CameraInfo.from(it)
   }.sortedByDescending {
       // HARDWARE_LEVEL is Int type, with the order of:
       // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL
       it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
   }

   return when {
       cam2Infos.isNotEmpty() -> {
           CameraSelector.Builder()
               .addCameraFilter {
                   it.filter { camInfo ->
                       // cam2Infos[0] is either EXTERNAL or best built-in camera
                       val thisCamId = Camera2CameraInfo.from(camInfo).cameraId
                       thisCamId == cam2Infos[0].cameraId
                   }
               }.build()
       }
       else -> null
    }
}

// create a CameraSelector for the USB camera (or highest level internal camera)
val selector = selectExternalOrBestCamera(processCameraProvider)
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

Mehrere Kameras gleichzeitig auswählen

Ab CameraX 1.3 können Sie auch mehrere Kameras gleichzeitig auswählen. Du kannst sie beispielsweise mit einer Front- und Rückkamera verbinden, um Fotos oder Videos aus beiden Perspektiven gleichzeitig aufzunehmen.

Mit der Funktion „Gleichzeitige Kamera“ kann das Gerät zwei Kameras mit unterschiedlichen Objektiven gleichzeitig oder zwei Rückkameras gleichzeitig betreiben. Im folgenden Codeblock sehen Sie, wie Sie beim Aufrufen von bindToLifecycle zwei Kameras einrichten und beide Kameraobjekte aus dem zurückgegebenen ConcurrentCamera-Objekt abrufen.

Kotlin

// Build ConcurrentCameraConfig
val primary = ConcurrentCamera.SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val secondary = ConcurrentCamera.SingleCameraConfig(
    secondaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val concurrentCamera = cameraProvider.bindToLifecycle(
    listOf(primary, secondary)
)

val primaryCamera = concurrentCamera.cameras[0]
val secondaryCamera = concurrentCamera.cameras[1]

Java

// Build ConcurrentCameraConfig
SingleCameraConfig primary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

SingleCameraConfig secondary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

ConcurrentCamera concurrentCamera =  
    mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary));

Camera primaryCamera = concurrentCamera.getCameras().get(0);
Camera secondaryCamera = concurrentCamera.getCameras().get(1);

Kameraauflösung

Sie können die Bildauflösung von CameraX anhand einer Kombination aus Gerätefunktionen, unterstützter Hardwarestufe, Anwendungsfall und bereitgestelltem Seitenverhältnis festlegen lassen. Alternativ können Sie eine bestimmte Zielauflösung oder ein bestimmtes Seitenverhältnis in Anwendungsfällen festlegen, die diese Konfiguration unterstützen.

Automatische Auflösung

CameraX kann basierend auf den in cameraProcessProvider.bindToLifecycle() angegebenen Anwendungsfällen automatisch die besten Auflösungseinstellungen ermitteln. Geben Sie nach Möglichkeit alle Anwendungsfälle für die gleichzeitige Ausführung in einer einzelnen Sitzung in einem einzigen bindToLifecycle()-Aufruf an. CameraX bestimmt Auflösungen basierend auf den begrenzten Anwendungsfällen unter Berücksichtigung des unterstützten Hardwareniveaus des Geräts und der gerätespezifischen Abweichung, wenn ein Gerät die verfügbaren Streamkonfigurationen überschreitet oder nicht erfüllt. Das Ziel besteht darin, die Anwendung auf einer Vielzahl von Geräten auszuführen und gleichzeitig gerätespezifische Codepfade zu minimieren.

Das Standard-Seitenverhältnis für Bilderfassungs- und Bildanalyseanwendungen ist 4:3.

Anwendungsfälle haben ein konfigurierbares Seitenverhältnis, damit die Anwendung das gewünschte Seitenverhältnis basierend auf dem Design der Benutzeroberfläche angeben kann. Die CameraX-Ausgabe wird an die angeforderten Seitenverhältnisse angepasst, die vom Gerät unterstützt werden. Wird keine Auflösung für genaue Übereinstimmung unterstützt, wird diejenige ausgewählt, die die meisten Bedingungen erfüllt. Daher bestimmt die Anwendung, wie die Kamera in der App angezeigt wird, und CameraX ermittelt die besten Einstellungen für die Kameraauflösung auf verschiedenen Geräten.

Beispielsweise kann eine Anwendung folgende Aktionen ausführen:

  • Geben Sie für einen Anwendungsfall eine Zielauflösung von 4:3 oder 16:9 an.
  • Geben Sie eine benutzerdefinierte Auflösung an, mit der CameraX nach einer möglichst genauen Übereinstimmung
  • Legen Sie ein Seitenverhältnis für den Zuschnitt für ImageCapture fest

CameraX wählt die Oberflächenauflösungen der internen Kamera2 automatisch aus. Die folgende Tabelle zeigt die Lösungen:

Anwendungsfall Interne Oberflächenauflösung Auflösung der Ausgabedaten
Vorschau Seitenverhältnis:Die Auflösung, die sich am besten an das Ziel der Einstellung anpasst. Interne Oberflächenauflösung. Es werden Metadaten bereitgestellt, damit eine Ansicht das Zielseitenverhältnis zuschneiden, skalieren und drehen kann.
Standardauflösung:Höchste Vorschauauflösung oder höchste vom Gerät bevorzugte Auflösung, die dem Seitenverhältnis der Vorschau entspricht.
Maximale Auflösung:Vorschaugröße, die sich auf die beste Größe für die Bildschirmauflösung des Geräts bezieht, oder auf 1080p (1920 × 1080), je nachdem, welcher Wert kleiner ist.
Bildanalyse Seitenverhältnis: Die Auflösung, die sich am besten für das Ziel der Einstellung eignet. Interne Oberflächenauflösung.
Standardauflösung: Die Standardeinstellung für die Zielauflösung ist 640 x 480. Wenn sowohl die Zielauflösung als auch das entsprechende Seitenverhältnis angepasst werden, wird die Auflösung am besten unterstützt.
Maximale Auflösung:Die maximale Ausgabeauflösung der Kamera im Format YUV_420_888, die aus StreamConfigurationMap.getOutputSizes() abgerufen wird. Die Zielauflösung ist standardmäßig auf 640 × 480 festgelegt. Wenn Sie also eine höhere Auflösung als 640 × 480 wünschen, müssen Sie setTargetResolution() und setTargetAspectRatio() verwenden, um die nächstgelegene von den unterstützten Auflösungen zu erhalten.
Bildaufnahme Seitenverhältnis:Das Seitenverhältnis, das am besten zur Einstellung passt. Interne Oberflächenauflösung.
Standardauflösung:Höchste verfügbare Auflösung oder höchste vom Gerät bevorzugte Auflösung, die dem Seitenverhältnis von ImageCapture entspricht.
Maximale Auflösung:Die maximale Ausgabeauflösung der Kamera im JPEG-Format. Verwenden Sie zum Abrufen StreamConfigurationMap.getOutputSizes().

Auflösung angeben

Sie können beim Erstellen von Anwendungsfällen mit der Methode setTargetResolution(Size resolution) bestimmte Auflösungen festlegen, wie im folgenden Codebeispiel gezeigt:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

Sie können nicht sowohl das Zielseitenverhältnis als auch die Zielauflösung für denselben Anwendungsfall festlegen. Andernfalls wird beim Erstellen des Konfigurationsobjekts ein IllegalArgumentException ausgegeben.

Drücken Sie die Auflösung Size im Koordinatenframe aus, nachdem Sie die unterstützten Größen um die Zielrotation gedreht haben. Beispielsweise kann ein Gerät mit natürlicher Ausrichtung im Hochformat und natürlicher Zielrotation, das ein Hochformatbild anfordert, 480 × 640 Pixel und dasselbe Gerät, um 90 Grad gedreht und für das Targeting im Querformat, auch 640 × 480 Pixel angeben.

Mit der Zielauflösung wird versucht, eine Mindestgrenze für die Bildauflösung festzulegen. Die tatsächliche Bildauflösung ist die nächstmögliche Auflösung, die nicht kleiner als die Zielauflösung ist, die von der Kameraimplementierung bestimmt wird.

Wenn jedoch keine Auflösung vorhanden ist, die größer oder gleich der Zielauflösung ist, wird die nächstgelegene verfügbare Auflösung ausgewählt, die kleiner als die Zielauflösung ist. Auflösungen, die dasselbe Seitenverhältnis wie die angegebenen Size haben, haben eine höhere Priorität als Auflösungen mit unterschiedlichen Seitenverhältnissen.

CameraX wendet je nach Anforderung die am besten geeignete Auflösung an. Wenn es in erster Linie darum geht, das Seitenverhältnis zu erfüllen, geben Sie nur setTargetAspectRatio an. KameraX ermittelt dann eine bestimmte Auflösung, die für das Gerät geeignet ist. Wenn die Anwendung hauptsächlich eine Auflösung angeben muss, um die Bildverarbeitung effizienter zu gestalten (z. B. ein kleines oder mittelgroßes Bild je nach Verarbeitungsfähigkeit des Geräts), verwenden Sie setTargetResolution(Size resolution).

Wenn für Ihre Anwendung eine exakte Auflösung erforderlich ist, sehen Sie in der Tabelle unter createCaptureSession() nach, welche maximalen Auflösungen von den einzelnen Hardwarestufen unterstützt werden. Welche Auflösungen vom aktuellen Gerät unterstützt werden, erfährst du unter StreamConfigurationMap.getOutputSizes(int).

Wenn deine App Android 10 oder höher verwendet, kannst du mit isSessionConfigurationSupported() eine bestimmte SessionConfiguration bestätigen.

Kameraausgabe steuern

Neben der Möglichkeit, die Kameraausgabe für jeden einzelnen Anwendungsfall zu konfigurieren, implementiert CameraX die folgenden Schnittstellen, um die Kameravorgänge zu unterstützen, die für alle Anwendungsfälle üblich sind:

  • Mit CameraControl können Sie gängige Kamerafunktionen konfigurieren.
  • Mit CameraInfo können Sie den Status dieser gängigen Kamerafunktionen abfragen.

Folgende Kamerafunktionen werden bei der Kamerasteuerung unterstützt:

  • Zoom
  • Fackel
  • Fokus und Messung (zum Fokussieren antippen)
  • Belichtungskorrektur

Instanzen von CameraControl und CameraInfo abrufen

Rufen Sie Instanzen von CameraControl und CameraInfo mit dem Objekt Camera ab, das von ProcessCameraProvider.bindToLifecycle() zurückgegeben wird. Der folgende Code zeigt ein Beispiel:

Kotlin

val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo

Java

Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl()
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo()

Beispielsweise können Sie nach dem Aufrufen von bindToLifecycle() Zoom- und andere CameraControl-Vorgänge senden. Nachdem Sie die Aktivität, die zum Binden der Kamerainstanz verwendet wird, beendet oder gelöscht haben, kann CameraControl keine Vorgänge mehr ausführen und gibt eine fehlgeschlagene ListenableFuture zurück.

Zoom

CameraControl bietet zwei Methoden zum Ändern der Zoomstufe:

  • Mit setZoomRatio() wird der Zoom anhand des Zoomverhältnisses festgelegt.

    Das Verhältnis muss im Bereich von CameraInfo.getZoomState().getValue().getMinZoomRatio() bis CameraInfo.getZoomState().getValue().getMaxZoomRatio() liegen. Andernfalls gibt die Funktion einen fehlgeschlagenen ListenableFuture zurück.

  • Mit setLinearZoom() wird der aktuelle Zoom auf einen linearen Zoomwert zwischen 0 und 1,0 festgelegt.

    Der Vorteil des linearen Zooms besteht darin, dass das Sichtfeld bei Änderungen des Zoomfaktors skaliert wird. Daher ist es ideal zur Verwendung mit einer Slider-Ansicht.

CameraInfo.getZoomState() gibt ein LiveData-Element des aktuellen Zoomstatus zurück. Der Wert ändert sich, wenn die Kamera initialisiert oder die Zoomstufe mit setZoomRatio() oder setLinearZoom() festgelegt wird. Durch den Aufruf einer der beiden Methoden werden die Werte festgelegt, die ZoomState.getZoomRatio() und ZoomState.getLinearZoom() unterstützen. Dies ist hilfreich, wenn Text für das Zoomverhältnis neben einem Schieberegler angezeigt werden soll. Sehen Sie sich einfach die ZoomState-LiveData an, um beide zu aktualisieren, ohne eine Konvertierung ausführen zu müssen.

Das von beiden APIs zurückgegebene ListenableFuture bietet Anwendungen die Möglichkeit, benachrichtigt zu werden, wenn eine wiederkehrende Anfrage mit dem angegebenen Zoomwert abgeschlossen ist. Wenn Sie außerdem einen neuen Zoomwert festlegen, während der vorherige Vorgang noch ausgeführt wird, schlägt außerdem ListenableFuture des vorherigen Zoomvorgangs fehl.

Fackel

CameraControl.enableTorch(boolean) aktiviert oder deaktiviert die Taschenlampe.

Mit CameraInfo.getTorchState() kann der aktuelle Brennerstatus abgefragt werden. Anhand des von CameraInfo.hasFlashUnit() zurückgegebenen Werts lässt sich feststellen, ob eine Taschenlampe verfügbar ist. Ist dies nicht der Fall, führt das Aufrufen von CameraControl.enableTorch(boolean) dazu, dass die zurückgegebene ListenableFuture sofort mit einem fehlgeschlagenen Ergebnis abgeschlossen wird, und der Brennerstatus wird auf TorchState.OFF gesetzt.

Wenn die Taschenlampe aktiviert ist, bleibt sie während der Foto- und Videoaufnahme unabhängig von der Einstellung "flashMode" eingeschaltet. Der flashMode in ImageCapture funktioniert nur, wenn die Taschenlampe deaktiviert ist.

Fokus und Messung

CameraControl.startFocusAndMetering() löst den Autofokus und die Belichtungsmessung aus, indem AF/AE/AWB-Messbereiche basierend auf der festgelegten FocusMeteringAction festgelegt werden. Dies wird häufig in vielen Kamera-Apps verwendet, um die Funktion „Zum Fokussieren antippen“ zu implementieren.

Messpunkt

Erstellen Sie zuerst einen MeteringPoint mit MeteringPointFactory.createPoint(float x, float y, float size). Ein MeteringPoint steht für einen einzelnen Punkt auf der Kamera Surface. Sie werden in normalisierter Form gespeichert, sodass sie einfach in Sensorkoordinaten für die Angabe von AF/AE/AWB-Regionen umgewandelt werden können.

Die Größe von MeteringPoint reicht von 0 bis 1.Die Standardgröße ist 0, 15f. Beim Aufrufen von MeteringPointFactory.createPoint(float x, float y, float size) erstellt CameraX einen rechteckigen Bereich, der bei (x, y) für die angegebene size zentriert ist.

Der folgende Code zeigt, wie ein MeteringPoint erstellt wird:

Kotlin

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener((view, motionEvent) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)
…
}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please note that if the preview is scaled or cropped in the View,
// it’s the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint might need to be adjusted with
// the offsets.
val meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
     imageWidth,
     imageHeight,
     imageAnalysis)

startFocusAndMetering und FocusMeteringAction

Zum Aufrufen von startFocusAndMetering() müssen Anwendungen einen FocusMeteringAction erstellen, der aus einer oder mehreren MeteringPoints mit optionalen Messmoduskombinationen von FLAG_AF, FLAG_AE, FLAG_AWB besteht. Im folgenden Code wird diese Verwendung veranschaulicht:

Kotlin

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action is canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

Wie im Code oben gezeigt, verwendet startFocusAndMetering() eine FocusMeteringAction, die aus einer MeteringPoint für AF/AE/AWB-Messregionen und einem weiteren MeteringPoint nur für AF und AE besteht.

Intern wandelt CameraX sie in Camera2-MeteringRectangles um und legt die entsprechenden Parameter CONTROL_AF_REGIONS/CONTROL_AE_REGIONS/CONTROL_AWB_REGIONS auf die Aufnahmeanfrage fest.

Da nicht jedes Gerät AF/AE/AWB und mehrere Regionen unterstützt, führt CameraX die FocusMeteringAction mit bester Leistung aus. CameraX verwendet die maximal unterstützte Anzahl von MeteringPoints in der Reihenfolge, in der die Punkte hinzugefügt wurden. Alle MeteringPoints, die nach der maximalen Anzahl hinzugefügt wurden, werden ignoriert. Wenn beispielsweise ein FocusMeteringAction mit 3 MeteringPoints auf einer Plattform bereitgestellt wird, die nur 2 MeteringPoints unterstützt, werden nur die ersten 2 MeteringPoints verwendet. Das letzte MeteringPoint-Objekt wird von CameraX ignoriert.

Belichtungskorrektur

Die Belichtungskorrektur ist nützlich, wenn Anwendungen die Belichtungswerte über das Ausgabeergebnis der automatischen Belichtung hinaus noch genauer abstimmen müssen. Die Belichtungskorrekturwerte werden folgendermaßen kombiniert, um die notwendige Belichtung für die aktuellen Bildbedingungen zu ermitteln:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX bietet die Funktion Camera.CameraControl.setExposureCompensationIndex() zum Festlegen der Belichtungskorrektur als Indexwert.

Positive Indexwerte machen das Bild heller, negative Werte dimmen es. Anwendungen können den unterstützten Bereich mit CameraInfo.ExposureState.exposureCompensationRange() abfragen, wie im nächsten Abschnitt beschrieben. Wenn der Wert unterstützt wird, wird das zurückgegebene ListenableFuture abgeschlossen, sobald der Wert in der Erfassungsanfrage erfolgreich aktiviert wurde. Wenn der angegebene Index außerhalb des unterstützten Bereichs liegt, bewirkt setExposureCompensationIndex(), dass die zurückgegebene ListenableFuture sofort mit einem fehlgeschlagenen Ergebnis abgeschlossen wird.

CameraX behält nur die letzte ausstehende setExposureCompensationIndex()-Anfrage bei und ruft die Funktion mehrmals auf, bevor die vorherige Anfrage ausgeführt wird und die Ergebnisse abgebrochen werden.

Das folgende Snippet legt einen Belichtungsausgleichsindex fest und registriert einen Callback für den Fall, dass die Anfrage zur Kontaktänderung ausgeführt wurde:

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it might be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      …
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() ruft den aktuellen ExposureState ab, einschließlich:

    • Die Unterstützung einer Belichtungskompensation.
    • Der aktuelle Belichtungskorrekturindex.
    • Der Bereich für den Belichtungsausgleichsindex.
    • Der Schritt zur Belichtungskorrektur, der bei der Berechnung des Belichtungskompensationswerts verwendet wird.

Mit dem folgenden Code werden beispielsweise die Einstellungen für die Offenlegung SeekBar mit aktuellen ExposureState-Werten initialisiert:

Kotlin

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

Weitere Informationen

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

Codelab

  • Erste Schritte mit CameraX
  • Codebeispiel

  • CameraX-Beispiel-Apps
  • Entwickler-Community

    Diskussionsgruppe zu Android CameraX