Rotazioni del caso d'uso di CameraX

Questo argomento illustra come configurare i casi d'uso di CameraX all'interno dell'app per: immagini con le informazioni di rotazione corrette, che provengano da ImageAnalysis o il caso d'uso ImageCapture. Così:

  • Il valore Analyzer del caso d'uso ImageAnalysis dovrebbe ricevere i frame con la rotazione corretta.
  • Il caso d'uso ImageCapture deve scattare foto con la rotazione corretta.

Terminologia

Questo argomento utilizza la seguente terminologia, quindi è importante comprendere il significato di ogni termine è importante:

Orientamento del display
Si riferisce a quale lato del dispositivo è rivolto verso l'alto e può essere Uno dei quattro valori: verticale, orizzontale, verticale invertito o inversa. panorama.
Rotazione del display
Questo è il valore restituito da Display.getRotation() e rappresenta i gradi di rotazione del dispositivo in senso antiorario da il suo orientamento naturale.
Rotazione target
Rappresenta il numero di gradi di rotazione il dispositivo in senso orario per raggiungere il suo orientamento naturale.

Come determinare la rotazione target

I seguenti esempi mostrano come determinare la rotazione target di un dispositivo in base al suo orientamento naturale.

Esempio 1: orientamento naturale verticale

Esempio di dispositivo: Pixel 3 XL

Orientamento naturale = verticale
Orientamento attuale = verticale

Rotazione del display = 0
Rotazione target = 0

Orientamento naturale = verticale
Orientamento attuale = orizzontale

Rotazione del display = 90
Rotazione target = 90

Esempio 2: orientamento naturale orizzontale

Esempio di dispositivo: Pixel C

Orientamento naturale = orizzontale
Orientamento attuale = orizzontale

Rotazione del display = 0
Rotazione target = 0

Orientamento naturale = orizzontale
Orientamento attuale = verticale

Rotazione del display = 270
Rotazione target = 270

Rotazione dell'immagine

Qual è il punto giusto? In Android l'orientamento del sensore è una costante che rappresenta i gradi (0, 90, 180, 270) da cui viene ruotato il sensore nella parte superiore del dispositivo quando si trova in una posizione naturale. Per tutte le nei diagrammi, la rotazione dell'immagine descrive come dovrebbero essere ruotato in senso orario per apparire in verticale.

I seguenti esempi mostrano come deve essere la rotazione dell'immagine in base al orientamento del sensore della fotocamera. Inoltre, presuppongono che la rotazione target sia impostata sul la rotazione del display.

Esempio 1: sensore ruotato di 90 gradi

Esempio di dispositivo: Pixel 3 XL

Rotazione del display = 0
Orientamento del display = Verticale
Rotazione immagine = 90

Rotazione del display = 90
Orientamento del display = orizzontale
Rotazione immagine = 0

Esempio 2: sensore ruotato di 270 gradi

Esempio di dispositivo: Nexus 5X

Rotazione del display = 0
Orientamento del display = Verticale
Rotazione immagine = 270

Rotazione del display = 90
Orientamento del display = orizzontale
Rotazione immagine = 180

Esempio 3: sensore ruotato di 0 gradi

Esempio di dispositivo: Pixel C (tablet)

Rotazione del display = 0
Orientamento del display = orizzontale
Rotazione immagine = 0

Rotazione del display = 270
Orientamento del display = Verticale
Rotazione immagine = 90

Calcolo della rotazione di un'immagine

Analisi delle immagini

Il dispositivo Analyzer di ImageAnalysis riceve immagini dalla fotocamera sotto forma di ImageProxy sec. Ogni immagine contiene informazioni sulla rotazione, a cui è possibile accedere tramite:

val rotation = imageProxy.imageInfo.rotationDegrees

Questo valore rappresenta i gradi di rotazione dell'immagine in senso orario per corrispondere alla rotazione target di ImageAnalysis. Nel contesto di un Per l'app per Android, la rotazione target di ImageAnalysis corrisponde in genere a l'orientamento dello schermo.

Acquisizione di immagini

Un callback è collegato a un'istanza ImageCapture per segnalare quando un'acquisizione che il risultato sia pronto. Il risultato può essere l'immagine acquisita o un errore.

Quando scatti una foto, il callback fornito può essere uno dei seguenti: tipi:

  • OnImageCapturedCallback: riceve un'immagine con accesso in memoria nel di ImageProxy.
  • OnImageSavedCallback: richiamata quando l'immagine acquisita è stata richiamata archiviati correttamente nella località specificata ImageCapture.OutputFileOptions. Le opzioni possono specificare un File, un OutputStream o una località presso MediaStore.

La rotazione dell'immagine acquisita, indipendentemente dal formato (ImageProxy, File, OutputStream, MediaStore Uri) rappresenta i gradi di rotazione per che deve essere ruotata in senso orario per l'immagine acquisita per farla corrispondere a quella di ImageCapture la rotazione target, che di solito nel contesto di un'app per Android corrispondano all'orientamento dello schermo.

Per recuperare la rotazione dell'immagine acquisita, procedi in uno dei seguenti modi: modi:

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

Verificare la rotazione di un'immagine

I casi d'uso ImageAnalysis e ImageCapture ricevono ImageProxy dal videocamera dopo che la richiesta di acquisizione è andata a buon fine. ImageProxy aggrega un'immagine e informazioni su quest'ultimo, inclusa la rotazione. Queste informazioni sulla rotazione rappresenta i gradi di rotazione dell'immagine per adattarla all'uso la rotazione target della richiesta.

Il flusso di verifica della rotazione di un'immagine

Linee guida sulla rotazione target per Image Capture/ImageAnalysis

Poiché molti dispositivi non ruotano per l'orientamento verticale o orizzontale invertito per impostazione predefinita, alcune app Android non supportano questi orientamenti. Indica se un'app la supporti o meno cambia il modo in cui la rotazione target dei casi d'uso può essere aggiornato.

Di seguito sono riportate due tabelle che definiscono come mantenere sincronizzata la rotazione dei target dei casi d'uso con la rotazione del display. La prima mostra come farlo supportando quattro orientamenti: la seconda gestisce solo gli orientamenti di rotazione del dispositivo per impostazione predefinita.

Per scegliere quali linee guida seguire nella tua app:

  1. Verifica se la fotocamera Activity dell'app ha un orientamento bloccato, una orientamento sbloccato o se sostituisce le modifiche alla configurazione dell'orientamento.

  2. Decidi se la videocamera Activity dell'app deve gestire tutti e quattro i dispositivi orientamenti (verticale, verticale, orizzontale e orizzontale invertito), o, se deve gestire solo gli orientamenti, supporta per impostazione predefinita.

Supporta tutti e quattro gli orientamenti

Questa tabella cita alcune linee guida da seguire nei casi in cui il dispositivo non viene ruotata in verticale. Lo stesso vale per i dispositivi non ruotare in orizzontale.

Scenario Linee guida Modalità a finestra singola Modalità schermo diviso multi-finestra
Orientamento sbloccato Configura i casi d'uso ogni volta che viene creato Activity, ad esempio Callback onCreate() di Activity.
Usa le app di OrientationEventListener onOrientationChanged() All'interno del callback, aggiorna la rotazione target dei casi d'uso. In questo modo vengono gestiti i casi in cui il sistema ricrea l'Activity anche dopo un cambio di orientamento, ad esempio ad esempio quando il dispositivo viene ruotato di 180 gradi. Gestisce anche quando il display è invertito l'orientamento verticale e il dispositivo non si ruota per invertire l'orientamento verticale predefinito. Gestisce anche i casi in cui Activity non è ricreata quando il dispositivo ruota (ad esempio di 90 gradi). Ciò si verifica il giorno dispositivi con fattori di forma piccoli quando l'app occupa metà dello schermo e su schermi più grandi quando l'app occupa due terzi dello schermo.
(Facoltativo) Imposta il valore screenOrientation di Activity proprietà a fullSensor nella zona AndroidManifest . In questo modo l'UI è in posizione verticale quando il dispositivo è invertito. verticale e consente che l'elemento Activity venga ricreato ogni volta che il dispositivo viene ruotato di 90 gradi. Non ha effetto sui dispositivi che non ruotano in orientamento verticale inverso predefinito. La modalità multi-finestra non è supportata quando il display è in una invertito l'orientamento verticale.
Orientamento bloccato I casi d'uso devono essere configurati una sola volta, Activity viene creato per la prima volta, ad esempio nell'Activity Chiamata onCreate().
Usa le app di OrientationEventListener onOrientationChanged() All'interno del callback, aggiorna la rotazione target dei casi d'uso, ad eccezione di Anteprima. Gestisce anche i casi in cui Activity non è ricreata quando il dispositivo ruota (ad esempio di 90 gradi). Ciò si verifica il giorno dispositivi con fattori di forma piccoli quando l'app occupa metà dello schermo e su schermi più grandi quando l'app occupa due terzi dello schermo.
Modifiche all'orientamento sostituite I casi d'uso devono essere configurati una sola volta, Activity viene creato per la prima volta, ad esempio nell'Activity Chiamata onCreate().
Usa le app di OrientationEventListener onOrientationChanged() All'interno del callback, aggiorna la rotazione target dei casi d'uso. Gestisce anche i casi in cui Activity non è ricreata quando il dispositivo ruota (ad esempio di 90 gradi). Ciò si verifica il giorno dispositivi con fattori di forma piccoli quando l'app occupa metà dello schermo e su schermi più grandi quando l'app occupa due terzi dello schermo.
(Facoltativo) Imposta la proprietà screenOrientation dell'attività su fullSensor in il file AndroidManifest. Consente che l'UI sia in posizione verticale quando il dispositivo è in verticale. Non ha effetto sui dispositivi che non ruotano in orientamento verticale inverso predefinito. La modalità multi-finestra non è supportata quando il display è in una invertito l'orientamento verticale.

Sono supportati solo gli orientamenti supportati dal dispositivo

Supporta solo gli orientamenti supportati per impostazione predefinita dal dispositivo (ad esempio, potrebbero non includere l'orientamento verticale/orizzontale inverso).

Scenario Linee guida Modalità schermo diviso multi-finestra
Orientamento sbloccato Configura i casi d'uso ogni volta che viene creato Activity, ad esempio Callback onCreate() di Activity.
Usa le app di DisplayListener onDisplayChanged() All'interno il callback di destinazione, aggiorna la rotazione target dei casi d'uso, ad esempio quando dispositivo ruotato di 180 gradi. Gestisce anche i casi in cui Activity non è ricreata quando il dispositivo ruota (ad esempio di 90 gradi). Ciò si verifica il giorno dispositivi con fattori di forma piccoli quando l'app occupa metà dello schermo e su schermi più grandi quando l'app occupa due terzi dello schermo.
Orientamento bloccato I casi d'uso devono essere configurati una sola volta, Activity viene creato per la prima volta, ad esempio nell'Activity Chiamata onCreate().
Usa le app di OrientationEventListener onOrientationChanged() All'interno del callback, aggiorna la rotazione target dei casi d'uso. Gestisce anche i casi in cui Activity non è ricreata quando il dispositivo ruota (ad esempio di 90 gradi). Ciò si verifica il giorno dispositivi con fattori di forma piccoli quando l'app occupa metà dello schermo e su schermi più grandi quando l'app occupa due terzi dello schermo.
Modifiche all'orientamento sostituite I casi d'uso devono essere configurati una sola volta, Activity viene creato per la prima volta, ad esempio nell'Activity Chiamata onCreate().
Usa le app di DisplayListener onDisplayChanged() All'interno il callback di destinazione, aggiorna la rotazione target dei casi d'uso, ad esempio quando dispositivo ruotato di 180 gradi. Gestisce anche i casi in cui Activity non è ricreata quando il dispositivo ruota (ad esempio di 90 gradi). Ciò si verifica il giorno dispositivi con fattori di forma piccoli quando l'app occupa metà dello schermo e su schermi più grandi quando l'app occupa due terzi dello schermo.

Orientamento sbloccato

Activity ha un orientamento sbloccato quando si trova nell'orientamento del display (orizzontale o verticale) corrisponda all'orientamento fisico del dispositivo, con tranne per l'orientamento verticale/orizzontale, che non è supportato da alcuni dispositivi per impostazione predefinita. Per forzare la rotazione del dispositivo in tutti e quattro gli orientamenti, imposta il valore proprietà screenOrientation di Activity a fullSensor.

In modalità multi-finestra, un dispositivo che non supporta la modalità verticale/orizzontale invertita per impostazione predefinita non viene ruotata in verticale/orizzontale, anche quando La proprietà screenOrientation è impostata su 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" />

Orientamento bloccato

Un display ha un orientamento bloccato quando rimane nello stesso orientamento (orizzontale o verticale) a prescindere dall'orientamento fisico dispositivo. Puoi farlo specificando un valore screenOrientation di Activity all'interno della relativa dichiarazione nel file AndroidManifest.xml.

Quando il display è bloccato, il sistema non distrugge e ricrea Activity mentre il dispositivo viene ruotato.

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

Modifiche alla configurazione dell'orientamento sostituite

Quando un elemento Activity sostituisce le modifiche alla configurazione dell'orientamento, il sistema non lo distrugge e lo ricrea quando cambia l'orientamento fisico del dispositivo. Il sistema aggiorna però l'UI in base all'orientamento fisico del dispositivo.

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

Configurazione dei casi d'uso della fotocamera

Negli scenari descritti sopra, i casi d'uso della fotocamera possono essere configurati quando Activity è stato creato per la prima volta.

Nel caso di un Activity con orientamento sbloccato, questa configurazione è completa ogni volta che il dispositivo viene ruotato, mentre il sistema distrugge e ricrea la Activity sulle modifiche di orientamento. Ciò porta ai casi d'uso che impostano la rotazione del target in modo che corrisponda all'orientamento del display per impostazione predefinita.

Nel caso di Activity con orientamento bloccato o con override la configurazione dell'orientamento cambia. Questa configurazione viene eseguita una sola volta, quando Activity viene creato prima di tutto.

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

Configurazione di OrientationEventListener

L'utilizzo di un OrientationEventListener ti consente di aggiornare continuamente il target la rotazione dei casi d'uso della fotocamera quando cambia l'orientamento del dispositivo.

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

Configurazione di DisplayListener

L'uso di DisplayListener ti consente di aggiornare la rotazione del target della videocamera casi d'uso in determinate situazioni, ad esempio quando il sistema non elimina e ricrea Activity dopo che il dispositivo ruota di 180 gradi.

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