Konfigurationsoptionen

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

Für den 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();

Neben den Konfigurationsoptionen bieten einige Anwendungsfälle APIs auch, 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 Image-Aufnahme.

CameraXConfig

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 diese Konfigurationen anpassen möchte, 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. Implementiere die Schnittstelle CameraXConfig.Provider in Application und gib das Objekt CameraXConfig in getCameraXConfig() zurück.
  3. Fügen Sie die Klasse Application der Datei AndroidManifest.xml hinzu, wie hier beschrieben.

Das folgende Codebeispiel beschränkt die CameraX-Protokollierung auf Fehlermeldungen:

Kotlin

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

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

Kamerabegrenzer

Beim ersten Aufruf von ProcessCameraProvider.getInstance() listet CameraX die Eigenschaften der auf dem Gerät verfügbaren Kameras auf und fragt diese ab. Da CameraX mit Hardwarekomponenten kommunizieren muss, kann dieser Prozess für jede Kamera, insbesondere auf Low-End-Geräten, sehr viel Zeit in Anspruch nehmen. 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-Element eine Kamera herausfiltert, verhält sich CameraX so, als wäre diese Kamera nicht vorhanden. Der folgende Code schränkt die Anwendung beispielsweise so ein, 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()
   }
}

Unterhaltungen

Viele der Plattform-APIs, auf denen CameraX basiert, erfordern eine Blockierung der Interprozesskommunikation (IPC) mit Hardware, die manchmal Hunderte von Millisekunden in Anspruch nehmen kann. 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. Einige Anwendungen erfordern jedoch eine strenge Kontrolle von Threads. Mit CameraXConfig kann eine Anwendung die Hintergrundthreads festlegen, die über CameraXConfig.Builder.setCameraExecutor() und CameraXConfig.Builder.setSchedulerHandler() verwendet werden.

Kamera-Executor

Der Camera Executor wird für alle API-Aufrufe der internen Kameraplattform sowie für Callbacks von diesen APIs verwendet. CameraX weist ein internes Executor zu und verwaltet es für diese Aufgaben. Wenn Ihre Anwendung jedoch eine strengere Steuerung von Threads erfordert, verwenden Sie CameraXConfig.Builder.setCameraExecutor().

Planer-Handler

Mit dem Planer-Handler werden interne Aufgaben in festen Intervallen geplant, z. B. wird versucht, die Kamera zu öffnen, wenn sie nicht verfügbar ist. Dieser Handler führt keine Jobs aus, sondern leitet sie nur an den Kamera-Executor weiter. Es wird manchmal auch auf den Legacy-API-Plattformen verwendet, die Handler für Callbacks erfordern. In diesen Fällen werden die Callbacks nur direkt an den Camera-Executor weitergeleitet. CameraX weist ein internes HandlerThread für diese Aufgaben zu und verwaltet es. Sie können es jedoch mit CameraXConfig.Builder.setSchedulerHandler() überschreiben.

Protokollierung

Mit der CameraX-Protokollierung können Anwendungen Logcat-Nachrichten filtern, da es sich bewährt hat, ausführliche Meldungen in Ihrem Produktionscode zu vermeiden. CameraX unterstützt vier Protokollierungsstufen, 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 geeignete Logging-Ebene für Ihre Anwendung festzulegen.

Automatische Auswahl

CameraX bietet automatisch gerätespezifische Funktionen. Beispielsweise ermittelt CameraX automatisch die beste Auflösung, wenn du keine Auflösung angibst oder die von dir angegebene Auflösung nicht unterstützt wird. All dies wird von der Bibliothek verarbeitet, sodass Sie keinen gerätespezifischen Code schreiben müssen.

Das Ziel von CameraX ist es, eine Kamerasitzung erfolgreich zu initialisieren. Das bedeutet, dass bei CameraX die Auflösung und das Seitenverhältnis je nach Gerätefunktion beeinträchtigt werden. 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 richtig zu funktionieren.
  • Auf einigen Geräten sind bestimmte Formate nur in bestimmten Seitenverhältnissen verfügbar.
  • Das Gerät bevorzugt für JPEG- oder Videocodierungen die nächste mod16-Version. Weitere Informationen finden Sie unter SCALER_STREAM_CONFIGURATION_MAP.

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

Drehung

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

Ihre App kann die Zielrotation mithilfe von Konfigurationseinstellungen festlegen. Anschließend können die Rotationseinstellungen mithilfe der Methoden aus den Anwendungsfall-APIs (z. B. ImageAnalysis.setTargetRotation()) aktualisiert werden, auch wenn der Lebenszyklus ausgeführt wird. Sie können diese Option verwenden, wenn die App auf das Hochformat eingestellt ist und bei der Rotation keine Neukonfiguration stattfindet. Beim Foto- oder Analyseanwendungsfall muss jedoch die aktuelle Gerätedrehung berücksichtigt werden. Beispielsweise kann eine Rotationserkennung erforderlich sein, damit Gesichter richtig für die Gesichtserkennung ausgerichtet sind, oder Fotos werden auf Quer- oder Hochformat eingestellt.

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

Damit Vorschaudaten mit der richtigen Ausrichtung angezeigt werden, können Sie die Metadatenausgabe von Preview.PreviewOutput() verwenden, um Transformationen zu 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 Rotation werden in jedem Anwendungsfall entweder die Bilddaten direkt gedreht oder den Nutzern der nicht gedrehten Bilddaten Rotationsmetadaten bereitgestellt.

  • Vorabversion: Eine Metadatenausgabe wird bereitgestellt, damit die Rotation der Zielauflösung mithilfe von Preview.getTargetRotation() bekannt ist.
  • ImageAnalysis: Die Metadatenausgabe wird bereitgestellt, sodass die Bildzwischenspeicherkoordinaten relativ zu den Anzeigekoordinaten bekannt sind.
  • ImageCapture: Die Metadaten des EXIF-Bilds, der Zwischenspeicher oder beide Zwischenspeicher und 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 Rechteck zuschneiden dem vollständigen Zwischenspeicher. Sie können ihn mit ViewPort und UseCaseGroup anpassen. Durch das Gruppieren von Anwendungsfällen und das Festlegen des Darstellungsbereichs sorgt CameraX dafür, dass der Zuschnitt aller Anwendungsfälle in der Gruppe auf denselben Bereich im Kamerasensor verweist.

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 Zwischenspeicherrechteck, das für Endnutzer sichtbar ist. Anschließend berechnet CameraX anhand der Eigenschaften des Darstellungsbereichs und der angehängten Anwendungsfälle den größtmöglichen Zuschneidebereich. Normalerweise können Sie den Darstellungsbereich basierend auf dem Anwendungsfall der Vorschau konfigurieren, um einen WYSIWYG-Effekt zu erzielen. Eine einfache Möglichkeit zum Abrufen des Darstellungsbereichs ist PreviewView.

Die folgenden Code-Snippets zeigen, wie das Objekt ViewPort 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 Informationen, die die Anwendung von ImageAnalysis und ImageCapture erhält, mit dem überein, was der Endnutzer in PreviewView sieht, vorausgesetzt, der Skalierungstyp von PreviewView ist auf die Standardeinstellung FILL_CENTER festgelegt. Nach Anwendung des Zuschneidebereichs und der Drehung auf den Ausgabezwischenspeicher ist das Bild aus allen Anwendungsfällen dasselbe, allerdings 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 Anwendung aus. Wenn Sie ein anderes Gerät als das für Sie ausgewählte verwenden möchten, haben Sie mehrere Möglichkeiten:

Das folgende Codebeispiel zeigt, wie Sie eine 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. Sie können beispielsweise eine Bindung an eine Front- und Rückkamera herstellen, um Fotos oder Videos aus beiden Perspektiven gleichzeitig aufzunehmen.

Wenn die Funktion „Gleichzeitige Kamera“ verwendet wird, kann das Gerät zwei Kameras mit unterschiedlichen Objektiven gleichzeitig oder zwei Rückkameras gleichzeitig bedienen. Der folgende Codeblock zeigt, wie zwei Kameras beim Aufrufen von bindToLifecycle eingerichtet werden und wie beide Kameraobjekte aus dem zurückgegebenen ConcurrentCamera-Objekt abgerufen werden.

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

Du kannst KameraX die Bildauflösung anhand einer Kombination aus Gerätefunktionen, unterstützter Hardware, Anwendungsfall und bereitgestelltem 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 anhand der in cameraProcessProvider.bindToLifecycle() angegebenen Anwendungsfälle automatisch die besten Auflösungseinstellungen ermitteln. Geben Sie nach Möglichkeit alle Anwendungsfälle an, die gleichzeitig in einer einzelnen Sitzung in einem einzigen bindToLifecycle()-Aufruf ausgeführt werden sollen. CameraX bestimmt die Auflösungen anhand der Gruppe von Anwendungsfällen, die auf der unterstützten Hardwareebene des Geräts festgelegt sind. Außerdem werden gerätespezifische Abweichungen berücksichtigt, z. B. wenn ein Gerät die verfügbaren Streamkonfigurationen überschreitet oder nicht erfüllt. Damit soll die Anwendung auf einer Vielzahl von Geräten ausgeführt werden und gleichzeitig gerätespezifische Codepfade minimiert werden.

Das Standardseitenverhältnis für die Bilderfassung und ‐analyse beträgt 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 Ausgabe von CameraX wird so erzeugt, dass sie den vom Gerät unterstützten Seitenverhältnissen möglichst genau entspricht. Wenn keine Auflösung für genaue Übereinstimmung unterstützt wird, wird diejenige ausgewählt, die die meisten Bedingungen erfüllt. Die Anwendung gibt also vor, wie die Kamera in der App angezeigt wird, und CameraX ermittelt die beste Kameraauflösung, um dies auf verschiedenen Geräten zu erreichen.

Eine App kann beispielsweise Folgendes tun:

  • 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 die beste Übereinstimmung mit
  • Seitenverhältnis für den Zuschnitt für ImageCapture angeben

CameraX wählt die Oberflächenauflösungen von Camera2 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 am besten zum Ziel passt. Interne Oberflächenauflösung. Metadaten werden bereitgestellt, damit eine Ansicht das Seitenverhältnis entsprechend dem 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 1080p (1920 × 1080), je nachdem, welcher Wert kleiner ist.
Bildanalyse Seitenverhältnis:Die Auflösung, die am besten zum Ziel passt. Interne Oberflächenauflösung.
Standardauflösung:Die Standardeinstellung für die Zielauflösung ist 640 x 480. Wenn du sowohl die Zielauflösung als auch das entsprechende Seitenverhältnis anpasst, wird die Auflösung am besten unterstützt.
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 640 × 480. Wenn du also eine höhere Auflösung als 640 × 480 möchtest, musst du setTargetResolution() und setTargetAspectRatio() verwenden, um die nächstgelegene Auflösung zu erhalten.
Bildaufnahme Seitenverhältnis:Das am besten zur Einstellung passende Seitenverhältnis. Interne Oberflächenauflösung.
Standardauflösung: Höchste verfügbare 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 StreamConfigurationMap.getOutputSizes(), um diese abzurufen.

Auflösung angeben

Beim Erstellen von Anwendungsfällen können Sie 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();

Es ist nicht möglich, für denselben Anwendungsfall sowohl das Zielseitenverhältnis als auch die Zielauflösung festzulegen. Dadurch wird beim Erstellen des Konfigurationsobjekts ein IllegalArgumentException ausgelöst.

Geben Sie die Auflösung Size im Koordinaten-Frame an, nachdem Sie die unterstützten Größen durch die Zieldrehung gedreht haben. Ein Gerät mit natürlicher Hochformatausrichtung in natürlicher Zielrotation, das ein Hochformatbild anfordert, kann beispielsweise 480 × 640 angeben. Auf demselben Gerät kann für eine 90-Grad-Drehung und für das Querformat 640 × 480 festgelegt werden.

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

Ist jedoch keine Auflösung vorhanden, die gleich oder größer als die Zielauflösung ist, wird die nächstgelegene verfügbare Auflösung ausgewählt, die kleiner als die Zielauflösung ist. Auflösungen, die mit dem angegebenen Seitenverhältnis für Size übereinstimmen, haben eine höhere Priorität als Auflösungen mit anderen Seitenverhältnissen.

CameraX wendet je nach den Anforderungen die am besten geeignete Auflösung an. Wenn das Seitenverhältnis in erster Linie wichtig ist, geben Sie nur setTargetAspectRatio an und CameraX bestimmt eine bestimmte Auflösung, die auf dem Gerät geeignet ist. 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 Geräteverarbeitungsfunktion), verwenden Sie setTargetResolution(Size resolution).

Wenn für Ihre Anwendung eine genaue Auflösung erforderlich ist, sehen Sie in der Tabelle unter createCaptureSession() nach, welche maximalen Auflösungen auf den einzelnen Hardwareebenen unterstützt werden. Die spezifischen Auflösungen, die vom aktuellen Gerät unterstützt werden, findest du unter StreamConfigurationMap.getOutputSizes(int).

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

Kameraausgabe steuern

Mit CameraX können Sie nicht nur die Kameraausgabe für jeden einzelnen Anwendungsfall entsprechend konfigurieren, sondern auch die folgenden Schnittstellen, um Kameravorgänge für alle gebundenen Anwendungsfälle zu unterstützen:

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

Folgende Kamerafunktionen werden von CameraControl unterstützt:

  • Zoom
  • Fackel
  • Fokus und Belichtungsmessung (Zum Fokussieren tippen)
  • Belichtungskorrektur

Instanzen von CameraControl und CameraInfo abrufen

Rufen Sie die 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 senden, nachdem Sie bindToLifecycle() aufgerufen haben. Nachdem Sie die zum Binden der Kamerainstanz verwendete Aktivität 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 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 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 eignet er sich ideal für die Verwendung mit einer Slider-Ansicht.

CameraInfo.getZoomState() gibt LiveData 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 für ZoomState.getZoomRatio() und ZoomState.getLinearZoom() festgelegt. Dies ist hilfreich, wenn Sie Text für das Zoomverhältnis neben einem Schieberegler anzeigen lassen möchten. Achten Sie einfach auf ZoomState LiveData, um beide zu aktualisieren, ohne eine Conversion ausführen zu müssen.

Mit der von beiden APIs zurückgegebenen ListenableFuture können Anwendungen benachrichtigt werden, wenn eine wiederkehrende Anfrage mit dem angegebenen Zoomwert abgeschlossen ist. Wenn Sie einen neuen Zoomwert festlegen, während der vorherige Vorgang noch ausgeführt wird, schlägt der ListenableFuture des vorherigen Zoomvorgangs sofort fehl.

Fackel

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

Mit CameraInfo.getTorchState() kann der aktuelle Taschenlampenstatus abgefragt werden. Sie können den von CameraInfo.hasFlashUnit() zurückgegebenen Wert prüfen, um festzustellen, ob eine Taschenlampe verfügbar ist. Wenn nicht, führt das Aufrufen von CameraControl.enableTorch(boolean) dazu, dass die zurückgegebene ListenableFuture sofort mit einem fehlgeschlagenen Ergebnis abgeschlossen wird und der Fackelstatus auf TorchState.OFF gesetzt wird.

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

Fokus und Belichtung

CameraControl.startFocusAndMetering() löst die Autofokus- und Belichtungsmessung aus, indem AF/AE/AWB-Messbereiche basierend auf der jeweiligen FocusMeteringAction festgelegt werden. Damit wird in vielen Kameraanwendungen die Funktion „Zum Fokussieren tippen“ implementiert.

Messpunkt

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

Die Größe von 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 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 eine FocusMeteringAction erstellen, die aus einem oder mehreren MeteringPoints mit optionalen Kombinationen des Messmodus aus FLAG_AF, FLAG_AE, FLAG_AWB besteht. Der folgende Code veranschaulicht diese Verwendung:

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 gezeigt, verwendet startFocusAndMetering() einen FocusMeteringAction, bestehend aus einer MeteringPoint für die AF/AE/AWB-Messregionen und einem weiteren MeteringPoint nur für AF und AE.

KameraX wandelt ihn intern in Kamera 2 MeteringRectangles um und legt die entsprechenden CONTROL_AF_REGIONS-/CONTROL_AE_REGIONS-/CONTROL_AWB_REGIONS-Parameter für die Aufnahmeanfrage fest.

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

Belichtungskorrektur

Die Belichtungskorrektur ist nützlich, wenn Anwendungen Belichtungswerte (LW) über das Ausgabeergebnis der automatischen Belichtung (AE) hinaus anpassen müssen. Belichtungskorrekturwerte werden so kombiniert, um die erforderliche Belichtung für aktuelle Bildbedingungen zu bestimmen:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

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

Positive Indexwerte machen das Bild heller, während negative Werte das Bild dimmen. Anwendungen können den unterstützten Bereich von CameraInfo.ExposureState.exposureCompensationRange() abfragen, wie im nächsten Abschnitt beschrieben. 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, führt setExposureCompensationIndex() dazu, 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, wird sie abgebrochen.

Das folgende Snippet legt einen Belichtungskompensationsindex fest und registriert einen Callback für den Fall, dass 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 den aktuellen ExposureState ab, einschließlich:

    • Die Unterstützung für die Belichtungskorrektur.
    • Der aktuelle Belichtungskompensationsindex.
    • Der Belichtungskompensationsindexbereich.
    • Der zur Berechnung des Belichtungskompensationswerts verwendete Belichtungskorrekturschritt.

Mit dem folgenden Code werden beispielsweise die Einstellungen für den Kontakt 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
}

Zusätzliche Ressourcen

Weitere Informationen zu CameraX finden Sie in den folgenden Ressourcen.

Codelab

  • Erste Schritte mit CameraX
  • Codebeispiel

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

    Diskussionsgruppe zu Android CameraX