Opzioni di configurazione

Configura ogni caso d'uso CameraX per controllare diversi aspetti delle operazioni del caso d'uso.

Ad esempio, con il caso d'uso di acquisizione di immagini, puoi impostare un formato immagine di destinazione e una 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 loro creazione. Per informazioni sulla configurazione specifica per i singoli casi d'uso, consulta Implementare un'anteprima, Analizzare le immagini e Acquisizione di immagini.

CameraXConfig

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

Con CameraXConfig, un'applicazione può:

Modello di utilizzo

La procedura seguente descrive come utilizzare CameraXConfig:

  1. Crea un oggetto CameraXConfig con le configurazioni personalizzate.
  2. Implementa l'interfaccia CameraXConfig.Provider in Application e restauro l'oggetto CameraXConfig in getCameraXConfig().
  3. Aggiungi il corso Application al file AndroidManifest.xml, come описано qui.

Ad esempio, il seguente esempio di codice limita la registrazione 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()
   }
}

Mantieni una copia locale dell'oggetto CameraXConfig se la tua applicazione deve conoscere la configurazione di CameraX dopo averlo impostato.

Limitatore della fotocamera

Durante la prima chiamata di ProcessCameraProvider.getInstance(), CameraX enumera e esegue query sulle caratteristiche delle fotocamere disponibili sul dispositivo. Poiché CameraX deve comunicare con i componenti hardware, questo processo può richiedere un tempo non trascurabile per ogni fotocamera, in particolare sui dispositivi di fascia bassa. Se la tua applicazione utilizza solo fotocamere specifiche sul dispositivo, come la fotocamera anteriore predefinita, puoi impostare CameraX in modo da ignorare le altre fotocamere, il che può ridurre la latenza di avvio per le fotocamere utilizzate dalla tua applicazione.

Se l'attributo CameraSelector passato a CameraXConfig.Builder.setAvailableCamerasLimiter() esclude una videocamera, CameraX si comporta come se la videocamera non esistesse. Ad esempio, il seguente codice limita l'applicazione a utilizzare 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 di piattaforma su cui è basata CameraX richiedono una comunicazione interprocessuale (IPC) bloccante con hardware che a volte può richiedere centinaia di millisecondi per rispondere. 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 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 della fotocamera

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

Gestore Scheduler

L'handler dell'organizzatore viene utilizzato per pianificare le attività interne a intervalli fissi, come riprovare ad aprire la videocamera quando non è disponibile. Questo gestore non esegue i job, ma li invia solo all'executor della videocamera. Inoltre, a volte viene utilizzato sulle piattaforme API precedenti che richiedono un Handler per i callback. In questi casi, i callback vengono comunque inviati direttamente all'executor della videocamera. CameraX alloca e gestisce un HandlerThread interno per eseguire queste attività, ma puoi sostituirlo con CameraXConfig.Builder.setSchedulerHandler().

Logging

La registrazione di CameraX consente alle applicazioni di filtrare i messaggi logcat, poiché può essere buona prassi evitare messaggi descrittivi nel codice di produzione. CameraX supporta quattro livelli di logging, 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 di Android Log. 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 la tua 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.

Lo scopo di CameraX è inizializzare correttamente una sessione della fotocamera. Ciò significa che CameraX scende a compromessi in termini di risoluzione e proporzioni in base alle funzionalità del dispositivo. Il compromesso può verificarsi perché:

  • Il dispositivo non supporta la risoluzione richiesta.
  • Il dispositivo presenta problemi di compatibilità, ad esempio i dispositivi precedenti che richiedono determinate risoluzioni per funzionare correttamente.
  • Su alcuni dispositivi, alcuni formati sono disponibili solo con determinate proporzioni.
  • Il dispositivo ha una preferenza per un "mod16 più vicino" per l'encoding JPEG o video. Per ulteriori informazioni, consulta 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 modificale di conseguenza.

Rotazione

Per impostazione predefinita, la rotazione della fotocamera è 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 con più display passando l'orientamento corrente del display durante la configurazione degli oggetti use case o in modo dinamico dopo la loro creazione.

L'app può impostare la rotazione target utilizzando le impostazioni di configurazione. Può quindi aggiornare le impostazioni di rotazione utilizzando i metodi delle API di casi d'uso (ad esempio ImageAnalysis.setTargetRotation()), anche quando il ciclo di vita è in stato di esecuzione. Potresti utilizzarlo quando l'app è bloccata in modalità Ritratto, quindi non viene eseguita alcuna ricoconfigurazione in caso di rotazione, ma il caso d'uso di foto o 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 dei volti o che le foto siano impostate su orizzontale o verticale.

I dati relativi alle immagini acquisite potrebbero essere archiviati senza informazioni sulla rotazione. I dati Exif contengono informazioni sulla rotazione in modo che le applicazioni della galleria possano 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 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 dell'immagine o fornisce i metadati di rotazione ai consumatori dei dati dell'immagine non ruotata.

  • 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 dell'immagine siano note rispetto alle coordinate del display.
  • ImageCapture: i metadati EXIF dell'immagine, il buffer o entrambi il buffer e i metadati vengono modificati per registrare l'impostazione di rotazione. Il valore modificato dipende dall'implementazione di HAL.

Rettangolo di ritaglio

Per impostazione predefinita, il rettangolo di ritaglio è il rettangolo del buffer completo. Puoi personalizzarlo con ViewPort e UseCaseGroup. Raggruppando i casi d'uso e impostando il viewport, CameraX garantisce che i rettangoli di ritaglio di tutti i casi d'uso nel gruppo indichino la stessa area nel sensore della fotocamera.

Il seguente snippet di codice 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 di buffer visibile agli utenti finali. CameraX calcola quindi 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 di 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, i dati che l'app riceve da ImageAnalysis e ImageCapture corrispondono 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 di tutti i casi d'uso è la stessa, anche se con risoluzioni diverse. Per ulteriori informazioni su come applicare le informazioni sulla trasformazione, consulta transform output.

Selezione della videocamera

CameraX seleziona automaticamente il dispositivo della fotocamera 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)

Selezionare più videocamere contemporaneamente

A partire da CameraX 1.3, puoi anche selezionare più fotocamere contemporaneamente. Ad esempio, puoi eseguire il binding a una fotocamera anteriore e posteriore per scattare foto o registrare video da entrambi i punti di vista contemporaneamente.

Quando utilizzi la funzionalità Fotocamera simultanea, il dispositivo può utilizzare contemporaneamente due fotocamere con obiettivi rivolti in direzioni diverse o due fotocamere posteriori. Il seguente blocco di codice mostra come impostare due videocamere quando si chiama bindToLifecycle e come ottenere 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 della fotocamera

Puoi scegliere di lasciare che CameraX imposti la risoluzione dell'immagine in base a una combinazione delle funzionalità del dispositivo, del livello hardware supportato dal dispositivo, del caso d'uso e del formato fornito. In alternativa, puoi impostare una risoluzione di destinazione o un formato specifico nei casi d'uso che supportano questa configurazione.

Risoluzione automatica

CameraX può determinare automaticamente le impostazioni di risoluzione migliori in base ai casi d'uso specificati in cameraProcessProvider.bindToLifecycle(). Ove possibile, specifica tutti i casi d'uso necessari per l'esecuzione in contemporanea in una singola sessione in una singola chiamata bindToLifecycle(). CameraX determina le risoluzioni in base all'insieme di casi d'uso vincolati dal livello hardware supportato del dispositivo e tenendo conto della varianza specifica del dispositivo (se un dispositivo supera o non soddisfa le configurazioni dello stream disponibili). L'obiettivo è consentire all'applicazione di funzionare su una vasta gamma di dispositivi, minimizzando al contempo i percorsi di codice specifici del dispositivo.

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

I casi d'uso hanno un'area visibile configurabile per consentire all'applicazione di specificare la proporzione desiderata in base al design dell'interfaccia utente. L'output di CameraX viene prodotto in modo da adattarsi alle proporzioni richieste il più possibile in base alle specifiche del dispositivo. Se non è supportata alcuna risoluzione con corrispondenza esatta, viene selezionata quella che soddisfa il maggior numero di condizioni. Pertanto, l'applicazione determina l'aspetto della videocamera nell'app e CameraX le impostazioni di risoluzione della fotocamera migliori per soddisfare questa esigenza su dispositivi diversi.

Ad esempio, un'app può eseguire una delle seguenti operazioni:

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

CameraX sceglie automaticamente le risoluzioni delle superfici Camera2 interne. La tabella seguente mostra le risoluzioni:

Caso d'uso Risoluzione della superficie interna Risoluzione dei dati di output
Anteprima Proporzioni: la risoluzione che si adatta meglio al target per l'impostazione. Risoluzione interna della superficie. I metadati vengono forniti per consentire a una visualizzazione di ritagliare, ridimensionare e ruotare in base alle proporzioni target.
Risoluzione predefinita: la risoluzione di anteprima più alta o la risoluzione preferita dal dispositivo più alta che corrisponde alle proporzioni dell'anteprima.
Risoluzione massima: dimensioni dell'anteprima, che si riferiscono alle dimensioni migliori per la risoluzione dello schermo del dispositivo o a 1080p (1920 x 1080), a seconda del valore più piccolo.
Analisi delle immagini Proporzioni: la risoluzione che si adatta meglio al target per l'impostazione. Risoluzione interna della superficie.
Risoluzione predefinita:l'impostazione predefinita della risoluzione di destinazione è 640 x 480. La regolazione sia della risoluzione target sia delle proporzioni corrispondenti consente di ottenere la risoluzione supportata migliore.
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 utilizzare setTargetResolution() e setTargetAspectRatio() per ottenere la risoluzione più simile tra quelle supportate.
Acquisizione di immagini Proporzioni:le proporzioni più adatte all'impostazione. Risoluzione interna della superficie.
Risoluzione predefinita: la risoluzione più alta disponibile o la risoluzione preferita dal dispositivo che corrisponde alle proporzioni di ImageCapture.
Risoluzione massima: la risoluzione di output massima del dispositivo della fotocamera in formato JPEG. Utilizza StreamConfigurationMap.getOutputSizes() per recuperarlo.

Specifica una risoluzione

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

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 il formato che la risoluzione target nella stessa situazione d'uso. In questo modo viene generato un IllegalArgumentException durante la compilazione dell'oggetto di configurazione.

Esprimere la risoluzione Size nell'ambito del frame di coordinate dopo aver ruotato le dimensioni supportate in base alla rotazione target. Ad esempio, un dispositivo con orientamento naturale verticale nella rotazione del target naturale che richiede un' immagine verticale può specificare 480 x 640 e lo stesso dispositivo, ruotato di 90 gradi e con orientamento del target orizzontale, 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 disponibile più simile in termini di dimensioni che non è inferiore alla risoluzione target, come stabilito dall'implementazione della fotocamera.

Tuttavia, se non esiste una risoluzione uguale o superiore alla risoluzione target, viene scelta la risoluzione disponibile più vicina e inferiore alla risoluzione target. Alle risoluzioni con le stesse proporzioni del Size fornito viene assegnata una priorità più elevata rispetto alle risoluzioni con proporzioni diverse.

CameraX applica la risoluzione più adatta in base alle richieste. Se la necessità principale è soddisfare le proporzioni, specifica solo setTargetAspectRatio e CameraX determina una risoluzione specifica adatta in base 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 in base alla capacità di elaborazione del dispositivo), utilizza setTargetResolution(Size resolution).

Se la tua app richiede una risoluzione esatta, consulta la tabella in createCaptureSession() per determinare le risoluzioni massime supportate da ciascun livello hardware. Per verificare le risoluzioni specifiche supportate dal dispositivo attuale, consulta StreamConfigurationMap.getOutputSizes(int).

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

Controllare l'uscita della videocamera

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

  • CameraControl ti consente di configurare le 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 videocamera supportate da CameraControl:

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

Ottenere 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 operazioni di 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 modificare il livello di zoom:

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

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

  • setLinearZoom() imposta lo zoom corrente con un valore di zoom lineare compreso tra 0 e 1,0.

    Il vantaggio dello zoom lineare è che consente di modificare il campo visivo (FOV) in base alle variazioni dello zoom. Questo lo rende ideale per l'utilizzo con una visualizzazione Slider.

CameraInfo.getZoomState() restituisce un LiveData dello stato attuale dello zoom. 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 di base di ZoomState.getZoomRatio() e ZoomState.getLinearZoom(). Questa opzione è utile se vuoi visualizzare il testo del rapporto di zoom accanto a un dispositivo di scorrimento. Basta osservare ZoomState LiveData per aggiornarli entrambi senza dover eseguire una conversione.

Il valore ListenableFuture restituito da entrambe le API offre la possibilità alle applicazioni di ricevere una notifica al completamento di una richiesta ripetuta con il valore di zoom specificato. Inoltre, se imposti un nuovo valore di zoom mentre l'operazione precedente è ancora in esecuzione, ListenableFuture dell'operazione di zoom precedente non va a buon fine immediatamente.

Torcia

CameraControl.enableTorch(boolean) attiva o disattiva la torcia (nota anche come luce notturna).

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

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

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 all'azione di misurazione della messa a fuoco specificata. Questo viene spesso utilizzato per implementare la funzionalità "Tocca per mettere a fuoco" in molte applicazioni per fotocamere.

MeteringPoint

Per iniziare, crea un MeteringPoint utilizzando MeteringPointFactory.createPoint(float x, float y, float size). Un MeteringPoint rappresenta un singolo punto sulla fotocamera Surface. Viene memorizzato in una forma normalizzata 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,15f. Quando chiami MeteringPointFactory.createPoint(float x, float y, float size), CameraX crea una regione rettangolare centrata su (x, y) per il fornito size.

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 invocare startFocusAndMetering(), le applicazioni devono creare un FocusMeteringAction, che consiste in uno o più MeteringPoints con combinazioni facoltative della modalità di misurazione da FLAG_AF, FLAG_AE, FLAG_AWB. Il codice seguente mostra 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() accetta un FocusMeteringAction costituito da un MeteringPoint per le regioni di misurazione AF/AE/AWB e un altro punto di misurazione solo per AF e AE.

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

Poiché non tutti i dispositivi supportano AF/AE/AWB e più regioni, CameraX esegue FocusMeteringAction con il massimo impegno. CameraX utilizza il numero massimo di punti di misurazione supportati, nell'ordine in cui sono stati aggiunti. Tutti i MeteringPoints aggiunti dopo il conteggio massimo vengono ignorati. Ad esempio, se a un FocusMeteringAction vengono forniti 3 MeteringPoint su una piattaforma che supporta solo 2, vengono utilizzati solo i primi 2 MeteringPoint. L'ultimo MeteringPoint 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 di 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 dell'indice.

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

CameraX conserva solo l'ultima richiesta setExposureCompensationIndex() in sospeso e l'eventuale chiamata della funzione più volte prima dell'esecuzione della richiesta precedente comporta la sua cancellazione.

Il seguente snippet imposta un indice di compensazione dell'esposizione e registra un callback per quando viene 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() retrieving the current ExposureState including:

    • La supportabilità del controllo della compensazione dell'esposizione.
    • L'indice di compensazione dell'esposizione attuale.
    • L'intervallo dell'indice di compensazione dell'esposizione.
    • Il passaggio di compensazione dell'esposizione utilizzato nel calcolo del valore della compensazione dell'esposizione.

Ad esempio, il seguente codice inizializza le impostazioni per un'esposizione SeekBar con i valori correnti di 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 scoprire di più su CameraX, consulta le seguenti risorse aggiuntive.

Codelab

  • Guida introduttiva a CameraX
  • Esempio di codice

  • Esempi di app CameraX
  • Community di sviluppatori

    Gruppo di discussione Android CameraX