Kameraausrichtungen

Wenn Ihre Android-App Kameras verwendet, müssen Sie bei der Verarbeitung von Ausrichtungen einige Besonderheiten beachten. In diesem Dokument wird davon ausgegangen, dass Sie mit den grundlegenden Konzepten der Android camera2 API vertraut sind. Eine Übersicht über camera2 finden Sie in unserem Blogpost oder in dieser Zusammenfassung. Wir empfehlen Ihnen außerdem, zuerst eine Kamera-App zu schreiben, bevor Sie dieses Dokument lesen.

Hintergrund

Die Verarbeitung von Ausrichtungen in Android-Kamera-Apps ist schwierig und erfordert die Berücksichtigung der folgenden Faktoren:

  • Natürliche Ausrichtung: Die Ausrichtung des Displays, wenn sich das Gerät in der „normalen“ Position für das Gerätedesign befindet – in der Regel das Hochformat für Smartphones und das Querformat für Laptops.
  • Sensorausrichtung: Die Ausrichtung des Sensors, der physisch am Gerät angebracht ist.
  • Displayausrichtung: Gibt an, wie stark das Gerät physisch von der natürlichen Ausrichtung abweicht.
  • Suchergröße: Die Größe des Suchers, der zum Anzeigen der Kameravorschau verwendet wird.
  • Die von der Kamera ausgegebene Bildgröße.

Durch die Kombination dieser Faktoren ergibt sich eine Vielzahl möglicher UI- und Vorschaukonfigurationen für Kamera-Apps. In diesem Dokument wird gezeigt, wie Entwickler diese Probleme umgehen und Kameraausrichtungen in Android-Apps richtig verarbeiten können.

Der Einfachheit halber gehen wir davon aus, dass alle Beispiele eine nach hinten gerichtete Kamera betreffen, sofern nicht anders angegeben. Außerdem sind alle folgenden Fotos simuliert, um die Abbildungen visuell klarer zu gestalten.

Ausrichtungen

Natürliche Ausrichtung

Die natürliche Ausrichtung ist die Displayausrichtung, wenn sich das Gerät in der Position befindet, in der es normalerweise verwendet wird. Bei Smartphones ist die natürliche Ausrichtung oft das Hochformat. Mit anderen Worten: Smartphones sind schmaler und höher. Laptops sind von Natur aus im Querformat ausgerichtet, d. h., sie sind breiter als hoch. Bei Tablets ist es etwas komplizierter, da sie sowohl im Hoch- als auch im Querformat verwendet werden können.

Illustration der natürlichen Ausrichtung mit einem Smartphone, einem Laptop und einem Objekt aus der Sicht des Betrachters

Sensorausrichtung

Die Sensorausrichtung wird formal in Grad gemessen, um die ein Ausgabebild des Sensors im Uhrzeigersinn gedreht werden muss, damit es der natürlichen Ausrichtung des Geräts entspricht. Die Sensorausrichtung ist die Anzahl der Grad, um die ein Sensor gegen den Uhrzeigersinn gedreht wird, bevor er auf dem Gerät montiert wird. Wenn Sie auf das Display schauen, scheint die Drehung im Uhrzeigersinn zu erfolgen. Das liegt daran, dass der Sensor der Rückkamera auf der Rückseite des Geräts installiert ist.

Gemäß Android 10 Compatibility Definition 7.5.5 Camera Orientation MÜSSEN Front- und Rückkameras so ausgerichtet sein, dass die lange Seite der Kamera mit der langen Seite des Displays übereinstimmt.

Ausgabepuffer von Kameras sind im Querformat. Da die natürliche Ausrichtung von Smartphones in der Regel das Hochformat ist, beträgt die Sensorausrichtung normalerweise 90 oder 270 Grad gegenüber der natürlichen Ausrichtung, damit die lange Seite des Ausgabepuffers der langen Seite des Bildschirms entspricht. Die Sensorausrichtung ist bei Geräten, deren natürliche Ausrichtung das Querformat ist, z. B. bei Chromebooks, anders. Bei diesen Geräten sind die Bildsensoren wieder so platziert, dass die lange Seite des Ausgabepuffers mit der langen Seite des Displays übereinstimmt. Da beide Bilder im Querformat aufgenommen wurden, stimmen die Ausrichtungen überein und die Sensorausrichtung beträgt 0 oder 180 Grad.

Illustration der natürlichen Ausrichtung mit einem Smartphone, einem Laptop und einem Objekt aus der Sicht des Betrachters

Die folgenden Abbildungen zeigen, wie die Dinge aus der Sicht eines Beobachters aussehen, der auf das Gerätedisplay schaut:

Abbildung der Sensorausrichtung mit einem Smartphone, einem Laptop und einem Objekt aus der Sicht des Betrachters

Stellen Sie sich folgende Szene vor:

Eine Szene mit einer niedlichen Android-Figur (Bugdroid)

Smartphone Laptop
Abbildung, die zeigt, wie es aussieht, wenn man durch den Sensor der Rückkamera eines Smartphones schaut Bildillustration aus der Perspektive des Rückkamerasensors eines Laptops

Da die Sensorausrichtung auf Smartphones in der Regel 90 oder 270 Grad beträgt, würden die Bilder ohne Berücksichtigung der Sensorausrichtung so aussehen:

Smartphone Laptop
Abbildung, die zeigt, wie es aussieht, wenn man durch den Sensor der Rückkamera eines Smartphones schaut Bildillustration aus der Perspektive des Rückkamerasensors eines Laptops

Angenommen, die Ausrichtung des Sensors gegen den Uhrzeigersinn wird in der Variablen „sensorOrientation“ gespeichert. Um die Sensorausrichtung zu berücksichtigen, müssen Sie die Ausgabepuffer um `sensorOrientation` im Uhrzeigersinn drehen, um die Ausrichtung wieder an die natürliche Ausrichtung des Geräts anzupassen.

In Android können Apps TextureView oder SurfaceView verwenden, um die Kameravorschau anzuzeigen. Beide können die Sensorausrichtung verarbeiten, wenn Apps sie richtig verwenden. In den folgenden Abschnitten wird beschrieben, wie Sie die Sensorausrichtung berücksichtigen sollten.

Displayausrichtung

Die Displaydrehung wird formal durch die Drehung der gezeichneten Grafiken auf dem Bildschirm definiert. Sie erfolgt in die entgegengesetzte Richtung der physischen Drehung des Geräts aus seiner natürlichen Ausrichtung. In den folgenden Abschnitten wird davon ausgegangen, dass alle Displayrotationen Vielfache von 90 sind. Wenn Sie die Displaydrehung anhand der absoluten Gradzahl abrufen, runden Sie sie auf den nächstgelegenen Wert aus {0, 90, 180, 270} auf.

Mit „Bildschirmausrichtung“ ist in den folgenden Abschnitten gemeint, ob ein Gerät physisch im Quer- oder Hochformat gehalten wird. Dies ist nicht dasselbe wie „Bildschirmdrehung“.

Angenommen, Sie drehen die Geräte um 90 Grad gegen den Uhrzeigersinn, wie in der folgenden Abbildung dargestellt:

Abbildung einer 90-Grad-Displaydrehung mit einem Smartphone, einem Laptop und einem Objekt aus der Sicht des Betrachters

Angenommen, die Ausgabepuffer wurden bereits entsprechend der Sensorausrichtung gedreht. Dann haben Sie die folgenden Ausgabepuffer:

Smartphone Laptop
Abbildung, die zeigt, wie es aussieht, wenn man durch den Sensor der Rückkamera eines Smartphones schaut Bildillustration aus der Perspektive des Rückkamerasensors eines Laptops

Wenn die Displaydrehung in der Variablen „displayRotation“ gespeichert ist, sollten Sie die Ausgabepuffer um „displayRotation“ gegen den Uhrzeigersinn drehen, um das richtige Bild zu erhalten.

Bei Frontkameras wirkt sich die Displaydrehung in Bezug auf den Bildschirm in die entgegengesetzte Richtung auf die Bildpuffer aus. Wenn Sie es mit einer Frontkamera zu tun haben, sollten Sie die Puffer um displayRotation im Uhrzeigersinn drehen.

Einschränkungen

Die Displayausrichtung gibt die Drehung des Geräts gegen den Uhrzeigersinn an. Das gilt nicht für alle APIs für Ausrichtung/Drehung.

Beispiel:

Wichtig ist hier, dass die Displaydrehung relativ zur natürlichen Ausrichtung erfolgt. Wenn Sie ein Smartphone beispielsweise um 90 oder 270 Grad drehen, wird das Display im Querformat angezeigt. Wenn Sie einen Laptop um denselben Betrag drehen, erhalten Sie einen Bildschirm im Hochformat. Apps sollten dies immer berücksichtigen und niemals Annahmen über die natürliche Ausrichtung eines Geräts treffen.

Beispiele

Anhand der vorherigen Abbildungen lässt sich veranschaulichen, was die Ausrichtungen und Drehungen sind.

Kombinierte Orientierungsabbildung mit einem Smartphone und einem Laptop, die nicht gedreht sind, und einem Objekt

Smartphone Laptop
Natürliche Ausrichtung = Hochformat Natürliche Ausrichtung = Querformat
Sensorausrichtung = 90 Sensor Orientation = 0
Displayausrichtung = 0 Displayausrichtung = 0
Displayausrichtung = Hochformat Displayausrichtung = Querformat

Kombinierte Orientierungsabbildung mit einem Smartphone und einem Laptop, die nicht gedreht sind, und einem Objekt

Smartphone Laptop
Natürliche Ausrichtung = Hochformat Natürliche Ausrichtung = Querformat
Sensorausrichtung = 90 Sensor Orientation = 0
Display Rotation = 90 Display Rotation = 90
Displayausrichtung = Querformat Displayausrichtung = Hochformat

Suchergröße

Apps sollten die Größe des Suchers immer an die Ausrichtung, Drehung und Bildschirmauflösung anpassen. Im Allgemeinen sollte die Ausrichtung des Suchers mit der aktuellen Displayausrichtung übereinstimmen. Das bedeutet, dass Apps die lange Kante des Suchers an der langen Kante des Displays ausrichten sollten.

Bildausgabegröße nach Kamera

Wählen Sie für die Bildausgabegröße der Vorschau nach Möglichkeit eine Größe aus, die der Größe des Suchers entspricht oder nur geringfügig größer ist. Ausgabepuffer sollten in der Regel nicht skaliert werden, da dies zu einer Verpixelung führen würde. Sie sollten auch keine zu große Größe auswählen, da dies die Leistung beeinträchtigen und den Akkuverbrauch erhöhen kann.

JPEG-Ausrichtung

Beginnen wir mit einer häufigen Situation: dem Aufnehmen eines JPEG-Fotos. In der camera2 API können Sie JPEG_ORIENTATION in der Aufnahmeanfrage übergeben, um anzugeben, um wie viel Grad Ihre ausgegebenen JPEGs im Uhrzeigersinn gedreht werden sollen.

Hier eine kurze Zusammenfassung der erwähnten Punkte:

  • Um die Sensorausrichtung zu berücksichtigen, müssen Sie den Bildpuffer um sensorOrientation im Uhrzeigersinn drehen.
  • Um die Displaydrehung zu verarbeiten, müssen Sie einen Puffer um displayRotation gegen den Uhrzeigersinn für Rückkameras und im Uhrzeigersinn für Frontkameras drehen.

Wenn Sie die beiden Faktoren addieren, erhalten Sie den Betrag, um den Sie die Grafik im Uhrzeigersinn drehen möchten:

  • sensorOrientation - displayRotation für Rückkameras.
  • sensorOrientation + displayRotation für Frontkameras.

Den Beispielcode für diese Logik finden Sie in der Dokumentation zu JPEG_ORIENTATION. Beachten Sie, dass im Beispielcode der Dokumentation deviceOrientation für die Drehung des Geräts im Uhrzeigersinn verwendet wird. Daher sind die Vorzeichen für die Displaydrehung umgekehrt.

Vorschau

Was ist mit der Kameravorschau? Es gibt zwei Hauptmethoden, mit denen eine App eine Kameravorschau anzeigen kann: SurfaceView und TextureView. Für jede ist ein anderer Ansatz erforderlich, um die Ausrichtung richtig zu verarbeiten.

SurfaceView

SurfaceView wird im Allgemeinen für Kameravorschauen empfohlen, sofern Sie die Vorschaupuffer nicht verarbeiten oder animieren müssen. Sie ist leistungsfähiger und weniger ressourcenintensiv als TextureView.

SurfaceView ist auch relativ einfach zu gestalten. Sie müssen sich nur um das Seitenverhältnis der SurfaceView kümmern, in der Sie die Kameravorschau anzeigen.

Quelle

Unterhalb von SurfaceView dreht die Android-Plattform Ausgabepuffer entsprechend der Bildschirmausrichtung des Geräts. Das bedeutet, dass sowohl die Sensorausrichtung als auch die Displayrotation berücksichtigt werden. Einfacher ausgedrückt: Wenn unser Display im Querformat ist, erhalten wir eine Vorschau im Querformat und umgekehrt.

Dies wird in der folgenden Tabelle veranschaulicht. Wichtig ist, dass die Ausrichtung der Quelle nicht nur durch die Displayrotation bestimmt wird.

Displayausrichtung Smartphone (natürliche Ausrichtung = Hochformat) Laptop (natürliche Ausrichtung = Querformat)
0 Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt
90 Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt
180 Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt
270 Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt

Layout

Wie Sie sehen, übernimmt SurfaceView bereits einige der schwierigen Dinge für uns. Jetzt müssen Sie jedoch die Größe des Suchers oder die Größe der Vorschau auf dem Bildschirm berücksichtigen. SurfaceView skaliert den Quellpuffer automatisch an seine Abmessungen an. Das Seitenverhältnis des Suchers muss mit dem des Quellpuffers übereinstimmen. Wenn Sie beispielsweise versuchen, eine Vorschau im Hochformat in eine SurfaceView im Querformat einzupassen, erhalten Sie ein verzerrtes Bild wie dieses:

Bild mit einem gestreckten Bugdroid, der durch das Anpassen einer Vorschau im Hochformat an einen Sucher im Querformat entstanden ist

Im Allgemeinen sollte das Seitenverhältnis (d.h. Breite/Höhe) des Suchers mit dem Seitenverhältnis der Quelle übereinstimmen. Wenn Sie das Bild im Sucher nicht zuschneiden möchten, um die Anzeige zu korrigieren, müssen Sie zwei Fälle berücksichtigen: wenn aspectRatioActivity größer als aspectRatioSource ist und wenn aspectRatioActivity kleiner oder gleich aspectRatioSource ist.

aspectRatioActivity > aspectRatioSource

Sie können sich den Fall als „breitere“ Aktivität vorstellen. Im Folgenden sehen Sie ein Beispiel mit einer Aktivität im Format 16:9 und einer Quelle im Format 4:3.

aspectRatioActivity = 16/9 ≈ 1.78
aspectRatioSource = 4/3 ≈ 1.33

Zuerst sollte auch dein Sucher ein Seitenverhältnis von 4:3 haben. Anschließend passen Sie die Quelle und den Sucher in die Aktivität ein:

Abbildung einer Aktivität, deren Seitenverhältnis größer ist als das des Suchers

In diesem Fall sollten Sie die Höhe des Suchers an die Höhe der Aktivität anpassen und das Seitenverhältnis des Suchers mit dem Seitenverhältnis der Quelle identisch machen. Der Pseudocode lautet so:

viewfinderHeight = activityHeight;
viewfinderWidth = activityHeight * aspectRatioSource;
aspectRatioActivity ≤ aspectRatioSource

Der andere Fall ist, wenn die Aktivität „schmaler“ oder „höher“ ist. Wir können das vorherige Beispiel wiederverwenden. Im folgenden Beispiel drehen Sie das Gerät jedoch um 90 Grad, sodass das Seitenverhältnis der Aktivität 9:16 und das der Quelle 3:4 ist.

aspectRatioActivity = 9/16 = 0.5625
aspectRatioSource = 3/4 = 0.75

In diesem Fall sollten Sie die Quelle und den Sucher so in die Aktivität einpassen:

Abbildung einer Aktivität, deren Seitenverhältnis geringer ist als das des Suchers

Die Breite des Suchers sollte der Breite der Aktivität entsprechen (im Gegensatz zur Höhe im vorherigen Fall). Das Seitenverhältnis des Suchers sollte mit dem Seitenverhältnis der Quelle identisch sein. Pseudo code:

viewfinderWidth = activityWidth;
viewfinderHeight = activityWidth / aspectRatioSource;
Abschneidungen

AutoFitSurfaceView.kt (github) aus den Camera2-Beispielen überschreibt SurfaceView und verarbeitet nicht übereinstimmende Seitenverhältnisse, indem ein Bild verwendet wird, das in beiden Dimensionen gleich oder „nur größer“ als die Aktivität ist. Inhalte, die überlaufen, werden dann abgeschnitten. Das ist nützlich für Apps, bei denen die Vorschau die gesamte Aktivität abdecken oder eine Ansicht mit festen Abmessungen vollständig ausfüllen soll, ohne das Bild zu verzerren.

Caveat

Im vorherigen Beispiel wird versucht, den Bildschirmbereich zu maximieren, indem die Vorschau nur etwas größer als die Aktivität ist, sodass kein Platz leer bleibt. Das funktioniert, weil die überlaufenden Teile standardmäßig vom übergeordneten Layout (oder der ViewGroup) abgeschnitten werden. Das Verhalten entspricht RelativeLayout und LinearLayout, aber NICHT ConstraintLayout. Bei einem ConstraintLayout werden die untergeordneten Ansichten möglicherweise so angepasst, dass sie in das Layout passen. Dadurch wird der beabsichtigte „Center-Crop“-Effekt unterbrochen und es kommt zu gestreckten Vorschaubildern. Sie können sich diesen Commit als Referenz ansehen.

TextureView

TextureView bietet maximale Kontrolle über den Inhalt der Kameravorschau, hat aber einen Leistungsaufwand. Außerdem ist mehr Aufwand erforderlich, um die Kameravorschau richtig darzustellen.

Quelle

Unterhalb von TextureView dreht die Android-Plattform die Ausgabepuffer entsprechend der Sensorausrichtung, um der natürlichen Ausrichtung des Geräts zu entsprechen. TextureView verarbeitet zwar die Sensorausrichtung, aber nicht die Displayrotation. Die Ausgabepuffer werden an der natürlichen Ausrichtung des Geräts ausgerichtet. Das bedeutet, dass Sie die Bildschirmrotationen selbst verarbeiten müssen.

Dies wird in der folgenden Tabelle veranschaulicht. Wenn Sie die Zahlen um die entsprechende Displayausrichtung drehen, erhalten Sie in SurfaceView dieselben Zahlen.

Displayausrichtung Smartphone (natürliche Ausrichtung = Hochformat) Laptop (natürliche Ausrichtung = Querformat)
0 Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt
90 Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach rechts zeigt Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach rechts zeigt
180 Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt
270 Ein Bild im Querformat, auf dem der Kopf des Bugdroid nach oben zeigt Ein hochformatiges Bild, auf dem der Kopf des Bugdroid nach oben zeigt

Layout

Das Layout ist bei TextureView etwas kompliziert. Bisher wurde empfohlen, eine Transformationsmatrix für TextureView zu verwenden, aber diese Methode funktioniert nicht auf allen Geräten. Wir empfehlen, stattdessen der Anleitung in diesem Artikel zu folgen.

So legst du Vorschauen in einem TextureView richtig an:

  1. Legen Sie die Größe der TextureView auf die ausgewählte Vorschaugröße fest.
  2. Skaliere die möglicherweise gestreckte TextureView zurück auf die ursprünglichen Abmessungen der Vorschau.
  3. Drehe die TextureView um displayRotation Grad gegen den Uhrzeigersinn.

Angenommen, Sie haben ein Smartphone, dessen Display um 90 Grad gedreht ist.

Abbildung eines Smartphones mit einer Displayrotation von 90 Grad und einem Objekt

1. Legen Sie die Größe des TextureView auf die ausgewählte Vorschaugröße fest.

Angenommen, die von Ihnen ausgewählte Vorschaugröße ist previewWidth × previewHeight, wobei previewWidth > previewHeight (die Sensorausgabe ist von Natur aus querformatig). Beim Konfigurieren einer Aufnahmesitzung sollte SurfaceTexture#setDefaultBufferSize(int width, height) aufgerufen werden, um die Vorschaubildgröße (previewWidth × previewHeight) anzugeben.

Bevor Sie setDefaultBufferSize aufrufen, müssen Sie auch die Größe des TextureView auf `previewWidth × previewHeight` mit View#setLayoutParams(android.view.ViewGroup.LayoutParams) festlegen. Der Grund dafür ist, dass TextureView SurfaceTexture#setDefaultBufferSize(int width, height) mit der gemessenen Breite und Höhe aufruft. Wenn die Größe des TextureView nicht explizit festgelegt wird, kann dies zu einem Race Condition führen. Dies lässt sich vermeiden, indem Sie die Größe der TextureView explizit festlegen.

Die TextureView-Ansicht entspricht jetzt möglicherweise nicht den Abmessungen der Quelle. Bei Smartphones ist die Quelle hochformatig, die TextureView jedoch aufgrund der gerade festgelegten layoutParams querformatig. Das würde zu gestreckten Vorschauen führen, wie hier dargestellt:

Bildillustration einer im Hochformat angeordneten Vorschau, die so gestreckt wurde, dass sie in eine TextureView mit derselben Größe wie die ausgewählte Vorschaugröße passt

2. Skaliere die möglicherweise gestreckte TextureView zurück auf die ursprünglichen Abmessungen der Vorschau.

Berücksichtigen Sie Folgendes, um die gestreckte Vorschau wieder auf die Abmessungen der Quelle zu skalieren.

Die Dimensionen der Quelle (sourceWidth × sourceHeight) sind:

  • previewHeight × previewWidth, wenn die natürliche Ausrichtung Hochformat oder umgekehrtes Hochformat ist (Sensorausrichtung ist 90 oder 270 Grad)
  • previewWidth × previewHeight, wenn die natürliche Ausrichtung das Querformat oder das umgekehrte Querformat ist (die Sensorausrichtung ist 0 oder 180 Grad).

Verzerrungen mit View#setScaleX(float) und View#setScaleY(float) beheben

  • setScaleX(sourceWidth / previewWidth)
  • setScaleY(sourceHeight / previewHeight)

Bild, das zeigt, wie die gestreckte Vorschau auf ihre ursprünglichen Abmessungen zurückskaliert wird

3. Drehen Sie die Vorschau um `displayRotation` gegen den Uhrzeigersinn.

Wie bereits erwähnt, sollten Sie die Vorschau um displayRotation gegen den Uhrzeigersinn drehen, um die Displaydrehung auszugleichen.

Dazu können Sie View#setRotation(float) verwenden.

  • setRotation(-displayRotation), da es sich um eine Drehung im Uhrzeigersinn handelt.

Bild, das zeigt, wie die Vorschau gedreht wird, um der Ausrichtung des Displays des Geräts zu entsprechen

Beispiel

Hinweis:Wenn Sie die Transformationsmatrix für TextureView bereits in Ihrem Code verwendet haben, wird die Vorschau auf Geräten, die standardmäßig im Querformat ausgerichtet sind, z. B. Chromebooks, möglicherweise nicht richtig angezeigt. Wahrscheinlich wird in Ihrer Transformationsmatrix fälschlicherweise davon ausgegangen, dass die Sensorausrichtung 90 oder 270 Grad beträgt. Sie können sich diesen Commit auf GitHub ansehen, um eine Problemumgehung zu finden. Wir empfehlen jedoch dringend, Ihre App stattdessen auf die hier beschriebene Methode umzustellen.