Rotationen der CameraX-Anwendungsfälle

In diesem Artikel erfahren Sie, wie Sie CameraX-Anwendungsfälle in Ihrer App einrichten, um Bilder mit den richtigen Rotationsinformationen zu erhalten, sei es aus dem Anwendungsfall ImageAnalysis oder ImageCapture. Also:

  • Das Analyzer des Anwendungsfalls ImageAnalysis sollte Frames mit der richtigen Rotation erhalten.
  • Der Anwendungsfall ImageCapture sollte Bilder mit der richtigen Drehung aufnehmen.

Terminologie

In diesem Thema wird die folgende Terminologie verwendet, daher ist es wichtig, die Bedeutung der einzelnen Begriffe zu verstehen:

Bildschirmausrichtung
Hier wird angegeben, welche Seite des Geräts nach oben ist. Dies kann einer der vier Werte sein: Hochformat, Querformat, umgekehrtes Hochformat oder umgekehrtes Querformat.
Displaydrehung
Dieser Wert wird von Display.getRotation() zurückgegeben. Er stellt die Grad dar, um die das Gerät aus seiner natürlichen Ausrichtung gegen den Uhrzeigersinn gedreht wird.
Zielrotation
Dies steht für die Anzahl der Grad, um die das Gerät im Uhrzeigersinn gedreht werden soll, um seine natürliche Ausrichtung zu erreichen.

So legen Sie die Zielrotation fest

Die folgenden Beispiele zeigen, wie die Zieldrehung für ein Gerät anhand seiner natürlichen Ausrichtung bestimmt wird.

Beispiel 1: Natürliche Ausrichtung im Hochformat

Gerätebeispiel: Pixel 3 XL

Natürliche Ausrichtung = Hochformat
Aktuelle Ausrichtung = Hochformat

Displaydrehung = 0
Zielrotation = 0

Natürliche Ausrichtung = Hochformat
Aktuelle Ausrichtung = Querformat

Displaydrehung = 90
Zielrotation = 90

Beispiel 2: Natürliche Ausrichtung im Querformat

Gerätebeispiel: Pixel C

Natürliche Ausrichtung = Querformat
Aktuelle Ausrichtung = Querformat

Displaydrehung = 0
Zielrotation = 0

Natürliche Ausrichtung = Querformat
Aktuelle Ausrichtung = Hochformat

Displayrotation = 270
Zielrotation = 270

Bilddrehung

Welches Ende ist angekommen? Die Sensorausrichtung wird in Android als konstanter Wert definiert, der die Grade (0, 90, 180, 270) angibt, die der Sensor vom oberen Rand des Geräts gedreht wird, wenn sich das Gerät in einer natürlichen Position befindet. Für alle Fälle in den Diagrammen beschreibt die Bilddrehung, wie die Daten im Uhrzeigersinn gedreht werden sollten, um sie aufrecht anzuzeigen.

Die folgenden Beispiele zeigen, wie die Bilddrehung je nach Ausrichtung des Kamerasensors aussehen sollte. Außerdem wird davon ausgegangen, dass die Zielrotation auf „Displayrotation“ festgelegt ist.

Beispiel 1: Sensor um 90 Grad gedreht

Gerätebeispiel: Pixel 3 XL

Displaydrehung = 0
Displayausrichtung = Hochformat
Bilddrehung = 90

Displaydrehung = 90
Displayausrichtung = Querformat
Bilddrehung = 0

Beispiel 2: Sensor um 270 Grad gedreht

Gerätebeispiel: Nexus 5X

Displaydrehung = 0
Displayausrichtung = Hochformat
Bilddrehung = 270

Displaydrehung = 90
Displayausrichtung = Querformat
Bilddrehung = 180

Beispiel 3: Sensor um 0 Grad gedreht

Gerätebeispiel: Pixel C (Tablet)

Displaydrehung = 0
Displayausrichtung = Querformat
Bilddrehung = 0

Displaydrehung = 270
Displayausrichtung = Hochformat
Bilddrehung = 90

Rotation eines Bilds berechnen

Bildanalyse

Das Analyzer von ImageAnalysis empfängt Bilder von der Kamera in Form von ImageProxys. Jedes Bild enthält Rotationsinformationen, auf die über Folgendes zugegriffen werden kann:

val rotation = imageProxy.imageInfo.rotationDegrees

Dieser Wert gibt die Grad an, um die das Bild im Uhrzeigersinn gedreht werden muss, damit es der Zieldrehung von ImageAnalysis entspricht. Bei einer Android-App entspricht die Zielrotation von ImageAnalysis in der Regel der Bildschirmausrichtung.

Bilderfassung

Ein Callback wird an eine ImageCapture-Instanz angehängt, um zu signalisieren, dass ein Erfassungsergebnis verfügbar ist. Das Ergebnis kann entweder das aufgenommene Bild oder ein Fehler sein.

Beim Aufnehmen eines Bildes kann der bereitgestellte Callback einen der folgenden Typen haben:

  • OnImageCapturedCallback:Erhält ein Image mit speicherinternem Zugriff in Form eines ImageProxy.
  • OnImageSavedCallback:Wird aufgerufen, wenn das aufgenommene Bild erfolgreich an dem in ImageCapture.OutputFileOptions angegebenen Ort gespeichert wurde. Die Optionen können einen File, OutputStream oder einen Standort in MediaStore angeben.

Die Drehung des aufgenommenen Bildes unabhängig von seinem Format (ImageProxy, File, OutputStream, MediaStore Uri) gibt die Grad der Drehung an, um die das aufgenommene Bild im Uhrzeigersinn gedreht werden muss, um der Zieldrehung von ImageCapture zu entsprechen, die auch im Kontext einer Android-App normalerweise der Bildschirmausrichtung entspricht.

Die Rotation des aufgenommenen Bildes kann auf eine der folgenden Arten abgerufen werden:

ImageProxy

val rotation = imageProxy.imageInfo.rotationDegrees

File

val exif = Exif.createFromFile(file)
val rotation = exif.rotation

OutputStream

val byteArray = outputStream.toByteArray()
val exif = Exif.createFromInputStream(ByteArrayInputStream(byteArray))
val rotation = exif.rotation

MediaStore uri

val inputStream = contentResolver.openInputStream(outputFileResults.savedUri)
val exif = Exif.createFromInputStream(inputStream)
val rotation = exif.rotation

Drehen eines Bildes prüfen

Bei den Anwendungsfällen ImageAnalysis und ImageCapture werden nach einer erfolgreichen Erfassungsanfrage ImageProxys von der Kamera empfangen. Ein ImageProxy umschließt ein Bild mit Informationen über es, einschließlich seiner Drehung. Diese Rotationsinformationen geben die Grad an, um die das Bild gedreht werden muss, damit es der Zieldrehung des Anwendungsfalls entspricht.

Überprüfung der Bilddrehung

Richtlinien für die Rotation von ImageCapture-/ImageAnalysis-Zielen

Da viele Geräte nicht standardmäßig das Hoch- oder Querformat umkehren, unterstützen einige Android-Apps diese Ausrichtungen nicht. Ob eine App dies unterstützt oder nicht, ändert die Art und Weise, wie die Zielrotation der Anwendungsfälle aktualisiert werden kann.

Unten sehen Sie zwei Tabellen, die definieren, wie die Zielrotation der Anwendungsfälle mit der Displayrotation synchron gehalten wird. Die erste zeigt, wie das geht, wobei alle vier Ausrichtungen unterstützt werden. Die zweite behandelt nur die Ausrichtungen, zu denen das Gerät standardmäßig gedreht wird.

So wählen Sie die Richtlinien für Ihre App aus:

  1. Prüfe, ob die Kamera (Activity) deiner App gesperrt oder entsperrt ist oder ob sie Änderungen der Ausrichtungskonfiguration überschreibt.

  2. Lege fest, ob die Kamera Activity deiner App alle vier Geräteausrichtungen (Hochformat, umgekehrtes Hochformat, Querformat und umgekehrtes Querformat) verarbeiten soll oder ob sie nur Ausrichtungen verarbeiten soll, auf denen das Gerät standardmäßig unterstützt wird.

Unterstützung für alle vier Ausrichtungen

In dieser Tabelle werden bestimmte Richtlinien aufgeführt, die für den Fall gelten, dass das Gerät nicht ins umgekehrte Hochformat gedreht wird. Dasselbe gilt für Geräte, die nicht in das Querformat gedreht werden.

Szenario Vorgaben Einzelfenstermodus Splitscreen-Modus im Mehrfenstermodus
Ausrichtung ohne SIM-Lock Richte die Anwendungsfälle jedes Mal ein, wenn die Activity erstellt wird, z. B. im onCreate()-Callback des Activity.
Verwenden Sie onOrientationChanged() von OrientationEventListener. Aktualisieren Sie im Callback die Zielrotation der Anwendungsfälle. Dadurch wird Activity auch nach einer Änderung der Ausrichtung, z. B. wenn das Gerät um 180 Grad gedreht wurde, nicht neu erstellt. Behandelt auch, wenn sich der Bildschirm im umgekehrten Hochformat befindet und das Gerät nicht standardmäßig in das umgekehrte Hochformat gedreht wird. Auch für Fälle, in denen Activity nicht neu erstellt wird, wenn das Gerät gedreht wird (z. B. um 90 Grad). Dies geschieht auf Geräten mit kleinem Formfaktor, wenn die App den halben Bildschirm einnimmt, und auf größeren Geräten, wenn die App zwei Drittel des Bildschirms einnimmt.
Optional: Setzen Sie die Eigenschaft screenOrientation von Activity in der Datei AndroidManifest auf fullSensor. Auf diese Weise kann die Benutzeroberfläche aufrecht stehen, wenn sich das Gerät im umgekehrten Hochformat befindet. Außerdem kann Activity vom System neu erstellt werden, wenn das Gerät um 90 Grad gedreht wird. Hat keine Auswirkungen auf Geräte, die nicht standardmäßig ins Hochformat drehen. Wenn sich das Display im umgekehrten Hochformat befindet, wird der Mehrfenstermodus nicht unterstützt.
Ausrichtung gesperrt Richten Sie die Anwendungsfälle nur einmal ein, wenn das Activity zum ersten Mal erstellt wird, z. B. im onCreate()-Callback des Activity.
Verwenden Sie onOrientationChanged() von OrientationEventListener. Aktualisieren Sie im Callback die Zielrotation der Anwendungsfälle mit Ausnahme der Vorschau. Auch für Fälle, in denen Activity nicht neu erstellt wird, wenn das Gerät gedreht wird (z. B. um 90 Grad). Dies geschieht auf Geräten mit kleinem Formfaktor, wenn die App den halben Bildschirm einnimmt, und auf größeren Geräten, wenn die App zwei Drittel des Bildschirms einnimmt.
Konfigurationsänderungen für die Ausrichtung überschrieben Richten Sie die Anwendungsfälle nur einmal ein, wenn das Activity zum ersten Mal erstellt wird, z. B. im onCreate()-Callback des Activity.
Verwenden Sie onOrientationChanged() von OrientationEventListener. Aktualisieren Sie im Callback die Zielrotation der Anwendungsfälle. Auch für Fälle, in denen Activity nicht neu erstellt wird, wenn das Gerät gedreht wird (z. B. um 90 Grad). Dies geschieht auf Geräten mit kleinem Formfaktor, wenn die App den halben Bildschirm einnimmt, und auf größeren Geräten, wenn die App zwei Drittel des Bildschirms einnimmt.
Optional: Setze die Eigenschaft „screenOrientation“ der Aktivität in der Datei „AndroidManifest“ auf „fullSensor“. Die Benutzeroberfläche kann aufrecht ausgerichtet sein, wenn sich das Gerät im umgekehrten Hochformat befindet. Hat keine Auswirkungen auf Geräte, die nicht standardmäßig ins Hochformat drehen. Wenn sich das Display im umgekehrten Hochformat befindet, wird der Mehrfenstermodus nicht unterstützt.

Nur von Geräten unterstützte Ausrichtungen unterstützen

Nur Ausrichtungen unterstützen, die das Gerät standardmäßig unterstützt. Dies kann umgekehrtes Hoch- oder umgekehrtes Querformat umfassen.

Szenario Vorgaben Splitscreen-Modus im Mehrfenstermodus
Ausrichtung ohne SIM-Lock Richte die Anwendungsfälle jedes Mal ein, wenn die Activity erstellt wird, z. B. im onCreate()-Callback des Activity.
Verwende onDisplayChanged() von DisplayListener. Aktualisieren Sie im Callback die Zieldrehung der Anwendungsfälle, z. B. wenn das Gerät um 180 Grad gedreht wird. Auch für Fälle, in denen Activity nicht neu erstellt wird, wenn das Gerät gedreht wird (z. B. um 90 Grad). Dies geschieht auf Geräten mit kleinem Formfaktor, wenn die App den halben Bildschirm einnimmt, und auf größeren Geräten, wenn die App zwei Drittel des Bildschirms einnimmt.
Ausrichtung gesperrt Richten Sie die Anwendungsfälle nur einmal ein, wenn das Activity zum ersten Mal erstellt wird, z. B. im onCreate()-Callback des Activity.
Verwenden Sie onOrientationChanged() von OrientationEventListener. Aktualisieren Sie im Callback die Zielrotation der Anwendungsfälle. Auch für Fälle, in denen Activity nicht neu erstellt wird, wenn das Gerät gedreht wird (z. B. um 90 Grad). Dies geschieht auf Geräten mit kleinem Formfaktor, wenn die App den halben Bildschirm einnimmt, und auf größeren Geräten, wenn die App zwei Drittel des Bildschirms einnimmt.
Konfigurationsänderungen für die Ausrichtung überschrieben Richten Sie die Anwendungsfälle nur einmal ein, wenn das Activity zum ersten Mal erstellt wird, z. B. im onCreate()-Callback des Activity.
Verwende onDisplayChanged() von DisplayListener. Aktualisieren Sie im Callback die Zieldrehung der Anwendungsfälle, z. B. wenn das Gerät um 180 Grad gedreht wird. Auch für Fälle, in denen Activity nicht neu erstellt wird, wenn das Gerät gedreht wird (z. B. um 90 Grad). Dies geschieht auf Geräten mit kleinem Formfaktor, wenn die App den halben Bildschirm einnimmt, und auf größeren Geräten, wenn die App zwei Drittel des Bildschirms einnimmt.

Ausrichtung ohne SIM-Lock

Ein Activity hat eine entsperrte Ausrichtung, wenn seine Bildschirmausrichtung (z. B. Hochformat oder Querformat) der physischen Ausrichtung des Geräts entspricht. Eine Ausnahme bildet das umgekehrte Hoch-/Querformat, das von einigen Geräten standardmäßig nicht unterstützt wird. Wenn Sie erzwingen möchten, dass das Gerät in alle vier Ausrichtungen gedreht wird, setzen Sie die Eigenschaft screenOrientation von Activity auf fullSensor.

Im Mehrfenstermodus wird ein Gerät, das das umgekehrte Hoch-/Querformat nicht unterstützt, nicht standardmäßig zum Hoch-/Querformat drehen, selbst wenn die Eigenschaft screenOrientation auf fullSensor gesetzt ist.

<!-- The Activity has an unlocked orientation, but might not rotate to reverse
portrait/landscape in single-window mode if the device doesn't support it by
default. -->
<activity android:name=".UnlockedOrientationActivity" />

<!-- The Activity has an unlocked orientation, and will rotate to all four
orientations in single-window mode. -->
<activity
   android:name=".UnlockedOrientationActivity"
   android:screenOrientation="fullSensor" />

Ausrichtung gesperrt

Die Ausrichtung eines Displays ist gesperrt, wenn es unabhängig von der physischen Ausrichtung des Geräts in derselben Bildschirmausrichtung bleibt (z. B. Hochformat oder Querformat). Dazu kann das Attribut screenOrientation einer Activity in der Deklaration in der Datei AndroidManifest.xml angegeben werden.

Wenn die Ausrichtung des Bildschirms gesperrt ist, löscht das System die Activity nicht und erstellt sie nicht neu, wenn das Gerät gedreht wird.

<!-- The Activity keeps a portrait orientation even as the device rotates. -->
<activity
   android:name=".LockedOrientationActivity"
   android:screenOrientation="portrait" />

Änderungen der Ausrichtungskonfiguration überschrieben

Wenn ein Activity Änderungen der Ausrichtungskonfiguration überschreibt, wird das System nicht gelöscht und neu erstellt, wenn sich die physische Ausrichtung des Geräts ändert. Das System aktualisiert die Benutzeroberfläche jedoch entsprechend der physischen Ausrichtung des Geräts.

<!-- The Activity's UI might not rotate in reverse portrait/landscape if the
device doesn't support it by default. -->
<activity
   android:name=".OrientationConfigChangesOverriddenActivity"
   android:configChanges="orientation|screenSize" />

<!-- The Activity's UI will rotate to all 4 orientations in single-window
mode. -->
<activity
   android:name=".OrientationConfigChangesOverriddenActivity"
   android:configChanges="orientation|screenSize"
   android:screenOrientation="fullSensor" />

Kamera-Anwendungsfälle einrichten

In den oben beschriebenen Szenarien können die Anwendungsfälle für die Kamera eingerichtet werden, wenn die Activity zum ersten Mal erstellt wird.

Bei einem Activity mit entsperrter Ausrichtung erfolgt diese Einrichtung jedes Mal, wenn das Gerät gedreht wird, da das System die Activity löscht und neu erstellt, wenn sich die Ausrichtung ändert. Dies führt dazu, dass die Zieldrehung in den Anwendungsfällen jedes Mal standardmäßig so festgelegt wird, dass sie der Bildschirmausrichtung entspricht.

Im Fall einer Activity mit gesperrter Ausrichtung oder einer, die Änderungen der Ausrichtungskonfiguration überschreibt, wird diese Einrichtung einmalig vorgenommen, wenn die Activity zum ersten Mal erstellt wird.

class CameraActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       val cameraProcessFuture = ProcessCameraProvider.getInstance(this)
       cameraProcessFuture.addListener(Runnable {
          val cameraProvider = cameraProcessFuture.get()

          // By default, the use cases set their target rotation to match the
          // display’s rotation.
          val preview = buildPreview()
          val imageAnalysis = buildImageAnalysis()
          val imageCapture = buildImageCapture()

          cameraProvider.bindToLifecycle(
              this, cameraSelector, preview, imageAnalysis, imageCapture)
       }, mainExecutor)
   }
}

OrientationEventListener-Einrichtung

Mit einer OrientationEventListener kannst du die Zieldrehung der Kamera-Anwendungsfälle kontinuierlich aktualisieren, wenn sich die Ausrichtung des Geräts ändert.

class CameraActivity : AppCompatActivity() {

    private val orientationEventListener by lazy {
        object : OrientationEventListener(this) {
            override fun onOrientationChanged(orientation: Int) {
                if (orientation == ORIENTATION_UNKNOWN) {
                    return
                }

                val rotation = when (orientation) {
                     in 45 until 135 -> Surface.ROTATION_270
                     in 135 until 225 -> Surface.ROTATION_180
                     in 225 until 315 -> Surface.ROTATION_90
                     else -> Surface.ROTATION_0
                 }

                 imageAnalysis.targetRotation = rotation
                 imageCapture.targetRotation = rotation
            }
        }
    }

    override fun onStart() {
        super.onStart()
        orientationEventListener.enable()
    }

    override fun onStop() {
        super.onStop()
        orientationEventListener.disable()
    }
}

DisplayListener-Einrichtung

Mit einem DisplayListener können Sie die Zieldrehung der Kameraanwendungsfälle in bestimmten Situationen aktualisieren, z. B. wenn das System nicht löscht und den Activity neu erstellt, nachdem das Gerät um 180 Grad gedreht wurde.

class CameraActivity : AppCompatActivity() {

    private val displayListener = object : DisplayManager.DisplayListener {
        override fun onDisplayChanged(displayId: Int) {
            if (rootView.display.displayId == displayId) {
                val rotation = rootView.display.rotation
                imageAnalysis.targetRotation = rotation
                imageCapture.targetRotation = rotation
            }
        }

        override fun onDisplayAdded(displayId: Int) {
        }

        override fun onDisplayRemoved(displayId: Int) {
        }
    }

    override fun onStart() {
        super.onStart()
        val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
        displayManager.registerDisplayListener(displayListener, null)
    }

    override fun onStop() {
        super.onStop()
        val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
        displayManager.unregisterDisplayListener(displayListener)
    }
}