Konfigurationsoptionen

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

Beim Anwendungsfall „Bildaufnahme“ 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 bieten einige Anwendungsfälle APIs, mit denen Einstellungen nach der Erstellung 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 Bilder aufnehmen.

CameraXConfig

Zur Vereinfachung bietet 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 entsprechende Schnittstelle.

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 CameraXConfig.Provider-Schnittstelle in Ihrem Application und geben Sie Ihr CameraXConfig-Objekt in getCameraXConfig() zurück.
  3. Fügen Sie die Klasse Application zu Ihrer Datei AndroidManifest.xml hinzu, wie hier beschrieben.

Das folgende Codebeispiel beschränkt das CameraX-Logging beispielsweise auf Fehlermeldungen:

Kotlin

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

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

Kamerabegrenzung

Beim ersten Aufruf von ProcessCameraProvider.getInstance()> werden die auf dem Gerät verfügbaren Kameras von CameraX aufgelistet und ihre Eigenschaften abgefragt. Da CameraX mit Hardwarekomponenten kommunizieren muss, kann dieser Vorgang für jede Kamera eine beträchtliche Zeit in Anspruch nehmen, 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 Startlatenz 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 ob diese Kamera nicht vorhanden wäre. Mit dem folgenden Code wird die Anwendung beispielsweise darauf beschränkt, nur die Standardkamera auf der Rückseite des Geräts zu verwenden:

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 blockierende Interprozesskommunikation (IPC) mit Hardware, deren Antwort manchmal Hunderte von Millisekunden dauern kann. 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 Hintergrund-Threads intern, sodass dieses Verhalten transparent erscheint. Bei einigen Anwendungen ist jedoch eine strenge Steuerung von Threads erforderlich. Mit CameraXConfig kann eine Anwendung die Hintergrund-Threads 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 Kameraplattform sowie für Callbacks von diesen APIs verwendet. CameraX weist einen internen Executor zu und verwaltet ihn, um diese Aufgaben auszuführen. Wenn Ihre Anwendung jedoch eine strengere Steuerung von Threads erfordert, verwenden Sie CameraXConfig.Builder.setCameraExecutor().

Scheduler-Handler

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

Protokollierung

Mit CameraX-Logging können Anwendungen logcat-Nachrichten filtern, da es sinnvoll sein kann, ausführliche Nachrichten in Ihrem 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 Protokollebenen finden Sie in der Android-Protokolldokumentation. Verwenden Sie CameraXConfig.Builder.setMinimumLoggingLevel(int), um die entsprechende Logging-Ebene für Ihre Anwendung festzulegen.

Automatische Auswahl

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

Ziel von CameraX ist es, eine Kamerasitzung erfolgreich zu initialisieren. Das bedeutet, dass CameraX die Auflösung und das Seitenverhältnis basierend auf den Gerätefunktionen anpasst. 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 benötigen, um richtig zu funktionieren.
  • Auf einigen Geräten sind bestimmte Formate nur bei bestimmten Seitenverhältnissen verfügbar.
  • Das Gerät bevorzugt „nearest 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 die zurückgegebenen Bildgrößen in der Ausgabe des Anwendungsfalls in Ihrem Code immer prüfen und entsprechend anpassen.

Ausrichtung

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

In Ihrer App kann die Zielrotation über Konfigurationseinstellungen festgelegt werden. Anschließend können die Rotations-Einstellungen mit den Methoden der APIs für Anwendungsfälle (z. B. ImageAnalysis.setTargetRotation()) aktualisiert werden, auch wenn sich der Lebenszyklus im Status „Wird ausgeführt“ befindet. Dies kann nützlich sein, wenn die App im Hochformat gesperrt ist und daher bei einer Drehung keine Neukonfiguration erfolgt, der Anwendungsfall für Fotos oder Analysen jedoch die aktuelle Ausrichtung des Geräts berücksichtigen muss. Beispielsweise kann es erforderlich sein, dass die Rotation berücksichtigt wird, 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 Rotationsinformationen, damit Galerieanwendungen das Bild nach dem Speichern in der richtigen Ausrichtung anzeigen können.

Um Vorschaudaten mit der richtigen Ausrichtung anzuzeigen, können Sie die Metadaten verwenden, die von Preview.PreviewOutput() ausgegeben werden, 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 eingestellter Drehung wird in jedem Anwendungsfall entweder die Bilddatendrehung direkt durchgeführt oder es werden Rotationsmetadaten für die Nutzer der nicht gedrehten Bilddaten bereitgestellt.

  • Vorschau: Die Metadaten werden ausgegeben, damit die Drehung der Zielauflösung mit Preview.getTargetRotation() bekannt ist.
  • ImageAnalysis: Es werden Metadaten ausgegeben, damit die Koordinaten des Bildpuffers relativ zu den Koordinaten des Displays 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.

Zuschneidebereich

Standardmäßig ist das Zuschneiderechteck das vollständige Pufferrechteck. Sie können es mit ViewPort und UseCaseGroup anpassen. Durch das Gruppieren von Anwendungsfällen und das Festlegen des Viewports 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);

Mit ViewPort wird das für Endnutzer sichtbare Rechteck für den Puffer definiert. Anschließend berechnet CameraX das größtmögliche Zuschneiderechteck basierend auf den Eigenschaften des Viewports und den angehängten Anwendungsfällen. Normalerweise können Sie den Darstellungsbereich entsprechend dem Vorschau-Anwendungsfall 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 Skalentyp 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 dasselbe, wenn auch 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 Anwendung aus. Wenn Sie ein anderes Gerät als das für Sie ausgewählte verwenden möchten, haben Sie folgende Möglichkeiten:

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

Kotlin

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

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

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

Mehrere Kameras gleichzeitig auswählen

Ab CameraX 1.3 können Sie auch mehrere Kameras gleichzeitig auswählen. Sie können beispielsweise eine Verbindung zu einer Front- und einer Rückkamera herstellen, um gleichzeitig Fotos oder Videos aus beiden Perspektiven aufzunehmen.

Wenn Sie die Funktion „Gleichzeitige Kamera“ verwenden, kann das Gerät zwei Kameras mit unterschiedlich ausgerichteten Objektiven gleichzeitig 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 die Bildauflösung von CameraX auf Grundlage einer Kombination aus den Gerätefunktionen, dem unterstützten Hardware-Level des Geräts, dem Anwendungsfall und dem bereitgestellten 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 an, die gleichzeitig in einer einzelnen Sitzung in einem einzelnen bindToLifecycle()-Aufruf ausgeführt werden müssen. CameraX bestimmt die Auflösungen anhand der Gruppe von Anwendungsfällen, die durch die Berücksichtigung der unterstützten Hardwareebene des Geräts und durch die Berücksichtigung gerätespezifischer Abweichungen (wenn ein Gerät die verfügbaren Streamkonfigurationen überschreitet oder nicht erfüllt) gebunden sind. 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 zur Bilderfassung und ‑analyse 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 erstellt, dass sie so gut wie möglich den angeforderten Seitenverhältnissen entspricht, soweit das Gerät dies unterstützt. Wenn keine Auflösung mit exakter Ü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 bestimmt 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
  • Geben Sie eine benutzerdefinierte Auflösung an, für die CameraX versucht, die nächstbeste Übereinstimmung zu finden.
  • Geben Sie ein Seitenverhältnis für den Zuschneidebereich für ImageCapture an.

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 Innenfläche Auflösung der Ausgabedaten
Vorschau Seitenverhältnis:Die Auflösung, die am besten zur Einstellung des Ziels passt. Auflösung der inneren Oberfläche. Metadaten werden bereitgestellt, damit eine Ansicht für das Zielseitenverhältnis zugeschnitten, skaliert und gedreht werden kann.
Standardauflösung:Die höchste Vorschauauflösung oder die höchste vom Gerät bevorzugte Auflösung, die dem Seitenverhältnis der Vorschau entspricht.
Maximale Auflösung:Vorschaugröße, 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 der Einstellung passt. Auflösung der inneren Oberfläche.
Standardauflösung:Die Standardeinstellung für die Zielauflösung ist 640 × 480. Wenn Sie sowohl die Zielauflösung als auch das entsprechende Seitenverhältnis anpassen, erhalten Sie die am besten unterstützte Auflösung.
Maximale Auflösung:Die maximale Ausgaberesolution 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 höhere Auflösung als 640 × 480 wünschen, müssen Sie setTargetResolution() und setTargetAspectRatio() verwenden, um die nächstgelegene der unterstützten Auflösungen zu erhalten.
Bildaufnahme Seitenverhältnis:Das Seitenverhältnis, das am besten zur Einstellung passt. Auflösung der inneren Oberfläche.
Standardauflösung:Die höchste verfügbare Auflösung oder die höchste vom Gerät bevorzugte Auflösung, die dem Seitenverhältnis des ImageCapture entspricht.
Maximale Auflösung:Die maximale Ausgaberesolution 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 gleichzeitig das Ziel-Seitenverhältnis und die Zielauflösung für denselben Anwendungsfall festlegen. Andernfalls wird beim Erstellen des Konfigurationsobjekts eine IllegalArgumentException ausgelöst.

Gibt die Auflösung Size im Koordinatensystem an, nachdem die unterstützten Größen um die Zielrotation gedreht wurden. Ein Gerät mit natürlicher Ausrichtung im Hochformat, das eine natürliche Zielrotation anfordert und ein Bild im Hochformat benötigt, kann beispielsweise 480 × 640 angeben. Dasselbe Gerät, das um 90 Grad gedreht wird und auf das Querformat ausgerichtet ist, kann 640 × 480 angeben.

Mit der Zielauflösung wird versucht, eine Untergrenze 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. Sie wird durch die Kameraimplementierung bestimmt.

Wenn es jedoch keine Auflösung gibt, die der Zielauflösung entspricht oder größer als die Zielauflösung ist, wird die nächstbeste verfügbare Auflösung ausgewählt, die kleiner als die Zielauflösung ist. Auflösungen mit demselben Seitenverhältnis wie das 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 Verarbeitungskapazität des Geräts), verwenden Sie setTargetResolution(Size resolution).

Wenn für Ihre App eine bestimmte Auflösung erforderlich ist, 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 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 überprüfen.

Kameraausgabe steuern

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

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

Folgende Kamerafunktionen werden mit CameraControl unterstützt:

  • Zoom
  • Taschenlampe
  • Fokus und Belichtungsmessung (Fokus durch 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 Aufrufen von bindToLifecycle() senden. Nachdem Sie die Aktivität beendet oder zerstört haben, die zum Binden der Kamera-Instanz verwendet wurde, kann CameraControl keine Vorgänge mehr ausführen und gibt ein fehlgeschlagenes ListenableFuture zurück.

Zoom

CameraControl bietet zwei Methoden zum Ändern des Zoomfaktors:

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

    Das Verhältnis muss zwischen CameraInfo.getZoomState().getValue().getMinZoomRatio() und CameraInfo.getZoomState().getValue().getMaxZoomRatio() liegen. Andernfalls gibt die Funktion ein fehlgeschlagenes 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 mit Änderungen des Zooms skaliert wird. Daher ist sie 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 wird oder wenn 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 dass eine Umstellung erforderlich ist.

Die von beiden APIs zurückgegebene ListenableFuture bietet die Möglichkeit, Anwendungen zu benachrichtigen, wenn eine wiederholte 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 die ListenableFuture des vorherigen Zoomvorgangs sofort fehl.

Taschenlampe

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

Mit CameraInfo.getTorchState() lässt sich der aktuelle Status der Taschenlampe abfragen. Sie können den von CameraInfo.hasFlashUnit() zurückgegebenen Wert prüfen, um festzustellen, ob eine Taschenlampe verfügbar ist. Andernfalls führt der Aufruf von CameraControl.enableTorch(boolean) dazu, dass die zurückgegebene ListenableFuture sofort mit einem fehlgeschlagenen Ergebnis abgeschlossen wird und der Taschenlampenstatus auf TorchState.OFF gesetzt wird.

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

Fokus und Messung

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

MeteringPoint

Erstellen Sie zuerst ein MeteringPoint mit MeteringPointFactory.createPoint(float x, float y, float size). Ein MeteringPoint stellt einen einzelnen Punkt auf der Kamera Surface dar. Sie wird in einer normalisierten Form gespeichert, sodass sie einfach in Sensorkoordinaten umgewandelt werden kann, um AF-/AE-/AWB-Bereiche anzugeben.

Die Größe von MeteringPoint liegt zwischen 0 und 1, wobei die Standardgröße 0,15 f ist. Beim Aufrufen von MeteringPointFactory.createPoint(float x, float y, float size) erstellt CameraX für das angegebene size eine rechteckige Region, die auf (x, y) zentriert ist.

Der folgende Code zeigt, wie Sie ein MeteringPoint erstellen:

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 ein FocusMeteringAction erstellen, das aus einem oder mehreren MeteringPoints mit optionalen Kombinationen aus Messmodi aus FLAG_AF, FLAG_AE und 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 zu sehen ist, verwendet startFocusAndMetering() ein FocusMeteringAction, das aus einem MeteringPoint für AF-/AE-/AWB-Messbereiche und einem weiteren MeteringPoint nur für AF und AE besteht.

Intern wird sie von CameraX in Camera2 MeteringRectangles konvertiert und die entsprechenden CONTROL_AF_REGIONS-/ CONTROL_AE_REGIONS-/ CONTROL_AWB_REGIONS-Parameter werden für die Aufnahmeanfrage festgelegt.

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 MeteringPoints in der Reihenfolge, in der die Punkte hinzugefügt wurden. Alle nach der maximalen Anzahl hinzugefügten MeteringPoints werden ignoriert. Wenn beispielsweise für ein FocusMeteringAction drei MeteringPoints auf einer Plattform angegeben werden, die nur zwei unterstützt, werden nur die ersten beiden MeteringPoints verwendet. Das endgültige MeteringPoint wird von CameraX ignoriert.

Belichtungskorrektur

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

Exposure = ExposureCompensationIndex * ExposureCompensationStep

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

Positive Indexwerte machen das Bild heller, negative Werte dunkler. 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 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 der zurückgegebene ListenableFuture sofort mit einem Fehler abgeschlossen wird.

CameraX behält nur die letzte ausstehende setExposureCompensationIndex()-Anfrage bei. 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 Callback registriert, der aufgerufen 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:

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

Im folgenden Code werden beispielsweise die Einstellungen für eine SeekBar-Einblendung mit aktuellen ExposureState-Werten initialisiert:

Kotlin

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

Zusätzliche Ressourcen

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

Codelab

  • Erste Schritte mit CameraX
  • Codebeispiel

  • CameraX-Beispiel-Apps
  • Entwickler-Community

    Android CameraX-Diskussionsgruppe