Opzioni di configurazione

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

Ad esempio, nel caso d'uso dell'acquisizione di immagini, puoi impostare proporzioni target e modalità flash. Il seguente codice 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 per modificare dinamicamente le impostazioni dopo la creazione del caso d'uso. Per informazioni sulla configurazione specifiche per i singoli casi d'uso, consulta Implementare un'anteprima, Analizzare le immagini e Acquisizione di immagini.

ConfigurazioneXcamera

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 adatta a questo scopo.

Con CameraXConfig, un'applicazione può:

Modello di utilizzo

La procedura seguente descrive come utilizzare CameraXConfig:

  1. Crea un oggetto CameraXConfig con le tue configurazioni personalizzate.
  2. Implementa l'interfaccia CameraXConfig.Provider in 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 di fotocamera

Durante la prima chiamata a ProcessCameraProvider.getInstance(), FotocameraX enumera ed esegue query sulle caratteristiche delle videocamere disponibili sul dispositivo. Poiché CameraX deve comunicare con i componenti hardware, questo processo può richiedere una quantità di 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 da ignorare le altre fotocamere, il che può ridurre la latenza di avvio delle fotocamere utilizzate dall'applicazione.

Se l'elemento CameraSelector passato a CameraXConfig.Builder.setAvailableCamerasLimiter() filtra una videocamera, CameraX si comporta come se questa non esistesse. Ad esempio, il seguente codice limita l'utilizzo da parte dell'applicazione solo della 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 è stata sviluppata CameraX richiedono il blocco della comunicazione tra processi (IPC) con hardware la cui risposta a volte può richiedere centinaia di millisecondi. Per questo motivo, CameraX chiama queste API solo dai thread in background, in modo che il thread principale non sia bloccato e l'interfaccia utente rimanga scorrevole. CameraX gestisce internamente questi thread sullo sfondo affinché questo comportamento risulti 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 fotocamera

L'esecutore videocamera viene utilizzato per tutte le chiamate API della piattaforma Fotocamera e per i callback da queste API. CameraX alloca e gestisce un elemento Executor interno per eseguire queste attività. Tuttavia, se la tua applicazione richiede un controllo più rigido dei thread, utilizza CameraXConfig.Builder.setCameraExecutor().

Gestore scheduler

Il gestore dello scheduler viene utilizzato per pianificare attività interne a intervalli fissi, ad esempio per riprovare ad aprire la fotocamera quando non è disponibile. Questo gestore non esegue job e li invia solo all'esecutore della fotocamera. Viene inoltre a volte utilizzato sulle piattaforme API legacy che richiedono un Handler per i callback. In questi casi, i callback vengono comunque inviati direttamente all'esecutore della videocamera. CameraX alloca e gestisce un elemento HandlerThread interno 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é è consigliabile 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 dei log 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 viene eseguita l'app. Ad esempio, CameraX determina automaticamente la risoluzione migliore da utilizzare se non ne specifichi una o se la risoluzione 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 capacità del dispositivo. La compromissione può verificarsi perché:

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

Anche se CameraX crea e gestisce la sessione, verifica 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 in un valore personalizzato per supportare dispositivi con più display passando l'orientamento di visualizzazione attuale durante la configurazione degli oggetti dei casi d'uso o in modo dinamico 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 per i casi d'uso (come ImageAnalysis.setTargetRotation()), anche quando il ciclo di vita è in esecuzione. Puoi utilizzare questa funzionalità quando l'app è bloccata in modalità verticale, perciò non viene eseguita alcuna riconfigurazione in rotazione, ma il caso d'uso relativo a foto o analisi deve essere consapevole della rotazione attuale del dispositivo. Ad esempio, potrebbe essere necessaria la percezione della rotazione per cui i volti siano orientati correttamente per il rilevamento dei volti o per le foto impostate in 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 con l'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 trasformazioni.

Il seguente esempio di codice mostra come impostare la rotazione per 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();
}

A seconda della rotazione impostata, ogni caso d'uso ruota direttamente i dati dell'immagine o fornisce metadati di rotazione ai consumatori dei dati delle immagini non ruotati.

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

Ritaglia retta

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

Lo snippet di codice riportato di seguito mostra come utilizzare queste due classi:

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 collegati. 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 è utilizzare 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, ciò che l'app riceve 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 per tutti i casi d'uso è la stessa, anche se probabilmente con risoluzioni diverse. Per ulteriori informazioni su come applicare le informazioni sulla trasformazione, consulta Transform output.

Selezione fotocamera

CameraX seleziona automaticamente la videocamera migliore per i requisiti e i 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ù videocamere contemporaneamente. Ad esempio, puoi associarlo a una fotocamera anteriore e posteriore per scattare foto o registrare video da entrambe le prospettive contemporaneamente.

Quando utilizzi la funzionalità Fotocamera contemporanea, il dispositivo può azionare contemporaneamente due fotocamere con obiettivi diversi o contemporaneamente azionare due fotocamere posteriori. Il seguente blocco di codice mostra come impostare due videocamere durante la chiamata a bindToLifecycle e come recuperare entrambi gli oggetti Fotocamera 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 lasciare che CameraX imposti la risoluzione dell'immagine in base a una combinazione di funzionalità del dispositivo, livello di hardware supportato, caso d'uso e proporzioni fornite. In alternativa, puoi impostare una risoluzione target specifica o proporzioni specifiche nei casi d'uso che supportano questa configurazione.

Risoluzione automatica

CameraX può 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 essere eseguiti contemporaneamente in una singola sessione in una singola chiamata 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 (quando un dispositivo supera o non soddisfa le configurazioni di streaming disponibili). Lo scopo è consentire l'esecuzione dell'applicazione su un'ampia varietà di dispositivi, riducendo al contempo percorsi di codice specifici per dispositivo.

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

I casi d'uso hanno proporzioni configurabili per consentire all'applicazione di specificare le proporzioni desiderate in base al design dell'interfaccia utente. L'output di CameraX viene prodotto in modo che le proporzioni corrispondano alle proporzioni richieste, il più vicino possibile al dispositivo. Se non è supportata una risoluzione con corrispondenza esatta, viene selezionata quella che soddisfa il maggior numero di condizioni. Pertanto, l'applicazione determina il modo in cui la fotocamera viene visualizzata al suo interno e CameraX determina le migliori impostazioni di risoluzione della fotocamera per soddisfare questo requisito su dispositivi diversi.

Ad esempio, un'app può:

  • Specifica una risoluzione target di 4:3 o 16:9 per un caso d'uso
  • Specifica una risoluzione personalizzata, a cui CameraX cerca di trovare la corrispondenza più vicina
  • 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. I metadati consentono di ritagliare, ridimensionare e ruotare in base alle proporzioni target per l'opzione Visualizza.
Risoluzione predefinita:la massima risoluzione di anteprima o la massima risoluzione preferita dal dispositivo che corrisponde alle proporzioni dell'anteprima.
Risoluzione massima: le dimensioni dell'anteprima, che si riferiscono alla dimensione migliore corrispondono alla risoluzione dello schermo del dispositivo, o a 1080p (1920x1080), a seconda di quale delle due opzioni è inferiore.
Analisi delle immagini Proporzioni:la risoluzione che meglio si adatta al target e all'impostazione. Risoluzione della superficie interna.
Risoluzione predefinita:l'impostazione predefinita di risoluzione target è 640 x 480. La regolazione sia della risoluzione target sia delle proporzioni corrispondenti consente di ottenere una risoluzione migliore supportata.
Risoluzione massima:la risoluzione di output massima 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 usare setTargetResolution() e setTargetAspectRatio() per ottenere quella più vicina tra le risoluzioni supportate.
Acquisizione di immagini Proporzioni:le proporzioni che si adattano meglio all'impostazione. Risoluzione della superficie interna.
Risoluzione predefinita: la massima risoluzione disponibile o la massima risoluzione preferita dal dispositivo, che corrisponde alle proporzioni di Image Capture.
Risoluzione massima:la risoluzione di output massima della fotocamera in formato JPEG. Usa StreamConfigurationMap.getOutputSizes() per recuperarlo.

Specifica una risoluzione

Puoi impostare risoluzioni specifiche quando crei 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 target che la risoluzione target nello stesso caso d'uso. In questo modo viene generato un valore IllegalArgumentException durante la creazione dell'oggetto di configurazione.

Esprimi la risoluzione Size nel frame di coordinate dopo aver ruotato le dimensioni supportate con la rotazione target. Ad esempio, un dispositivo con orientamento naturale verticale nella rotazione target naturale e che richiede un'immagine verticale può specificare 480 x 640, mentre per lo stesso dispositivo, se ruotato di 90 gradi e con orientamento orizzontale come target, è possibile specificare 640 x 480.

La risoluzione target cerca di stabilire un limite minimo per la risoluzione dell'immagine. La risoluzione effettiva dell'immagine è la risoluzione più vicina disponibile, non inferiore a quella target, come stabilito dall'implementazione della fotocamera.

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

CameraX applica la risoluzione migliore in base alle richieste. Se l'esigenza principale è soddisfare le proporzioni, specifica solo setTargetAspectRatio e CameraX determinerà una risoluzione specifica adatta in base al dispositivo. Se l'esigenza principale dell'app è specificare una risoluzione per rendere più efficiente l'elaborazione delle immagini (ad esempio un'immagine di piccole o medie dimensioni in base alla capacità 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 quali risoluzioni massime sono supportate per ogni livello di hardware. Per verificare le risoluzioni specifiche supportate dal dispositivo attuale, vedi StreamConfigurationMap.getOutputSizes(int).

Se la tua app è installata su 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 esigenze di ogni singolo caso d'uso, CameraX implementa anche le seguenti interfacce per supportare le operazioni comuni a tutti i casi d'uso associati:

  • CameraControl consente di configurare funzionalità comuni della videocamera.
  • CameraInfo ti consente di eseguire query sugli stati di queste funzionalità comuni della fotocamera.

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

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

Ottieni 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 CameraControl dopo aver chiamato bindToLifecycle(). Dopo aver interrotto o distrutto 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 cambiare il livello di zoom:

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

    Il rapporto deve essere compreso tra CameraInfo.getZoomState().getValue().getMinZoomRatio() e CameraInfo.getZoomState().getValue().getMaxZoomRatio(). In caso contrario, la funzione restituisce un errore ListenableFuture con errori.

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

    Il vantaggio dello zoom lineare è che il campo visivo viene ridimensionato con le variazioni di zoom. Per questo motivo è ideale per l'utilizzo con una visualizzazione di 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(). La chiamata di uno dei due metodi imposta i valori che supportano ZoomState.getZoomRatio() e ZoomState.getLinearZoom(). Questa funzionalità è utile se vuoi visualizzare il testo relativo al rapporto di zoom insieme a un cursore. Basta osservare ZoomState LiveData per aggiornare entrambi senza dover eseguire una conversione.

Il valore ListenableFuture restituito da entrambe le API consente di inviare una notifica alle applicazioni 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'operazione ListenableFuture dell'operazione di zoom precedente non andrà a buon fine immediatamente.

Torcia

CameraControl.enableTorch(boolean) attiva o disattiva la torcia (nota anche come 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 una torcia è disponibile. In caso contrario, la chiamata a CameraControl.enableTorch(boolean) comporta il completamento immediato dell'elemento ListenableFuture restituito con un risultato non riuscito e l'impostazione dello stato della torcia su TorchState.OFF.

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

Messa a fuoco e misurazione

CameraControl.startFocusAndMetering() attiva la messa a fuoco automatica e la misurazione dell'esposizione impostando le regioni di misurazione AF/AE/AWB in base alla specifica FocusMeteringAction. Viene spesso usata per implementare la funzionalità "tocco per mettere a fuoco" in molte applicazioni della fotocamera.

Punto di misurazione

Per iniziare, crea un MeteringPoint utilizzando MeteringPointFactory.createPoint(float x, float y, float size). Un elemento MeteringPoint rappresenta un singolo punto sulla 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 di MeteringPoint va da 0 a 1, con una dimensione predefinita di 0,15 f. Quando chiami MeteringPointFactory.createPoint(float x, float y, float size), CameraX crea una regione rettangolare centrata su (x, y) per l'elemento 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)

startFocusAndMetering e FocusMeteringAction

Per richiamare startFocusAndMetering(), le applicazioni devono creare un FocusMeteringAction, costituito da uno o più MeteringPoints con combinazioni facoltative di modalità di misurazione di 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() prende un FocusMeteringAction composto da un MeteringPoint per le regioni di misurazione AF/AE/AWB e un altro MeteringPoint solo per AF ed 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 la FocusMeteringAction con il massimo sforzo. CameraX utilizza il numero massimo di MeteringPoints supportato, nell'ordine in cui sono stati aggiunti i punti. Tutti i MeteringPoint aggiunti dopo il conteggio massimo vengono ignorati. Ad esempio, se FocusMeteringAction viene fornito con 3 MeteringPoints su una piattaforma che ne supporta solo 2, vengono utilizzati solo i primi 2 MeteringPoint. L'elemento MeteringPoint finale viene ignorato da CameraX.

Compensazione dell'esposizione

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

Exposure = ExposureCompensationIndex * ExposureCompensationStep

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

Valori di indice positivi rendono l'immagine più luminosa, mentre valori 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() restituisce immediatamente il valore ListenableFuture restituito con un risultato non riuscito.

CameraX conserva solo l'ultima richiesta setExposureCompensationIndex() in sospeso e, chiamando la funzione più volte, prima che la richiesta precedente venga eseguita, ne risulta 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'attuale ExposureState tra cui:

    • La compatibilità del controllo della compensazione dell'esposizione.
    • L'indice di compensazione dell'esposizione corrente.
    • L'intervallo dell'indice di compensazione dell'esposizione.
    • La fase di compensazione dell'esposizione utilizzata nel calcolo del valore di compensazione dell'esposizione.

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

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 seguenti risorse aggiuntive.

Codelab

  • Introduzione a FotocameraX
  • Esempio di codice

  • Esempi di app CameraX
  • Community di sviluppatori

    Gruppo di discussione di Android CameraX