Rotacja przypadków użycia w aplikacji CameraX

W tym artykule omawiamy, jak skonfigurować w aplikacji przypadki użycia Aparatu X, aby uzyskać z poprawnymi informacjami o obróceniu, niezależnie od tego, ImageAnalysis lub ImageCapture. A więc:

  • Element Analyzer przypadku użycia ImageAnalysis powinien otrzymywać ramki z atrybutem właściwy obrót.
  • W przypadku użycia funkcji ImageCapture zdjęcia powinny być wykonywane z prawidłowym obrotem.

Terminologia

W tym temacie posługujemy się poniższą terminologią, dlatego warto zrozumieć, co oznaczają poszczególne terminy jest ważne.

Orientacja wyświetlacza
Wskazuje, która strona urządzenia jest u góry. Można ją ustawić jedną z czterech wartości: orientacja pionowa, orientacja pozioma, orientacja pionowa lub odwrotna; w orientacji poziomej.
Obrót wyświetlacza
To wartość zwracana przez funkcję Display.getRotation() oraz przedstawia stopień obrotu urządzenia w lewo i jej naturalnej orientacji.
Rotacja docelowa
To jest liczba stopni, o którą ma się obrócić aby osiągnąć naturalną orientację.

Określanie rotacji docelowej

W przykładach poniżej pokazujemy, jak określić docelową rotację urządzenia. na podstawie jej naturalnej orientacji.

Przykład 1. Naturalna orientacja pionowa

Przykład urządzenia: Pixel 3 XL

Naturalna orientacja = Orientacja pionowa
Obecna orientacja = Pionowa

Obrót wyświetlacza = 0
Rotacja docelowa = 0

Naturalna orientacja = Orientacja pionowa
Obecna orientacja = Poziomo

Obrót wyświetlacza = 90
Rotacja docelowa = 90

Przykład 2. Naturalna orientacja pozioma

Przykład urządzenia: Pixel C

Orientacja naturalna = Pozioma
Obecna orientacja = Poziomo

Obrót wyświetlacza = 0
Rotacja docelowa = 0

Orientacja naturalna = Pozioma
Obecna orientacja = Pionowa

Obrót wyświetlacza = 270
Rotacja docelowa = 270

Obrót obrazu

Co jest gotowe? Orientacja czujnika jest zdefiniowana w Androidzie jako stała reprezentująca stopnie (0, 90, 180, 270), od którego obraca się czujnik U góry urządzenia, gdy jest ono w naturalnym położeniu. Dla wszystkich przypadków na diagramach obrót obrazu określa, w jaki sposób dane powinny być obrócono w prawo, aby wyświetlić widok pionowo.

W przykładach poniżej pokazujemy, jak powinien być obrócony obraz w zależności od orientacji czujnika aparatu. Zakładają też, że rotacja docelowa jest ustawiona na obrót wyświetlacza.

Przykład 1: czujnik obrócony o 90 stopni

Przykład urządzenia: Pixel 3 XL

Obrót wyświetlacza = 0
Orientacja wyświetlacza = Pionowa
Obrót obrazu = 90

Obrót wyświetlacza = 90
Orientacja wyświetlacza = Pozioma
Obrót obrazu = 0

Przykład 2: czujnik obrócony o 270 stopni

Przykład urządzenia: Nexus 5X

Obrót wyświetlacza = 0
Orientacja wyświetlacza = Pionowa
Obrót obrazu = 270

Obrót wyświetlacza = 90
Orientacja wyświetlacza = Pozioma
Obrót obrazu = 180

Przykład 3: czujnik obrócony o 0 stopni

Przykład urządzenia: Pixel C (tablet)

Obrót wyświetlacza = 0
Orientacja wyświetlacza = Pozioma
Obrót obrazu = 0

Obrót wyświetlacza = 270
Orientacja wyświetlacza = Pionowa
Obrót obrazu = 90

Obliczanie obrotu obrazu

Analiza obrazu

Urządzenie Analyzer, z którego korzysta ImageAnalysis, odbiera z kamery obrazy w postaci: ImageProxy s Każdy obraz zawiera informacje o rotacji, które są dostępne przez:

val rotation = imageProxy.imageInfo.rotationDegrees

Ta wartość określa kąt, o jaki obraz musi zostać obrócony w prawo, aby dopasować się do obrotu docelowego elementu ImageAnalysis. W kontekście aplikacji na Androida, rotacja docelowa w grupie ImageAnalysis zwykle odpowiada do orientacji ekranu.

Robienie zdjęć

Do instancji ImageCapture dołączone jest wywołanie zwrotne, które informuje o przechwyceniu wynik jest gotowy. Efektem może być zrzut ekranu lub błąd.

Podczas robienia zdjęcia możesz uzyskać oddzwanianie z jednego z tych numerów: typy:

  • OnImageCapturedCallback: odbiera obraz z dostępem w pamięci w ma postać ImageProxy.
  • OnImageSavedCallback: wywoływane po wykonaniu przechwyconego obrazu. zapisane w lokalizacji określonej przez ImageCapture.OutputFileOptions Dostępne opcje to File, OutputStream lub lokalizację w: MediaStore.

Obrót zrobionego obrazu niezależnie od jego formatu (ImageProxy, File, OutputStream, MediaStore Uri) pokazuje stopnie obrotu według które należy obrócić w prawo, aby dopasowaćImageCapture kierowania reklam, czyli w kontekście aplikacji na Androida zwykle do orientacji ekranu.

Obrót zdjęcia można pobrać w jednej z następujących opcji: sposoby:

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

Sprawdzanie obrotu obrazu

Przypadki użycia ImageAnalysis i ImageCapture otrzymują zdarzenia ImageProxy z metody z aparatu po pomyślnym żądaniu przechwytywania. ImageProxy zawija obraz i informacji o nim, w tym o jego rotacji. Te informacje o rotacji przedstawia stopnie obrotu obrazu w celu dopasowania do zastosowania docelową rotację.

Proces weryfikacji obrotu obrazu

Wskazówki dotyczące rotacji celów w przypadku przechwytywania obrazów i analizy obrazów

Ponieważ wiele urządzeń nie obraca się do pozycji pionowej lub poziomej, domyślnie, niektóre aplikacje na Androida nie obsługują tych orientacji. Określa, czy aplikacja obsługuje ją lub nie zmienia sposobu, w jaki rotacja docelowa przypadków użycia może być Zaktualizowano.

Poniżej znajdują się 2 tabele, które określają, jak zsynchronizować rotację docelową w przypadkach użycia. dzięki obróceniu wyświetlacza. Pierwszy pokazuje, jak to robić, a jednocześnie wspiera wszystkie w czterech orientacjach, drugi obsługuje tylko orientacje, w których obraca się urządzenie domyślnie.

Aby wybrać wytyczne, których należy przestrzegać w aplikacji:

  1. Sprawdź, czy aparat Activity w aplikacji ma zablokowane ustawienie orientacji. jest odblokowane lub zastępuje zmiany konfiguracji orientacji.

  2. Określ, czy kamera aplikacji Activity powinna obsługiwać wszystkie 4 urządzenia orientacjami (pionowa, odwrócona pionowa, pozioma i odwrócona pozioma), lub obsługuje tylko orientacje, na których działa. domyślnie.

Obsługa wszystkich 4 orientacji

W tej tabeli podano określone wskazówki, których należy przestrzegać w przypadku, gdy urządzenie nie zostanie odwrócona do orientacji pionowej. To samo można zastosować na urządzeniach, które i nie obracaj kamery w odwrotnej orientacji poziomej.

Scenariusz Wskazówki Tryb jednego okna Tryb podzielonego ekranu dla wielu okien
Odblokowana orientacja Skonfiguruj przypadki użycia co podczas tworzenia Activity, na przykład w Activity – połączenie zwrotne (onCreate()).
Użyj OrientationEventListener onOrientationChanged() W wywołaniu zwrotnym zaktualizuj docelową rotację przypadków użycia. To dotyczy przypadków, w których system nie odtworzyć Activity nawet po zmianie orientacji, takiej jak tak jak przy obróceniu urządzenia o 180 stopni. Działa również wtedy, gdy wyświetlacz jest odwrócony do orientacji pionowej, a urządzenie nie obraca się do tyłu wartość domyślną. Obsługuje również przypadki, w których Activity nie jest odtwarzane podczas obrotu urządzenia (np. o 90 stopni). To się dzieje w na małych urządzeniach, gdy aplikacja zajmuje połowę ekranu i na większym ekranie gdy aplikacja zajmuje 2/3 ekranu.
Opcjonalnie: ustaw właściwość screenOrientation aplikacji Activity usługę do fullSensor w: AndroidManifest . Dzięki temu interfejs może być ustawiony pionowo, gdy urządzenie jest odwrócone. pionową i umożliwia odtworzenie obiektu Activity przez po obróceniu urządzenia o 90 stopni. Nie ma wpływu na urządzenia, które nie obracają się do orientacji pionowej wartość domyślną. Tryb wielu okien nie jest obsługiwany, gdy wyświetlacz jest do odwrotnej orientacji pionowej.
Blokada orientacji Skonfiguruj przypadki użycia tylko raz, gdy Aplikacja Activity jest tworzona po raz pierwszy, np. w Activity onCreate() oddzwonienie.
Użyj OrientationEventListener onOrientationChanged() W wywołaniu zwrotnym zaktualizuj docelową rotację przypadków użycia z wyjątkiem podglądu. Obsługuje również przypadki, w których Activity nie jest odtwarzane podczas obrotu urządzenia (np. o 90 stopni). To się dzieje w na małych urządzeniach, gdy aplikacja zajmuje połowę ekranu i na większym ekranie gdy aplikacja zajmuje 2/3 ekranu.
Zmiana orientacji orientacji została zastąpiona Skonfiguruj przypadki użycia tylko raz, gdy Aplikacja Activity jest tworzona po raz pierwszy, np. w Activity onCreate() oddzwonienie.
Użyj OrientationEventListener onOrientationChanged() W wywołaniu zwrotnym zaktualizuj docelową rotację przypadków użycia. Obsługuje również przypadki, w których Activity nie jest odtwarzane podczas obrotu urządzenia (np. o 90 stopni). To się dzieje w na małych urządzeniach, gdy aplikacja zajmuje połowę ekranu i na większym ekranie gdy aplikacja zajmuje 2/3 ekranu.
Opcjonalnie: ustaw właściwość screenOrientation aktywności na fullSensor w: w pliku AndroidManifest. Pozwala ustawić interfejs użytkownika pionowo, gdy urządzenie jest ustawione pionowo. Nie ma wpływu na urządzenia, które nie obracają się do orientacji pionowej wartość domyślną. Tryb wielu okien nie jest obsługiwany, gdy wyświetlacz jest do odwrotnej orientacji pionowej.

Obsługuj tylko orientacje obsługiwane przez urządzenie

Obsługują tylko orientacje, które urządzenie obsługuje domyślnie (które mogą nie może zawierać odwróconej orientacji pionowej lub poziomej).

Scenariusz Wskazówki Tryb podzielonego ekranu dla wielu okien
Odblokowana orientacja Skonfiguruj przypadki użycia co podczas tworzenia Activity, na przykład w Activity – połączenie zwrotne (onCreate()).
Użyj DisplayListener onDisplayChanged() Wewnątrz z wywołaniem zwrotnym, zaktualizuj docelową rotację przypadków użycia, np. gdy obrócono urządzenie o 180 stopni. Obsługuje również przypadki, w których Activity nie jest odtwarzane podczas obrotu urządzenia (np. o 90 stopni). To się dzieje w na małych urządzeniach, gdy aplikacja zajmuje połowę ekranu i na większym ekranie gdy aplikacja zajmuje 2/3 ekranu.
Blokada orientacji Skonfiguruj przypadki użycia tylko raz, gdy Aplikacja Activity jest tworzona po raz pierwszy, np. w Activity onCreate() oddzwonienie.
Użyj OrientationEventListener onOrientationChanged() W wywołaniu zwrotnym zaktualizuj docelową rotację przypadków użycia. Obsługuje również przypadki, w których Activity nie jest odtwarzane podczas obrotu urządzenia (np. o 90 stopni). To się dzieje w na małych urządzeniach, gdy aplikacja zajmuje połowę ekranu i na większym ekranie gdy aplikacja zajmuje 2/3 ekranu.
Zmiana orientacji orientacji została zastąpiona Skonfiguruj przypadki użycia tylko raz, gdy Aplikacja Activity jest tworzona po raz pierwszy, np. w Activity onCreate() oddzwonienie.
Użyj DisplayListener onDisplayChanged() Wewnątrz z wywołaniem zwrotnym, zaktualizuj docelową rotację przypadków użycia, np. gdy obrócono urządzenie o 180 stopni. Obsługuje również przypadki, w których Activity nie jest odtwarzane podczas obrotu urządzenia (np. o 90 stopni). To się dzieje w na małych urządzeniach, gdy aplikacja zajmuje połowę ekranu i na większym ekranie gdy aplikacja zajmuje 2/3 ekranu.

Odblokowana orientacja

Urządzenie Activity ma otwartą orientację, gdy ma orientację wyświetlacza (np. pionową lub poziomą) pasuje do fizycznej orientacji urządzenia, przy czym wartość z wyjątkiem orientacji odwróconej orientacji pionowej i poziomej, których niektóre urządzenia nie obsługują domyślnie. Aby wymusić obrót urządzenia we wszystkich czterech orientacjach, ustaw Activity na fullSensor.screenOrientation

na urządzeniu w trybie wielu okien, które nie obsługuje odwrotnego trybu pionowego ani poziomego; domyślnie nie zostanie odwrócona do orientacji pionowej/poziomej, nawet jeśli Właściwość screenOrientation jest ustawiona na fullSensor.

<!-- 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" />

Zablokowana orientacja

Wyświetlacz ma zablokowaną orientację, gdy pozostaje w tej samej orientacji (np. pionową lub poziomą) niezależnie od fizycznej orientacji urządzenia. Można to zrobić, określając właściwość screenOrientation elementu Activity właściwości zawartej w deklaracji w pliku AndroidManifest.xml.

Gdy wyświetlacz jest w zablokowanej orientacji, system nie zniszczy ani nie uszkodzi Odtwórz Activity podczas obracania urządzenia.

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

Zastąpiono zmiany konfiguracji orientacji

Gdy Activity zastąpi konfigurację orientacji, system nie zniszczy go i nie tworzy ponownie, gdy zmieni się orientacja fizyczna urządzenia. System aktualizuje interfejs, aby dopasować się do fizycznej orientacji urządzenia.

<!-- 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" />

Konfiguracja przypadków użycia aparatu

W przypadkach opisanych powyżej przypadki użycia kamery można skonfigurować, gdy Utworzono Activity.

W przypadku urządzenia Activity z odblokowaną orientacją konfiguracja jest gotowa. przy każdym obróceniu urządzenia, bo system niszczy i tworzy Activity o zmianach orientacji. W efekcie przypadki użycia ustawiają docelowy obrót, aby domyślnie pasował do orientacji wyświetlacza.

W przypadku elementu Activity z zablokowaną orientacją lub zastąpieniem konfiguracji orientacji, konfiguracja odbywa się raz, gdy Activity został utworzony po raz pierwszy.

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)
   }
}

Konfiguracja OrientationEventListener

Użycie parametru OrientationEventListener umożliwia ciągłe aktualizowanie wartości docelowej lub obracania aparatu wraz ze zmianą orientacji urządzenia.

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()
    }
}

Konfiguracja DisplayListener

Użycie elementu DisplayListener umożliwia zaktualizowanie docelowego obrotu kamery przypadków użycia w określonych sytuacjach, na przykład gdy system nie zniszczy i odtwórz element Activity po obróceniu urządzenia o 180 stopni.

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)
    }
}