Konfigurationsoptionen

Sie konfigurieren jeden CameraX-Anwendungsfall, um verschiedene Aspekte des Betriebs des Anwendungsfalls zu steuern.

Für den Anwendungsfall „Bildaufnahme“ können Sie beispielsweise ein Zielseitenverhältnis und einen Blitzmodus festlegen. Im folgenden Code wird ein Beispiel gezeigt:

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 sich Einstellungen nach dem Erstellen des Anwendungsfalls dynamisch ändern lassen. Informationen zur Konfiguration für die einzelnen Anwendungsfälle finden Sie unter Vorschau implementieren, Bilder analysieren und Bilder erfassen.

CameraXConfig

Zur Vereinfachung gibt es in CameraX Standardkonfigurationen wie interne Executors und Handler, die für die meisten Anwendungsfälle geeignet sind. Wenn Ihre Anwendung jedoch spezielle Anforderungen hat oder Sie diese Konfigurationen lieber anpassen möchten, ist CameraXConfig die richtige Oberfläche.

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. Implementiere die Schnittstelle CameraXConfig.Provider in deinem Application und gib das CameraXConfig-Objekt in getCameraXConfig() zurück.
  3. Fügen Sie der Datei AndroidManifest.xml die Application-Klasse 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()
   }
}

Behalten Sie eine lokale Kopie des CameraXConfig-Objekts, wenn Ihre Anwendung die CameraX-Konfiguration nach der Einrichtung kennen muss.

Kamerabegrenzer

Bei der ersten Aufrufung von ProcessCameraProvider.getInstance() zählt CameraX die Kameras auf dem Gerät auf und fragt deren Eigenschaften ab. Da CameraX mit Hardwarekomponenten kommunizieren muss, kann dieser Vorgang für jede Kamera relativ lange dauern, insbesondere auf 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 Startverzögerung für die von Ihrer Anwendung verwendeten Kameras reduziert werden.

Wenn die CameraSelector, die an CameraXConfig.Builder.setAvailableCamerasLimiter() übergeben wird, eine Kamera herausfiltert, verhält sich CameraX so, als würde diese Kamera nicht existieren. Mit dem folgenden Code wird beispielsweise festgelegt, dass die Anwendung nur die Standardrückkamera des Geräts verwendet:

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 Inter-Process-Kommunikation (IPC) mit Hardware, die manchmal Hunderte von Millisekunden für eine Antwort benötigt. Aus diesem Grund ruft CameraX diese APIs nur aus 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. Bei einigen Anwendungen ist jedoch eine strenge Steuerung der Threads erforderlich. Mit CameraXConfig kann eine Anwendung die Hintergrundthreads festlegen, die über CameraXConfig.Builder.setCameraExecutor() und CameraXConfig.Builder.setSchedulerHandler() verwendet werden.

Kamera-Executor

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

Scheduler-Handler

Mit dem Scheduler-Handler werden interne Aufgaben in festen Intervallen geplant, z. B. der erneute Versuch, die Kamera zu öffnen, wenn sie nicht verfügbar ist. Dieser Handler führt keine Jobs aus, sondern sendet sie nur an den Kameraausführer. Sie wird manchmal auch auf älteren API-Plattformen verwendet, für die ein Handler für Callbacks erforderlich ist. In diesen Fällen werden die Rückrufe weiterhin direkt an den Kameraausführer gesendet. CameraX weist und verwaltet eine interne HandlerThread zu, um diese Aufgaben auszuführen. Sie können sie jedoch mit CameraXConfig.Builder.setSchedulerHandler() überschreiben.

Protokollierung

Mit CameraX-Logging können Anwendungen Logcat-Nachrichten filtern. Es ist empfehlenswert, ausführliche Nachrichten in Ihrem Produktionscode zu vermeiden. CameraX unterstützt vier Logging-Ebenen, von der ausführlichsten bis zur striktesten:

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

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

Automatische Auswahl

CameraX bietet automatisch Funktionen, die für das Gerät spezifisch sind, auf dem Ihre App ausgeführt wird. CameraX ermittelt beispielsweise 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 bei der Auflösung und dem Seitenverhältnis Kompromisse eingeht, die auf den Gerätefunktionen basieren. Die Manipulation kann folgende Gründe haben:

  • Das Gerät unterstützt die angeforderte Auflösung nicht.
  • Das Gerät weist Kompatibilitätsprobleme auf, z. B. bei älteren Geräten, die für die ordnungsgemäße Funktion bestimmte Auflösungen erfordern.
  • Auf einigen Geräten sind bestimmte Formate nur mit bestimmten Seitenverhältnissen verfügbar.
  • Das Gerät hat eine Einstellung für „nearest mod16“ für die JPEG- oder Videocodierung. Weitere Informationen finden Sie unter SCALER_STREAM_CONFIGURATION_MAP.

Auch wenn CameraX die Sitzung erstellt und verwaltet, sollten Sie immer die zurückgegebenen Bildgrößen in der Anwendungsfallausgabe in Ihrem Code prüfen und entsprechend anpassen.

Ausrichtung

Standardmäßig wird die Kameradrehung so eingestellt, dass sie der Drehung des Standarddisplays beim Erstellen des Use-Cases entspricht. In diesem Standardfall gibt CameraX Ausgabedaten aus, damit die App den Erwartungen in der Vorschau entspricht. Sie können die Drehung in einen benutzerdefinierten Wert ändern, um Geräte mit mehreren Displays zu unterstützen. Geben Sie dazu beim Konfigurieren von Use-Case-Objekten die aktuelle Displayausrichtung an oder ändern Sie sie nach dem Erstellen dynamisch.

In Ihrer App kann die Zieldrehung über die Konfigurationseinstellungen festgelegt werden. Die Rotationseinstellungen können dann mithilfe der Methoden aus den Anwendungsfall-APIs (z. B. ImageAnalysis.setTargetRotation()) aktualisiert werden, auch wenn der Lebenszyklus aktiv ist. Sie können diese Option verwenden, wenn die App im Hochformat gesperrt ist und daher beim Drehen keine Neukonfiguration erfolgt. Der Anwendungsfall für Fotos oder Analysen muss jedoch die aktuelle Drehung des Geräts berücksichtigen. Beispielsweise kann die Ausrichtung berücksichtigt werden, damit Gesichter für die Gesichtserkennung richtig ausgerichtet sind oder Fotos im Quer- oder Hochformat vorliegen.

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

Wenn Sie Vorschaudaten in der richtigen Ausrichtung anzeigen möchten, können Sie mithilfe der Metadatenausgabe von Preview.PreviewOutput() Transformationen erstellen.

Im folgenden Codebeispiel wird gezeigt, 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 eingestellter Drehung werden die Bilddaten bei jedem Anwendungsfall entweder direkt gedreht oder den Nutzern der nicht gedrehten Bilddaten werden Drehungsmetadaten zur Verfügung gestellt.

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

Rechteck zuschneiden

Standardmäßig ist das Zuschneide-Rechteck der gesamte Buffor-Rechteck. Sie können es mit ViewPort und UseCaseGroup anpassen. Durch das Gruppieren von Anwendungsfällen und das Festlegen des Ansichtsbereichs sorgt CameraX dafür, dass die Zuschneiderechtecke aller Anwendungsfälle in der Gruppe auf denselben Bereich im Kamerasensor zeigen.

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 den für Endnutzer sichtbaren Pufferbereich. Anschließend berechnet CameraX anhand der Eigenschaften des Ansichtsbereichs und der angehängten Anwendungsfälle das größte mögliche Zuschneide-Rechteck. Normalerweise können Sie den Darstellungsbereich basierend auf dem Vorschau-Use-Case 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 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 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 die Standardeinstellung FILL_CENTER festgelegt. Nachdem das Zuschneide-Rechteck und die Drehung auf den Ausgabebuffer angewendet wurden, ist das Bild aus allen Anwendungsfällen identisch, jedoch möglicherweise 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 Sie ein anderes Gerät als das ausgewählte verwenden möchten, haben Sie folgende Möglichkeiten:

Im folgenden Codebeispiel wird gezeigt, wie eine CameraSelector erstellt wird, 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. Sie können beispielsweise eine Front- und eine Rückkamera binden, um Fotos oder Videos aus beiden Perspektiven gleichzeitig aufzunehmen.

Wenn Sie die Funktion „Simultane Kamera“ verwenden, kann das Gerät zwei Kameras mit Objektiven in unterschiedlichen Richtungen oder zwei Rückkameras gleichzeitig betreiben. Im folgenden Codeblock wird gezeigt, wie Sie beim Aufrufen von bindToLifecycle zwei Kameras festlegen 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 festlegen, dass 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 festlegt. Alternativ kannst du 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 basierend auf den in cameraProcessProvider.bindToLifecycle() angegebenen Anwendungsfällen automatisch ermitteln. Geben Sie nach Möglichkeit alle Anwendungsfälle an, die in einer einzigen Sitzung gleichzeitig ausgeführt werden müssen, in einem einzigen bindToLifecycle()-Aufruf. CameraX bestimmt die Auflösungen basierend auf den Anwendungsfällen, wobei die unterstützte Hardwareebene des Geräts und gerätespezifische Abweichungen berücksichtigt werden (d. h., ob ein Gerät die verfügbaren Streamkonfigurationen überschreitet oder nicht erfüllt). Ziel ist es, die Anwendung auf einer Vielzahl von Geräten ausführen zu können und gleichzeitig gerätespezifische Codepfade zu minimieren.

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

Anwendungsfälle haben ein konfigurierbares Seitenverhältnis, damit die Anwendung das gewünschte Seitenverhältnis basierend auf dem UI-Design angeben kann. Die CameraX-Ausgabe wird so erstellt, dass sie den angeforderten Seitenverhältnissen so nahe wie möglich kommt, was das Gerät unterstützt. Wenn keine Lösung mit exakter Übereinstimmung unterstützt wird, wird die Lösung ausgewählt, die die meisten Bedingungen erfüllt. Die App legt also fest, wie die Kamera in der App angezeigt wird, und CameraX bestimmt die besten Kameraauflösungseinstellungen, um diese Anforderung auf verschiedenen Geräten zu erfüllen.

Eine App kann beispielsweise Folgendes tun:

  • Zielauflösung von 4:3 oder 16:9 für einen Anwendungsfall angeben
  • Geben Sie eine benutzerdefinierte Auflösung an, für die CameraX die beste Übereinstimmung finden soll.
  • Seitenverhältnis für den Zuschnitt von ImageCapture angeben

CameraX wählt die internen Camera2-Oberflächenauflösungen automatisch aus. Die folgende Tabelle enthält die Auflösungen:

Anwendungsfall Auflösung der Innenfläche Auflösung der Ausgabedaten
Vorschau Seitenverhältnis:Die Auflösung, die das Ziel am besten an die Einstellung anpasst. Interne Oberflächenauflösung. Metadaten werden bereitgestellt, damit eine Ansicht zugeschnitten, skaliert und für das Zielseitenverhältnis gedreht werden kann.
Standardauflösung:Die höchste Auflösung der Vorschau oder die höchste vom Gerät bevorzugte Auflösung, die dem Seitenverhältnis der Vorschau entspricht.
Maximale Auflösung:Größe der Vorschau, die der Bildschirmauflösung des Geräts oder 1080p (1920 × 1080) entspricht, je nachdem, was kleiner ist.
Bildanalyse Seitenverhältnis:Die Auflösung, die am besten zum Ziel und zur 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, wird die am besten unterstützte Auflösung verwendet.
Max. Auflösung:Die maximale Ausgabeauflösung des Kamerageräts im YUV_420_888-Format, die aus StreamConfigurationMap.getOutputSizes() abgerufen wird. Die Zielauflösung ist standardmäßig auf 640 × 480 festgelegt. Wenn Sie eine Auflösung von mehr als 640 × 480 benötigen, müssen Sie setTargetResolution() und setTargetAspectRatio() verwenden, um die am besten geeignete Auflösung aus den unterstützten Auflösungen zu erhalten.
Bilderfassung Seitenverhältnis:Das Seitenverhältnis, das am besten zur Einstellung passt. Interne Oberflächenauflösung.
Standardauflösung:Die höchste verfügbare Auflösung oder die vom Gerät bevorzugte höchste Auflösung, die dem Seitenverhältnis von ImageCapture entspricht.
Maximale Auflösung:Die maximale Ausgabeauflösung der Kamera im JPEG-Format. Verwende StreamConfigurationMap.getOutputSizes(), um sie abzurufen.

Auflösung angeben

Sie können beim Erstellen von Use-Cases 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 für denselben Anwendungsfall nicht gleichzeitig das Zielseitenverhältnis und die Zielauflösung festlegen. Andernfalls wird beim Erstellen des Konfigurationsobjekts ein IllegalArgumentException geworfen.

Geben Sie die Auflösung Size im Koordinatensystem an, nachdem Sie die unterstützten Größen um die Zieldrehung gedreht haben. Beispiel: Für ein Gerät mit natürlicher Hochformatausrichtung und natürlicher Ausrichtung des Ziels, für das ein Hochformatbild angefordert wird, kann 480 × 640 angegeben werden. Für dasselbe Gerät, das um 90 Grad gedreht und auf das Querformat ausgerichtet ist, kann 640 × 480 angegeben werden.

Mit der Zielauflösung wird versucht, eine untere Grenze 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, die von der Kameraimplementierung bestimmt wird.

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

CameraX wendet anhand der Anfragen die am besten geeignete Auflösung an. Wenn das Hauptanliegen darin besteht, das Seitenverhältnis einzuhalten, geben Sie nur setTargetAspectRatio an. CameraX bestimmt dann eine Auflösung, die für das Gerät geeignet ist. Wenn in der App hauptsächlich eine Auflösung angegeben werden muss, um die Bildverarbeitung effizienter zu gestalten (z. B. ein kleines oder mittelgroßes Bild basierend auf der Verarbeitungskapazität des Geräts), verwenden Sie setTargetResolution(Size resolution).

Wenn für Ihre App eine bestimmte Auflösung erforderlich ist, sehen Sie in der Tabelle in createCaptureSession() nach, welche maximalen Auflösungen von den einzelnen Hardwareebenen unterstützt werden. Informationen zu den Auflösungen, die vom aktuellen Gerät unterstützt werden, finden Sie unter StreamConfigurationMap.getOutputSizes(int).

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

Kameraausgabe steuern

Sie können die Kameraausgabe nicht nur für jeden einzelnen Anwendungsfall nach Bedarf konfigurieren, sondern CameraX implementiert auch die folgenden Schnittstellen, um Kamerafunktionen zu unterstützen, die für alle verknüpften Anwendungsfälle gemeinsam sind:

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

Folgende Kamerafunktionen werden von CameraControl unterstützt:

  • Zoom
  • Taschenlampe
  • Fokus und Belichtung (Zum Fokussieren tippen)
  • Belichtungskorrektur

Instanzen von CameraControl und CameraInfo abrufen

Rufe 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 einreichen, nachdem Sie bindToLifecycle() aufgerufen haben. Wenn Sie die Aktivität beenden oder zerstören, die zum Binden der Kamerainstanz verwendet wurde, kann CameraControl keine Vorgänge mehr ausführen und gibt eine fehlgeschlagene ListenableFuture zurück.

Zoom

CameraControl bietet zwei Methoden zum Ändern des Zoomfaktors:

  • Mit setZoomRatio() wird der Zoom durch das Zoomverhältnis festgelegt.

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

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

    Der Vorteil des linearen Zooms besteht darin, dass das Sichtfeld mit Änderungen des Zooms skaliert wird. Das macht sie ideal für die Verwendung in einer Slider-Ansicht.

CameraInfo.getZoomState() gibt LiveData zum aktuellen Zoomstatus zurück. Der Wert ändert sich, wenn die Kamera initialisiert wird oder die Zoomstufe mit setZoomRatio() oder setLinearZoom() festgelegt wird. Durch Aufrufen 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 Umwandlung vornehmen zu müssen.

Mit dem von beiden APIs zurückgegebenen ListenableFuture können Anwendungen benachrichtigt 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 ListenableFuture des vorherigen Zoomvorgangs sofort fehl.

Taschenlampe

Mit CameraControl.enableTorch(boolean) können Sie die Taschenlampe aktivieren oder deaktivieren.

Mit CameraInfo.getTorchState() können Sie den aktuellen Taschenlampenstatus abfragen. Sie können den von CameraInfo.hasFlashUnit() zurückgegebenen Wert prüfen, um festzustellen, ob eine Taschenlampe verfügbar ist. Andernfalls wird durch das Aufrufen von CameraControl.enableTorch(boolean) der zurückgegebene Wert ListenableFuture sofort mit einem Fehler abgeschlossen und der Status der Taschenlampe auf TorchState.OFF gesetzt.

Wenn die Taschenlampe aktiviert ist, bleibt sie unabhängig von der Einstellung „flashMode“ bei der Aufnahme von Fotos und Videos eingeschaltet. Die Taste flashMode in ImageCapture funktioniert nur, wenn die Taschenlampe deaktiviert ist.

Fokus und Belichtung

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

MeteringPoint

Erstellen Sie zuerst eine MeteringPoint mit MeteringPointFactory.createPoint(float x, float y, float size). Ein MeteringPoint steht für einen einzelnen Punkt auf der Kamera Surface. Sie wird in normalisierter Form gespeichert, damit sie leicht in Sensorkoordinaten umgewandelt werden kann, um AF-/AE-/AWB-Regionen anzugeben.

Die Größe von MeteringPoint kann zwischen 0 und 1 liegen, die Standardgröße ist 0,15f. Wenn Sie MeteringPointFactory.createPoint(float x, float y, float size) aufrufen, erstellt CameraX für die angegebene size einen rechteckigen Bereich mit (x, y) in der Mitte.

Der folgende Code veranschaulicht das Erstellen eines MeteringPoint:

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

Um startFocusAndMetering() aufzurufen, müssen Anwendungen einen FocusMeteringAction erstellen, der aus einem oder mehreren MeteringPoints mit optionalen Kombinationen aus den Belichtungsmodi FLAG_AF, FLAG_AE und 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 vorherigen Code zu sehen, nimmt startFocusAndMetering() einen FocusMeteringAction an, der aus einem MeteringPoint für AF/AE/AWB-Messbereiche und einem weiteren Messpunkt nur für AF und AE besteht.

Intern wandelt CameraX sie in Camera2 MeteringRectangles um und setzt die entsprechenden CONTROL_AF_REGIONS-/CONTROL_AE_REGIONS-/CONTROL_AWB_REGIONS-Parameter in der Aufnahmeanfrage.

Da nicht jedes Gerät AF/AE/AWB und mehrere Regionen unterstützt, führt CameraX die FocusMeteringAction nach dem Best-Effort-Prinzip aus. CameraX verwendet die maximal unterstützte Anzahl von Belichtungspunkten in der Reihenfolge, in der sie hinzugefügt wurden. Alle MeteringPoints, die nach Erreichen der maximalen Anzahl hinzugefügt werden, werden ignoriert. Wenn beispielsweise für eine FocusMeteringAction drei Messpunkte auf einer Plattform angegeben werden, die nur zwei unterstützt, werden nur die ersten beiden Messpunkte verwendet. Die letzte MeteringPoint wird von CameraX ignoriert.

Belichtungskorrektur

Die Belichtungskorrektur ist nützlich, wenn die Belichtungswerte (EV) in Anwendungen über das Ausgabeergebnis der automatischen Belichtung (AE) hinaus optimiert werden müssen. Die Werte für die Belichtungskorrektur werden auf die folgende Weise kombiniert, um die erforderliche Belichtung für die aktuellen Bildbedingungen zu ermitteln:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX bietet die Funktion Camera.CameraControl.setExposureCompensationIndex(), um die Belichtungskorrektur als Indexwert festzulegen.

Positive Indexwerte hellen das Bild auf, während negative Werte es abdunkeln. 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 der zurückgegebene ListenableFuture-Wert abgeschlossen, wenn der Wert in der Erfassungsanfrage erfolgreich aktiviert wurde. Wenn der angegebene Index nicht im unterstützten Bereich liegt, führt setExposureCompensationIndex() dazu, dass der zurückgegebene ListenableFuture-Wert sofort mit einem Fehlerergebnis abgeschlossen wird.

CameraX speichert nur die letzte ausstehende setExposureCompensationIndex()-Anfrage. Wenn die Funktion mehrmals aufgerufen wird, bevor die vorherige Anfrage ausgeführt wird, wird sie abgebrochen.

Im folgenden Snippet wird ein Index für die Belichtungskorrektur festgelegt und ein Rückruf registriert, der ausgeführt wird, wenn die Anfrage zur Belichtungsä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 die aktuelle ExposureState ab, einschließlich:

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

Im folgenden Code werden beispielsweise die Einstellungen für eine Belichtung SeekBar mit den 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

  • Beispiel-Apps für CameraX
  • Entwickler-Community

    Android CameraX-Diskussionsgruppe