Opzioni di configurazione

Puoi configurare ogni caso d'uso di CameraX per controllare aspetti diversi delle operazioni del caso d'uso.

Ad esempio, con il caso d'uso di acquisizione di immagini, puoi impostare proporzioni target e una modalità flash. Il codice che segue mostra un esempio:

Kotlin

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Java

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

Oltre alle opzioni di configurazione, alcuni casi d'uso espongono le API a modificare dinamicamente le impostazioni dopo la creazione del caso d'uso. Per informazioni sulla configurazione specifica per i singoli casi d'uso, consulta Implementare un'anteprima, Analizzare le immagini e Acquisizione delle immagini.

Configurazione CameraX

Per semplicità, CameraX dispone di configurazioni predefinite, come esecutori e gestori interni, adatti alla maggior parte degli scenari di utilizzo. Tuttavia, se la tua applicazione ha requisiti speciali o preferisce personalizzare queste configurazioni, CameraXConfig è l'interfaccia adeguata.

Con CameraXConfig, un'applicazione può:

Modello di utilizzo

La seguente procedura descrive come utilizzare CameraXConfig:

  1. Crea un oggetto CameraXConfig con le tue configurazioni personalizzate.
  2. Implementa l'interfaccia CameraXConfig.Provider nel tuo Application e restituisci l'oggetto CameraXConfig in getCameraXConfig().
  3. Aggiungi la tua classe Application al file AndroidManifest.xml, come descritto qui.

Ad esempio, il seguente esempio di codice limita il logging di CameraX solo ai messaggi di errore:

Kotlin

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

Conserva una copia locale dell'oggetto CameraXConfig se l'applicazione deve conoscere la configurazione di CameraX dopo averla impostata.

Limitatore fotocamera

Durante la prima chiamata a ProcessCameraProvider.getInstance(), CameraX enumera ed esegue query sulle caratteristiche delle videocamere disponibili sul dispositivo. Poiché CameraX deve comunicare con i componenti hardware, questo processo può richiedere un tempo non banale per ogni videocamera, in particolare sui dispositivi di fascia bassa. Se l'applicazione utilizza solo fotocamere specifiche sul dispositivo, ad esempio la fotocamera anteriore predefinita, puoi impostare CameraX in modo che ignori altre fotocamere, il che può ridurre la latenza di avvio per le fotocamere utilizzate dall'applicazione.

Se il campo CameraSelector passato a CameraXConfig.Builder.setAvailableCamerasLimiter() esclude una videocamera, CameraX si comporta come se quella videocamera non esistesse. Ad esempio, il seguente codice limita l'applicazione in modo che l'applicazione utilizzi solo la fotocamera posteriore predefinita del dispositivo:

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

Thread

Molte delle API delle piattaforme su cui è sviluppata CameraX richiedono il blocco delle comunicazioni IPC (Interprocess Communication) con hardware la cui risposta può richiedere a volte centinaia di millisecondi. Per questo motivo, CameraX chiama queste API solo dai thread in background, in modo che il thread principale non venga bloccato e l'interfaccia utente rimanga fluida. CameraX gestisce internamente questi thread in background in modo che questo comportamento appaia trasparente. Tuttavia, alcune applicazioni richiedono un controllo rigoroso dei thread. CameraXConfig consente a un'applicazione di impostare i thread in background utilizzati tramite CameraXConfig.Builder.setCameraExecutor() e CameraXConfig.Builder.setSchedulerHandler().

Esecutore videocamera

L'esecutore della fotocamera viene utilizzato per tutte le chiamate API della piattaforma Camera interne, nonché per i callback da queste API. CameraX alloca e gestisce una Executor interna per eseguire queste attività. Tuttavia, se la tua applicazione richiede un controllo più rigoroso dei thread, utilizza CameraXConfig.Builder.setCameraExecutor().

Gestore scheduler

Il gestore dello scheduler viene utilizzato per pianificare le attività interne a intervalli fissi, ad esempio ritentare l'apertura della fotocamera quando non è disponibile. Questo gestore non esegue i job, ma li invia soltanto all'esecutore della videocamera. A volte viene utilizzato anche sulle piattaforme API legacy che richiedono Handler per i callback. In questi casi, i callback vengono comunque inviati direttamente solo all'esecutore della videocamera. CameraX assegna e gestisce una HandlerThread interna per eseguire queste attività, ma puoi eseguirne l'override con CameraXConfig.Builder.setSchedulerHandler().

Logging

Il logging di CameraX consente alle applicazioni di filtrare i messaggi logcat, poiché può essere buona norma evitare messaggi dettagliati nel codice di produzione. CameraX supporta quattro livelli di registrazione, dal più dettagliato al più grave:

  • Log.DEBUG (valore predefinito)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Per descrizioni dettagliate di questi livelli di log, consulta la documentazione relativa ai log di Android. Utilizza CameraXConfig.Builder.setMinimumLoggingLevel(int) per impostare il livello di logging appropriato per la tua applicazione.

Selezione automatica

CameraX fornisce automaticamente funzionalità specifiche per il dispositivo su cui è in esecuzione la tua app. Ad esempio, CameraX determina automaticamente la risoluzione migliore da utilizzare se non ne specifichi una o se quella specificata non è supportata. Tutto questo viene gestito dalla libreria, eliminando la necessità di scrivere codice specifico per il dispositivo.

L'obiettivo di CameraX è inizializzare correttamente una sessione della videocamera. Ciò significa che CameraX compromette la risoluzione e le proporzioni in base alle funzionalità del dispositivo. La compromissione può verificarsi perché:

  • Il dispositivo non supporta la risoluzione richiesta.
  • Il dispositivo presenta problemi di compatibilità, ad esempio dispositivi legacy che richiedono determinate risoluzioni per funzionare correttamente.
  • Su alcuni dispositivi, alcuni formati sono disponibili solo con alcune proporzioni.
  • Il dispositivo ha una preferenza per la "mod16 più vicina" per la codifica JPEG o video. Per maggiori informazioni, vedi SCALER_STREAM_CONFIGURATION_MAP.

Sebbene CameraX crei e gestisca la sessione, controlla sempre le dimensioni delle immagini restituite nell'output del caso d'uso nel codice e apporta le modifiche necessarie.

Rotazione

Per impostazione predefinita, la rotazione della videocamera è impostata in modo da corrispondere alla rotazione del display predefinito durante la creazione del caso d'uso. In questo caso predefinito, CameraX produce output per consentire all'app di corrispondere a ciò che ti aspetti di vedere nell'anteprima. Puoi modificare la rotazione impostandola su un valore personalizzato per supportare i dispositivi multi-display, passando l'orientamento del display corrente durante la configurazione degli oggetti caso d'uso o dinamicamente dopo la loro creazione.

La tua app può impostare la rotazione target utilizzando le impostazioni di configurazione. Può quindi aggiornare le impostazioni di rotazione utilizzando i metodi delle API dei casi d'uso (ad esempio ImageAnalysis.setTargetRotation()), anche quando il ciclo di vita è in esecuzione. Puoi utilizzare questa opzione quando l'app è bloccata in modalità Ritratto, quindi non viene eseguita alcuna riconfigurazione in rotazione, ma il caso d'uso della foto o dell'analisi deve essere consapevole della rotazione corrente del dispositivo. Ad esempio, potrebbe essere necessario il rilevamento della rotazione, in modo che i volti siano orientati correttamente per il rilevamento facciale oppure le foto siano impostate su orizzontale o verticale.

I dati delle immagini acquisite potrebbero essere archiviati senza informazioni sulla rotazione. I dati EXIF contengono informazioni sulla rotazione per consentire alle applicazioni galleria di mostrare l'immagine nell'orientamento corretto dopo il salvataggio.

Per visualizzare i dati di anteprima con l'orientamento corretto, puoi utilizzare l'output dei metadati di Preview.PreviewOutput() per creare le trasformazioni.

Il seguente esempio di codice mostra come impostare la rotazione su un evento di orientamento:

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

In base alla rotazione impostata, ogni caso d'uso ruota direttamente i dati immagine o fornisce metadati di rotazione ai consumatori dei dati immagine non ruotati.

  • Anteprima: l'output dei metadati viene fornito in modo che la rotazione della risoluzione di destinazione sia nota utilizzando Preview.getTargetRotation().
  • ImageAnalysis: l'output dei metadati viene fornito in modo che le coordinate del buffer di immagine siano note relative alle coordinate di visualizzazione.
  • Image Capture: i metadati EXIF dell'immagine, il buffer o sia il buffer sia i metadati vengono modificati in modo da notare l'impostazione di rotazione. Il valore modificato dipende dall'implementazione dell'HAL.

Ritaglia rettangolo

Per impostazione predefinita, il rettangolo di ritaglio è l'intero rettangolo buffer. Puoi personalizzarla con ViewPort e UseCaseGroup. Raggruppando i casi d'uso e impostando l'area visibile, CameraX garantisce che i retti di ritaglio di tutti i casi d'uso del gruppo puntino alla stessa area nel sensore della videocamera.

Il seguente snippet di codice mostra come utilizzare questi due corsi:

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

ViewPort definisce il rettangolo del buffer visibile agli utenti finali. Quindi, CameraX calcola il rettangolo di ritaglio più grande possibile in base alle proprietà dell'area visibile e ai casi d'uso allegati. In genere, per ottenere un effetto WYSIWYG, puoi configurare l'area visibile in base al caso d'uso dell'anteprima. Un modo semplice per ottenere l'area visibile è usare PreviewView.

I seguenti snippet di codice mostrano come ottenere l'oggetto ViewPort:

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

Nell'esempio precedente, i dati che l'app ottiene da ImageAnalysis e ImageCapture corrisponde a ciò che l'utente finale vede in PreviewView, supponendo che il tipo di scala di PreviewView sia impostato sul valore predefinito, FILL_CENTER. Dopo aver applicato il rettangolo di ritaglio e la rotazione al buffer di output, l'immagine in tutti i casi d'uso è la stessa, anche se possibilmente con risoluzioni diverse. Per ulteriori informazioni su come applicare le informazioni sulla trasformazione, vedi Transform output.

Selezione fotocamera

CameraX seleziona automaticamente la fotocamera più adatta ai requisiti e ai casi d'uso della tua applicazione. Se vuoi utilizzare un dispositivo diverso da quello selezionato per te, hai a disposizione alcune opzioni:

Il seguente esempio di codice illustra come creare un CameraSelector per influenzare la selezione del dispositivo:

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)

Seleziona più videocamere contemporaneamente

A partire da CameraX 1.3, puoi anche selezionare più fotocamere contemporaneamente. Ad esempio, puoi eseguire l'associazione a una fotocamera anteriore e posteriore per scattare foto o registrare video da entrambe le prospettive contemporaneamente.

Quando utilizzi la funzionalità Fotocamera simultanea, il dispositivo può attivare contemporaneamente due videocamere con obiettivi diversi o due fotocamere posteriori contemporaneamente. Il seguente blocco di codice mostra come impostare due videocamere quando si chiama bindToLifecycle e come recuperare entrambi gli oggetti Camera dall'oggetto ConcurrentCamera restituito.

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

Risoluzione fotocamera

Puoi scegliere di consentire a CameraX di impostare la risoluzione dell'immagine in base a una combinazione delle funzionalità del dispositivo, del livello di hardware supportato, del caso d'uso e delle proporzioni fornite. In alternativa, puoi impostare una risoluzione target specifica o un formato specifico nei casi d'uso che supportano tale configurazione.

Risoluzione automatica

CameraX è in grado di determinare automaticamente le migliori impostazioni di risoluzione in base ai casi d'uso specificati in cameraProcessProvider.bindToLifecycle(). Se possibile, specifica tutti i casi d'uso necessari per l'esecuzione contemporanea in una singola sessione in un'unica chiamata a bindToLifecycle(). CameraX determina le risoluzioni in base all'insieme di casi d'uso vincolati considerando il livello hardware supportato del dispositivo e tenendo conto della varianza specifica del dispositivo (in cui un dispositivo supera o non soddisfa le configurazioni di streaming disponibili). Lo scopo è consentire l'esecuzione dell'applicazione su un'ampia gamma di dispositivi riducendo al minimo i percorsi di codice specifici dei dispositivi.

Le proporzioni predefinite per i casi d'uso di acquisizione e analisi delle immagini sono 4:3.

I casi d'uso prevedono proporzioni configurabili per consentire all'applicazione di specificare il formato desiderato in base al design dell'interfaccia utente. L'output di CameraX viene prodotto per corrispondere alle proporzioni richieste, esattamente come quelle supportate dal dispositivo. Se non è supportata una risoluzione con corrispondenza esatta, viene selezionata quella che soddisfa il maggior numero di condizioni. Pertanto, l'applicazione determina come la fotocamera viene visualizzata nell'app e CameraX determina le impostazioni di risoluzione migliori per la risoluzione sui diversi dispositivi.

Ad esempio, un'app può effettuare le seguenti operazioni:

  • Specifica una risoluzione target pari a 4:3 o 16:9 per un caso d'uso
  • Specifica una risoluzione personalizzata, di cui CameraX tenta di trovare la corrispondenza più simile
  • Specifica le proporzioni di ritaglio per ImageCapture

CameraX sceglie automaticamente le risoluzioni della superficie interna di Camera2. La tabella seguente mostra le risoluzioni:

Caso d'uso Risoluzione della superficie interna Risoluzione dei dati di output
Anteprima Proporzioni:la risoluzione che meglio si adatta al target all'impostazione. Risoluzione della superficie interna. Vengono forniti metadati per consentire un'azione di ritaglio, scalabilità e rotazione in base alle proporzioni target.
Risoluzione predefinita: la risoluzione di anteprima massima o la risoluzione preferita dal dispositivo, che corrisponde alle proporzioni dell'anteprima.
Risoluzione massima:dimensione dell'anteprima, che si riferisce alla dimensione migliore corrispondente alla risoluzione dello schermo del dispositivo oppure a 1080p (1920 x 1080), a seconda di quale sia la più piccola.
Analisi delle immagini Proporzioni:la risoluzione che meglio si adatta al target all'impostazione. Risoluzione della superficie interna.
Risoluzione predefinita: l'impostazione predefinita della risoluzione target è 640 x 480. La regolazione sia della risoluzione target sia delle proporzioni corrispondenti consente di ottenere una risoluzione più supportata.
Risoluzione massima:la risoluzione massima di output del dispositivo della fotocamera, nel formato YUV_420_888, recuperata da StreamConfigurationMap.getOutputSizes(). La risoluzione target è impostata su 640 x 480 per impostazione predefinita, quindi se vuoi una risoluzione superiore a 640 x 480, devi utilizzare setTargetResolution() e setTargetAspectRatio() per ottenere la risoluzione più simile tra quelle supportate.
Acquisizione di immagini Proporzioni:il formato che meglio si adatta all'impostazione. Risoluzione della superficie interna.
Risoluzione predefinita: la risoluzione più alta disponibile o la massima risoluzione preferita dal dispositivo che corrisponde alle proporzioni di Image Capture.
Risoluzione massima:la risoluzione di output massima del dispositivo della fotocamera in un formato JPEG. Utilizza StreamConfigurationMap.getOutputSizes() per recuperarlo.

Specifica una risoluzione

Puoi impostare risoluzioni specifiche durante la creazione di casi d'uso utilizzando il metodo setTargetResolution(Size resolution), come mostrato nel seguente esempio di codice:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

Non puoi impostare sia le proporzioni che la risoluzione target nello stesso caso d'uso. In questo modo viene generato un IllegalArgumentException durante la creazione dell'oggetto di configurazione.

Esprimi la risoluzione Size nel frame delle coordinate dopo aver ruotato le dimensioni supportate dalla rotazione di destinazione. Ad esempio, un dispositivo con orientamento naturale verticale nella rotazione del target naturale che richiede un'immagine verticale può specificare una dimensione di 480 x 640, mentre lo stesso dispositivo, ruotato di 90 gradi e con orientamento orizzontale per il targeting, può specificare 640 x 480.

La risoluzione target tenta di stabilire un limite minimo per la risoluzione dell'immagine. La risoluzione effettiva dell'immagine è la risoluzione più vicina disponibile in termini di dimensioni non inferiore alla risoluzione target, come stabilito dall'implementazione della fotocamera.

Tuttavia, se non esiste una risoluzione uguale o superiore a quella target, viene scelta la risoluzione più vicina disponibile inferiore a quella target. Alle risoluzioni con le stesse proporzioni dell'elemento Size specificato viene assegnata una priorità maggiore rispetto a quelle con proporzioni diverse.

CameraX applica la risoluzione più adatta in base alle richieste. Se l'esigenza principale è soddisfare le proporzioni, specifica solo setTargetAspectRatio e CameraX determinerà una risoluzione specifica adatta al dispositivo. Se la necessità principale dell'app è specificare una risoluzione per rendere più efficiente l'elaborazione delle immagini (ad esempio un'immagine di piccole o medie dimensioni basata sulla funzionalità di elaborazione del dispositivo), utilizza setTargetResolution(Size resolution).

Se la tua app richiede una risoluzione esatta, consulta la tabella all'interno di createCaptureSession() per determinare le risoluzioni massime supportate da ogni livello hardware. Per controllare le risoluzioni specifiche supportate dal dispositivo attuale, consulta StreamConfigurationMap.getOutputSizes(int).

Se la tua app utilizza Android 10 o versioni successive, puoi utilizzare isSessionConfigurationSupported() per verificare un SessionConfiguration specifico.

Controlla l'output della videocamera

Oltre a consentirti di configurare l'output della videocamera in base alle necessità per ogni singolo caso d'uso, CameraX implementa anche le seguenti interfacce per supportare le operazioni della videocamera comuni a tutti i casi d'uso correlati:

  • CameraControl ti consente di configurare le funzionalità più comuni della videocamera.
  • CameraInfo ti consente di eseguire query sullo stato delle funzionalità più comuni della fotocamera.

Di seguito sono riportate le funzionalità della fotocamera supportate con CameraControl:

  • Zoom
  • Torcia
  • Messa a fuoco e misurazione esposimetrica (tocca per mettere a fuoco)
  • Compensazione dell'esposizione

Recuperare istanze di CameraControl e CameraInfo

Recupera le istanze di CameraControl e CameraInfo utilizzando l'oggetto Camera restituito da ProcessCameraProvider.bindToLifecycle(). Il seguente codice mostra un esempio:

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

Ad esempio, puoi inviare lo zoom e altre operazioni di CameraControl dopo aver chiamato bindToLifecycle(). Dopo aver interrotto o eliminato l'attività utilizzata per associare l'istanza della videocamera, CameraControl non può più eseguire operazioni e restituisce un ListenableFuture non riuscito.

Zoom

CameraControl offre due metodi per modificare il livello di zoom:

  • setZoomRatio() imposta lo zoom in base al rapporto di zoom.

    Il rapporto deve essere compreso nell'intervallo di CameraInfo.getZoomState().getValue().getMinZoomRatio() e CameraInfo.getZoomState().getValue().getMaxZoomRatio(). In caso contrario, la funzione restituisce un errore ListenableFuture.

  • setLinearZoom() imposta lo zoom corrente con un valore di zoom lineare che va da 0 a 1,0.

    Il vantaggio dello zoom lineare è che consente di scalare il campo visivo con variazioni dello zoom. Per questo è ideale per l'utilizzo con una vista Slider.

CameraInfo.getZoomState() restituisce un valore LiveData dello stato di zoom corrente. Il valore cambia quando la fotocamera viene inizializzata o se il livello di zoom viene impostato utilizzando setZoomRatio() o setLinearZoom(). Se chiami uno dei due metodi, vengono impostati i valori di supporto per ZoomState.getZoomRatio() e ZoomState.getLinearZoom(). Ciò è utile se vuoi visualizzare il testo del rapporto di zoom insieme a un dispositivo di scorrimento. È sufficiente osservare l'LiveData di ZoomState per aggiornarli entrambi senza dover effettuare una conversione.

L'elemento ListenableFuture restituito da entrambe le API consente alle applicazioni di ricevere una notifica quando viene completata una richiesta ricorrente con il valore di zoom specificato. Inoltre, se imposti un nuovo valore di zoom mentre l'operazione precedente è ancora in esecuzione, l'elemento ListenableFuture dell'operazione di zoom precedente non riesce immediatamente.

Torcia

CameraControl.enableTorch(boolean) attiva o disattiva la torcia.

CameraInfo.getTorchState() può essere utilizzato per eseguire query sullo stato attuale della torcia. Puoi controllare il valore restituito da CameraInfo.hasFlashUnit() per determinare se è disponibile una torcia. In caso contrario, la chiamata a CameraControl.enableTorch(boolean) fa sì che il valore ListenableFuture restituito venga completato immediatamente con un risultato non riuscito e lo stato della torcia viene impostato su TorchState.OFF.

Quando la torcia è attiva, rimane accesa durante l'acquisizione di foto e video indipendentemente dall'impostazione flashMode. flashMode in ImageCapture funziona solo quando la torcia è disattivata.

Messa a fuoco e misurazione esposimetrica

CameraControl.startFocusAndMetering() attiva la messa a fuoco automatica e la misurazione dell'esposizione impostando le regioni di misurazione AF/AE/AWB in base alla FocusMeteringAction specificata. Questa funzionalità viene spesso usata per implementare la funzionalità "tocca per mettere a fuoco".

Punto misurazione

Per iniziare, crea una MeteringPoint utilizzando MeteringPointFactory.createPoint(float x, float y, float size). Un MeteringPoint rappresenta un singolo punto della fotocamera Surface. Viene memorizzato in un formato normalizzato in modo da poter essere facilmente convertito in coordinate del sensore per specificare le regioni AF/AE/AWB.

La dimensione dell'intervallo MeteringPoint va da 0 a 1, con una dimensione predefinita di 0,15f. Quando chiami MeteringPointFactory.createPoint(float x, float y, float size), CameraX crea una regione rettangolare centrata su (x, y) per il size specificato.

Il seguente codice mostra come creare un MeteringPoint:

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)

startMetering Focus e ActionMetering

Per richiamare startFocusAndMetering(), le applicazioni devono creare un FocusMeteringAction, composto da uno o più MeteringPoints con combinazioni facoltative della modalità di misurazione da FLAG_AF, FLAG_AE e FLAG_AWB. Il seguente codice dimostra questo utilizzo:

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)

Come mostrato nel codice precedente, startFocusAndMetering() richiede un valore FocusMeteringAction costituito da un MeteringPoint per le regioni di misurazione AF/AE/AWB e un altro MeteringPoint solo per AF e AE.

Internamente, CameraX lo converte in Camera2 MeteringRectangles e imposta i parametri CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS corrispondenti alla richiesta di acquisizione.

Poiché non tutti i dispositivi supportano AF/AE/AWB e più regioni, CameraX esegue FocusMeteringAction con il massimo sforzo. CameraX utilizza il numero massimo di punti di misurazione supportati, nell'ordine in cui sono stati aggiunti i punti. Tutti i MeteringPoint aggiunti dopo il conteggio massimo vengono ignorati. Ad esempio, se un FocusMeteringAction viene fornito con 3 punti di misurazione su una piattaforma che ne supporta solo 2, vengono utilizzati solo i primi 2 punti di misurazione. L'ultimo elemento MeteringPoint viene ignorato da CameraX.

Compensazione dell'esposizione

La compensazione dell'esposizione è utile quando le applicazioni devono regolare i valori di esposizione (EV) oltre il risultato dell'output di esposizione automatica (AE). I valori di compensazione dell'esposizione vengono combinati nel seguente modo per determinare l'esposizione necessaria per le attuali condizioni dell'immagine:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX fornisce la funzione Camera.CameraControl.setExposureCompensationIndex() per impostare la compensazione dell'esposizione come valore di indice.

I valori di indice positivi rendono l'immagine più luminosa, mentre quelli negativi attenuano l'immagine. Le applicazioni possono eseguire query sull'intervallo supportato tramite CameraInfo.ExposureState.exposureCompensationRange() descritto nella prossima sezione. Se il valore è supportato, il valore ListenableFuture restituito viene completato quando il valore viene abilitato correttamente nella richiesta di acquisizione. Se l'indice specificato non rientra nell'intervallo supportato, setExposureCompensationIndex() fa sì che il valore ListenableFuture restituito venga completato immediatamente con un risultato non riuscito.

CameraX conserva solo l'ultima richiesta setExposureCompensationIndex() in sospeso e richiama la funzione più volte prima che la richiesta precedente venga eseguita ne risulti l'annullamento.

Lo snippet seguente imposta un indice di compensazione dell'esposizione e registra un callback per quando è stata eseguita la richiesta di modifica dell'esposizione:

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() recupera l'elemento corrente di ExposureState tra cui:

    • Supporta il controllo della compensazione dell'esposizione.
    • L'indice di compensazione dell'esposizione corrente.
    • L'intervallo dell'indice di compensazione dell'esposizione.
    • L'intervallo di compensazione dell'esposizione utilizzato nel calcolo del valore di compensazione dell'esposizione.

Ad esempio, il seguente codice inizializza le impostazioni per un'esposizione SeekBar con gli attuali valori ExposureState:

Kotlin

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

Risorse aggiuntive

Per saperne di più su CameraX, consulta le risorse aggiuntive che seguono.

Codelab

  • Introduzione a CameraX
  • Esempio di codice

  • Esempi di app CameraX
  • Community di sviluppatori

    Gruppo di discussione di Android CameraX