Konfigurationsoptionen

Sie konfigurieren jeden CameraX-Anwendungsfall, um verschiedene Aspekte der Vorgänge des Anwendungsfalls zu steuern.

Beim Anwendungsfall „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 bereit, mit denen Einstellungen nach dem Erstellen des Anwendungsfalls dynamisch geändert werden können. Informationen zur Konfiguration für die einzelnen Anwendungsfälle finden Sie unter Vorschau implementieren, Bilder analysieren und Bilderfassung.

CameraXConfig

Der Einfachheit halber bietet CameraX Standardkonfigurationen wie interne Executors und Handler, die für die meisten Anwendungsfälle geeignet sind. Wenn Ihre Anwendung jedoch besondere Anforderungen hat oder Sie diese Konfigurationen anpassen möchten, ist CameraXConfig die Schnittstelle für diesen Zweck.

Mit CameraXConfig kann eine Anwendung Folgendes tun:

Nutzungsmodell

Im Folgenden wird beschrieben, wie Sie CameraXConfig verwenden:

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

Im folgenden Codebeispiel wird das CameraX-Logging beispielsweise auf Fehlermeldungen beschränkt:

Kotlin

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

Behalte eine lokale Kopie des CameraXConfig-Objekts, wenn deine Anwendung die CameraX-Konfiguration nach dem Festlegen kennen muss.

Kamerabegrenzung

Beim ersten Aufruf von ProcessCameraProvider.getInstance() listet CameraX die auf dem Gerät verfügbaren Kameras auf und fragt ihre Eigenschaften ab. Da CameraX mit Hardwarekomponenten kommunizieren muss, kann dieser Vorgang für jede Kamera eine gewisse Zeit in Anspruch nehmen, insbesondere auf Geräten mit geringer Leistung. Wenn deine App nur bestimmte Kameras auf dem Gerät verwendet, z. B. die Standard-Frontkamera, kannst du CameraX so einstellen, dass andere Kameras ignoriert werden. Dadurch kann die Startlatenz für die von deiner App verwendeten Kameras reduziert werden.

Wenn der an CameraXConfig.Builder.setAvailableCamerasLimiter() übergebene CameraSelector eine Kamera herausfiltert, verhält sich CameraX so, als ob diese Kamera nicht existiert. Mit dem folgenden Code wird die Anwendung beispielsweise so eingeschränkt, dass nur die Standard-Rückkamera des Geräts verwendet wird:

Kotlin

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

Threads

Viele der Plattform-APIs, auf denen CameraX basiert, erfordern eine blockierende Interprocess Communication (IPC) mit Hardware, die manchmal Hunderte von Millisekunden für eine Antwort benötigt. Aus diesem Grund ruft CameraX diese APIs nur über Hintergrundthreads auf, damit der Hauptthread nicht blockiert wird und die Benutzeroberfläche flüssig 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.

Camera Executor

Der Kamera-Executor wird für alle internen API-Aufrufe der Kamera-Plattform 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 der Threads erfordert, verwenden Sie CameraXConfig.Builder.setCameraExecutor().

Planer-Handler

Der Scheduler-Handler wird verwendet, um interne Aufgaben in festen Intervallen zu planen, z. B. um das Öffnen der Kamera noch einmal zu versuchen, wenn sie nicht verfügbar ist. Dieser Handler führt keine Jobs aus, sondern leitet sie nur an den Kamera-Executor weiter. Sie wird manchmal auch auf Legacy-API-Plattformen verwendet, die einen Handler für Callbacks erfordern. 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.

Logging

Mit dem CameraX-Logging können Anwendungen Logcat-Nachrichten filtern. Es empfiehlt sich, ausführliche Nachrichten im Produktionscode zu vermeiden. CameraX unterstützt vier Logging-Stufen, 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 Dokumentation zu Android-Logs. Verwenden Sie CameraXConfig.Builder.setMinimumLoggingLevel(int), um die entsprechende Logging-Ebene für Ihre Anwendung festzulegen.

Automatische Auswahl

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

Das Ziel von CameraX ist es, eine Kamerasitzung erfolgreich zu initialisieren. Das bedeutet, dass CameraX je nach Gerätefunktionen Kompromisse bei Auflösung und Seitenverhältnis eingeht. Das kann folgende Gründe haben:

  • Das Gerät unterstützt die angeforderte Auflösung nicht.
  • Das Gerät hat Kompatibilitätsprobleme, z. B. ältere Geräte, die bestimmte Auflösungen erfordern, um ordnungsgemäß zu funktionieren.
  • Auf einigen Geräten sind bestimmte Formate nur in bestimmten Seitenverhältnissen verfügbar.
  • Das Gerät bevorzugt „nächstes mod16“ für die JPEG- oder Videocodierung. Weitere Informationen finden Sie unter SCALER_STREAM_CONFIGURATION_MAP.

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

Rotation

Standardmäßig ist die Kamerarotation so eingestellt, dass sie der Standardrotation des Displays bei der Erstellung des Anwendungsfalls entspricht. In diesem Standardfall erzeugt CameraX Ausgaben, damit die App dem entspricht, was Sie in der Vorschau erwarten. Du kannst die Drehung auf einen benutzerdefinierten Wert ändern, um Geräte mit mehreren Displays zu unterstützen. Dazu übergibst du die aktuelle Displayausrichtung, wenn du Anwendungsfallobjekte konfigurierst, oder dynamisch, nachdem sie erstellt wurden.

In Ihrer App kann die Zielrotation über Konfigurationseinstellungen festgelegt werden. Anschließend können die Rotationseinstellungen mithilfe der Methoden aus den Anwendungsfall-APIs (z. B. ImageAnalysis.setTargetRotation()) aktualisiert werden, auch wenn der Lebenszyklus aktiv ist. Dies kann nützlich sein, wenn die App im Porträtmodus gesperrt ist und daher bei einer Drehung keine Neukonfiguration erfolgt, aber für den Foto- oder Analyseanwendungsfall die aktuelle Drehung des Geräts bekannt sein muss. Beispielsweise kann die Erkennung der Ausrichtung erforderlich sein, damit Gesichter für die Gesichtserkennung richtig ausgerichtet sind oder Fotos im Quer- oder Hochformat angezeigt werden.

Daten für aufgenommene Bilder werden möglicherweise ohne Rotationsinformationen gespeichert. Exif-Daten enthalten Informationen zur Drehung, damit Galerieanwendungen das Bild nach dem Speichern in der richtigen Ausrichtung anzeigen können.

Wenn Sie Vorschaudaten mit der richtigen Ausrichtung anzeigen möchten, können Sie die Metadatenausgabe von Preview.PreviewOutput() verwenden, um Transformationen zu erstellen.

Das folgende Codebeispiel zeigt, wie die Drehung 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();
}

Je nach festgelegter Drehung wird bei jedem Anwendungsfall entweder die Bilddaten direkt gedreht oder es werden Rotationsmetadaten für die Nutzer der nicht gedrehten Bilddaten bereitgestellt.

  • Vorschau: Die Metadatenausgabe wird bereitgestellt, damit die Drehung der Zielauflösung mit Preview.getTargetRotation() bekannt ist.
  • ImageAnalysis: Die Metadatenausgabe wird bereitgestellt, damit die Koordinaten des Bildzwischenspeichers relativ zu den Anzeigekoordinaten bekannt sind.
  • ImageCapture: Die Exif-Metadaten des Bildes, der Zwischenspeicher oder sowohl der Zwischenspeicher als auch die Metadaten werden geändert, um die Rotationseinstellung zu berücksichtigen. Der geänderte Wert hängt von der HAL-Implementierung ab.

Zuschneiderechteck

Standardmäßig ist das Zuschneiderechteck das vollständige Pufferrechteck. Sie können sie mit ViewPort und UseCaseGroup anpassen. Durch das Gruppieren von Anwendungsfällen und das Festlegen des Darstellungsbereichs sorgt CameraX dafür, dass die Zuschneiderechtecke 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 Pufferrechteck. Anschließend berechnet CameraX das größtmögliche Zuschneiderechteck basierend auf den Eigenschaften des Darstellungsbereichs und den angehängten Anwendungsfällen. Normalerweise können Sie den Darstellungsbereich basierend auf dem Anwendungsfall der Vorschau konfigurieren, um einen WYSIWYG-Effekt zu erzielen. Eine einfache Möglichkeit, den Darstellungsbereich abzurufen, ist die Verwendung von PreviewView.

Die folgenden Code-Snippets zeigen, wie Sie das ViewPort-Objekt abrufen:

Kotlin

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

Java

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

Im vorherigen Beispiel entspricht das, was die App von ImageAnalysis und ImageCapture erhält, dem, was der Endnutzer in PreviewView sieht, vorausgesetzt, der Skalierungstyp von PreviewView ist auf den Standardwert FILL_CENTER festgelegt. Nachdem das Zuschneiderechteck und die Drehung auf den Ausgabepuffer angewendet wurden, ist das Bild aus allen Anwendungsfällen identisch, möglicherweise jedoch mit unterschiedlichen Auflösungen. Weitere Informationen zum Anwenden der Transformationsinformationen finden Sie unter Transformationsausgabe.

Kameraauswahl

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

Das folgende Codebeispiel zeigt, wie Sie einen 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 beispielsweise eine Front- und eine Rückkamera binden, um gleichzeitig Fotos aufzunehmen oder Videos aus beiden Perspektiven aufzuzeichnen.

Wenn du die Funktion „Gleichzeitige Kamera“ verwendest, kann das Gerät zwei Kameras mit unterschiedlich ausgerichteten Objektiven oder zwei Rückkameras gleichzeitig verwenden. Der folgende Codeblock zeigt, wie Sie beim Aufrufen von bindToLifecycle zwei Kameras festlegen und wie Sie 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 CameraX die Bildauflösung basierend auf einer Kombination aus den Gerätefunktionen, der unterstützten Hardwareebene des Geräts, dem Anwendungsfall und dem angegebenen Seitenverhältnis festlegen lassen. Alternativ können Sie in Anwendungsfällen, die diese Konfiguration unterstützen, eine bestimmte Zielauflösung oder ein bestimmtes Seitenverhältnis festlegen.

Automatische Auflösung

CameraX kann die besten Auflösungseinstellungen automatisch anhand der in cameraProcessProvider.bindToLifecycle() angegebenen Anwendungsfälle ermitteln. Geben Sie nach Möglichkeit alle Anwendungsfälle, die gleichzeitig in einer einzelnen Sitzung ausgeführt werden müssen, in einem einzigen bindToLifecycle()-Aufruf an. CameraX bestimmt die Auflösungen anhand der gebundenen Anwendungsfälle. Dabei werden die unterstützte Hardwarestufe des Geräts und gerätespezifische Abweichungen berücksichtigt, d. h. ob ein Gerät die verfügbaren Streamkonfigurationen über- oder unterschreitet. Ziel ist es, die Anwendung auf einer Vielzahl von Geräten auszuführen und gleichzeitig gerätespezifische Codepfade zu minimieren.

Das Standardseitenverhältnis für Anwendungsfälle der Bilderfassung und Bildanalyse ist 4:3.

Anwendungsfälle haben ein konfigurierbares Seitenverhältnis, sodass die Anwendung das gewünschte Seitenverhältnis basierend auf dem UI-Design angeben kann. Die CameraX-Ausgabe wird so erzeugt, dass sie den angeforderten Seitenverhältnissen so weit wie möglich entspricht, wie es das Gerät unterstützt. Wenn keine genaue Übereinstimmung unterstützt wird, wird die Auflösung ausgewählt, die die meisten Bedingungen erfüllt. Die Anwendung bestimmt also, wie die Kamera in der App angezeigt wird, und CameraX ermittelt die besten Kameraauflösungseinstellungen, um dies auf verschiedenen Geräten zu ermöglichen.

Eine App kann beispielsweise Folgendes tun:

  • Zielauflösung von 4:3 oder 16:9 für einen Anwendungsfall angeben
  • Eine benutzerdefinierte Auflösung angeben, für die CameraX versucht, die beste Übereinstimmung zu finden
  • Seitenverhältnis für den Zuschneidebereich für ImageCapture angeben

CameraX wählt die internen Camera2-Oberflächenauflösungen automatisch aus. In der folgenden Tabelle sind die Auflösungen aufgeführt:

Anwendungsfall Auflösung der internen Oberfläche Auflösung der Ausgabedaten
Vorschau Seitenverhältnis: Die Auflösung, die am besten zum Ziel der Einstellung passt. Interne Oberflächenauflösung. Es werden Metadaten bereitgestellt, damit eine Ansicht für das Zielseitenverhältnis zugeschnitten, skaliert und gedreht werden 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 der besten Größe für die Bildschirmauflösung des Geräts entspricht, oder 1080p (1920 × 1080), je nachdem, welcher Wert kleiner ist.
Bildanalyse Seitenverhältnis: Die Auflösung, die am besten zum Ziel der Einstellung passt. Interne Oberflächenauflösung.
Standardauflösung: Die Standardeinstellung für die Zielauflösung ist 640 × 480. Wenn du sowohl die Zielauflösung als auch das entsprechende Seitenverhältnis anpasst, erhältst du die am besten unterstützte Auflösung.
Maximale Auflösung: Die maximale Ausgabeauflösung des Kamerageräts im Format YUV_420_888, die von StreamConfigurationMap.getOutputSizes() abgerufen wird. Die Zielauflösung ist standardmäßig auf 640 × 480 festgelegt. Wenn Sie eine Auflösung größer als 640 × 480 wünschen, müssen Sie setTargetResolution() und setTargetAspectRatio() verwenden, um die nächstgelegene der unterstützten Auflösungen zu erhalten.
Bilderfassung Seitenverhältnis: 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 des Kamerageräts im JPEG-Format. Verwenden Sie StreamConfigurationMap.getOutputSizes(), um diese abzurufen.

Auflösung angeben

Sie können bestimmte Auflösungen festlegen, wenn Sie Anwendungsfälle mit der Methode setTargetResolution(Size resolution) erstellen, 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 Ziel-Seitenverhältnis als auch die Zielauflösung für denselben Anwendungsfall festlegen. In diesem Fall wird beim Erstellen des Konfigurationsobjekts ein IllegalArgumentException-Fehler ausgegeben.

Geben Sie die Auflösung Size im Koordinatensystem an, nachdem Sie die unterstützten Größen um die Zieldrehung gedreht haben. Beispiel: Ein Gerät mit natürlicher Ausrichtung im Hochformat, das ein Bild im Hochformat anfordert, kann 480 × 640 angeben. Dasselbe Gerät, das um 90 Grad gedreht ist und auf das Querformat ausgerichtet ist, kann 640 × 480 angeben.

Mit der Zielauflösung wird versucht, eine Mindestgrenze für die Bildauflösung festzulegen. Die tatsächliche Bildauflösung ist die nächstgelegene verfügbare Auflösung, die nicht kleiner als die Zielauflösung ist, wie von der Kameraimplementierung festgelegt.

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

CameraX wendet die am besten geeignete Auflösung basierend auf den Anfragen an. Wenn das primäre Ziel darin besteht, das Seitenverhältnis zu erfüllen, geben Sie nur setTargetAspectRatio an. CameraX bestimmt dann eine geeignete Auflösung basierend auf dem Gerät. Wenn die App hauptsächlich eine Auflösung angeben muss, um die Bildverarbeitung effizienter zu gestalten (z. B. ein kleines oder mittelgroßes Bild basierend auf der Verarbeitungsleistung des Geräts), verwenden Sie setTargetResolution(Size resolution).

Wenn Ihre App eine genaue Auflösung erfordert, finden Sie in der Tabelle unter createCaptureSession() Informationen dazu, welche maximalen Auflösungen von den einzelnen Hardwarestufen unterstützt werden. Informationen zu den vom aktuellen Gerät unterstützten Auflösungen findest du unter StreamConfigurationMap.getOutputSizes(int).

Wenn Ihre App unter Android 10 oder höher ausgeführt wird, können Sie isSessionConfigurationSupported() verwenden, um eine bestimmte SessionConfiguration zu bestätigen.

Kameraausgabe steuern

Mit CameraX kannst du nicht nur die Kameraausgabe für jeden einzelnen Anwendungsfall nach Bedarf konfigurieren, sondern auch die folgenden Schnittstellen implementieren, um gängige Kameravorgänge für alle gebundenen Anwendungsfälle zu unterstützen:

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

Folgende Kamerafunktionen werden mit CameraControl unterstützt:

  • Zoom
  • Taschenlampe, einschließlich Taschenlampenhelligkeit auf unterstützten Geräten
  • Low Light-Modus auf unterstützten Geräten
  • Fokus und Belichtung (Zum Fokussieren tippen)
  • 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();

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

Zoom

„CameraControl“ bietet zwei Methoden zum Ändern der Zoomstufe:

  • setZoomRatio() legt den Zoom anhand des Zoomverhältnisses fest.

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

  • setLinearZoom() legt den aktuellen Zoom mit einem linearen Zoomwert zwischen 0 und 1,0 fest.

    Der Vorteil des linearen Zooms besteht darin, dass das Sichtfeld (FOV) mit Änderungen des Zooms skaliert wird. Daher eignet sie sich ideal für die Verwendung mit einer Slider-Ansicht.

CameraInfo.getZoomState() gibt ein LiveData-Objekt des aktuellen Zoomstatus zurück. Der Wert ändert sich, wenn die Kamera initialisiert wird oder die Zoomstufe mit setZoomRatio() oder setLinearZoom() festgelegt wird. Durch den Aufruf einer der beiden Methoden werden die Werte für ZoomState.getZoomRatio() und ZoomState.getLinearZoom() festgelegt. Das ist hilfreich, wenn Sie neben einem Schieberegler Text zum Zoomverhältnis anzeigen möchten. Beobachten Sie einfach die ZoomState LiveData, um beide zu aktualisieren, ohne eine Konvertierung durchführen zu müssen.

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

Fackel

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

Mit CameraInfo.getTorchState() kann der aktuelle Status der Taschenlampe abgefragt werden. Sie können den von CameraInfo.hasFlashUnit() zurückgegebenen Wert prüfen, um festzustellen, ob ein Blitz verfügbar ist. Andernfalls führt der Aufruf von CameraControl.enableTorch(boolean) dazu, dass die zurückgegebene ListenableFuture sofort mit einem Fehlerergebnis abgeschlossen und der Blitzlichtstatus auf TorchState.OFF gesetzt wird.

Wenn die Taschenlampe aktiviert ist, bleibt sie während der Foto- und Videoaufnahme eingeschaltet, unabhängig von der Einstellung des Blitzmodus. Der mit ImageCapture.setFlashMode() festgelegte Blitzmodus funktioniert nur, wenn die Taschenlampe deaktiviert ist.

Fokus und Belichtungsmessung

CameraControl.startFocusAndMetering() löst die Messung von Autofokus (AF), automatischer Belichtung (AE) und automatischem Weißabgleich (AWB) aus, indem Messbereiche basierend auf dem angegebenen FocusMeteringAction festgelegt werden. Mit dieser Methode kannst du eine Funktion zum Fokussieren per Tippen in Kamera-Apps implementieren.

MeteringPoint

Erstellen Sie zuerst mit MeteringPointFactory.createPoint(float x, float y, float size) ein MeteringPoint. Ein MeteringPoint stellt einen einzelnen Punkt auf der Surface der Kamera dar. Sie werden in normalisierter Form gespeichert, damit sie in Sensorkoordinaten umgewandelt werden können, um AF/AE/AWB-Regionen anzugeben.

Die Größe des MeteringPoint liegt zwischen 0 und 1, wobei die Standardgröße 0,15f beträgt. Beim Aufrufen von MeteringPointFactory.createPoint(float x, float y, float size) erstellt CameraX einen rechteckigen Bereich, der für die angegebene size auf (x, y) zentriert ist.

Der folgende Code zeigt, wie Sie einen MeteringPoint erstellen:

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener { view, motionEvent ->
    if (motionEvent.action == MotionEvent.ACTION_UP) {
        val meteringPoint = previewView.meteringPointFactory
            .createPoint(motionEvent.x, motionEvent.y)
        // Build and start a FocusMeteringAction with meteringPoint.
        view.performClick()
    }
    true
}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used
// for preview. 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 displayFactory = DisplayOrientedMeteringPointFactory(
    surfaceView.display,
    camera.cameraInfo,
    surfaceView.width.toFloat(),
    surfaceView.height.toFloat()
)

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

startFocusAndMetering und FocusMeteringAction

Zum Aufrufen von startFocusAndMetering() müssen Anwendungen eine FocusMeteringAction erstellen, die aus einem oder mehreren MeteringPoint-Objekten mit optionalen Kombinationen von Messmodi aus FLAG_AF, FLAG_AE und FLAG_AWB besteht. Der folgende Code veranschaulicht diese Verwendung:

// meteringPointFactory is any MeteringPointFactory from the previous example.
val meteringPoint1 = meteringPointFactory.createPoint(x1, y1)
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 or 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 vorherigen Code gezeigt, verwendet startFocusAndMetering() ein FocusMeteringAction, das aus einem MeteringPoint für AF/AE/AWB-Messregionen und einem weiteren MeteringPoint nur für AF und AE besteht.

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

Belichtungskorrektur

Die Belichtungskorrektur ist nützlich, wenn Anwendungen die Lichtwerte (LW) über das Ergebnis der automatischen Belichtung hinaus optimieren müssen. Die Werte für die Belichtungskorrektur werden folgendermaßen kombiniert, um die erforderliche Belichtung für die aktuellen Bildbedingungen zu bestimmen:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX bietet die Funktion CameraControl.setExposureCompensationIndex(), mit der die Belichtungskorrektur als Indexwert festgelegt werden kann.

Positive Indexwerte machen das Bild heller, negative Werte dunkler. Anwendungen können den unterstützten Bereich mit ExposureState.getExposureCompensationRange() abfragen. Wenn der Wert unterstützt wird, wird der zurückgegebene ListenableFuture abgeschlossen, wenn der Wert in der Erfassungsanfrage erfolgreich aktiviert wurde. Wenn der angegebene Index außerhalb des unterstützten Bereichs liegt, wird der zurückgegebene ListenableFuture durch setExposureCompensationIndex() sofort mit einem fehlgeschlagenen Ergebnis abgeschlossen.

CameraX behält nur die letzte ausstehende setExposureCompensationIndex()-Anfrage bei. Wenn Sie die Funktion noch einmal aufrufen, bevor CameraX die vorherige Anfrage angewendet hat, bricht CameraX die vorherige Anfrage ab.

Im folgenden Snippet wird ein Index für die Belichtungskorrektur festgelegt und ein Callback registriert, der ausgeführt wird, wenn die Anfrage zur Belichtungsänderung ausgeführt wurde:

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
        // Handle the updated exposure compensation index.
    }, mainExecutor)
  • CameraInfo.getExposureState() ruft die aktuelle ExposureState ab, einschließlich:

    • Die Unterstützung der Belichtungskorrektur.
    • Der aktuelle Index für die Belichtungskorrektur.
    • Der Bereich des Belichtungskorrekturindex.
    • Der Schritt der Belichtungskorrektur, der bei der Berechnung des Belichtungskorrekturwerts verwendet wird.

Im folgenden Code werden beispielsweise die Einstellungen für eine Belichtung SeekBar mit aktuellen ExposureState-Werten initialisiert. Da ein SeekBar-Fortschrittswert bei 0 beginnt, wird er im Code um die untere Grenze des Belichtungskorrekturbereichs verschoben:

val exposureState = camera.cameraInfo.exposureState
val range = exposureState.exposureCompensationRange
binding.seekBar.apply {
    isEnabled = exposureState.isExposureCompensationSupported
    max = range.upper - range.lower
    progress = exposureState.exposureCompensationIndex - range.lower
    setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
        override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
            if (fromUser) {
                camera.cameraControl.setExposureCompensationIndex(progress + range.lower)
            }
        }
        override fun onStartTrackingTouch(seekBar: SeekBar) {}
        override fun onStopTrackingTouch(seekBar: SeekBar) {}
    })
}

Zusätzliche Ressourcen

Weitere Informationen zu CameraX findest du in den folgenden Ressourcen.

Codelab

  • Erste Schritte mit CameraX
  • Codebeispiel

  • CameraX-Beispiel-Apps
  • Entwickler-Community

    Android CameraX-Forum