Nota: questa pagina fa riferimento al pacchetto Fotocamera2. A meno che la tua app non richieda funzionalità specifiche di basso livello di Fotocamera2, ti consigliamo di utilizzare FotocameraX. Sia CameraX che Camera2 supportano Android 5.0 (livello API 21) e versioni successive.
La modalità multicamera è stata introdotta con Android 9 (livello API 28). Sin dal rilascio, sono arrivati sul mercato dispositivi che supportano l'API. Molti casi d'uso multicamera sono strettamente collegati a una specifica configurazione hardware. In altre parole, non tutti i casi d'uso sono compatibili con tutti i dispositivi, il che rende le funzionalità multicamera una buona candidata per la distribuzione delle funzionalità di Google Play.
Ecco alcuni casi d'uso tipici:
- Zoom: consente di passare da una fotocamera all'altra a seconda dell'area di ritaglio o della lunghezza focale desiderata.
- Profondità: utilizzo di più fotocamere per creare una mappa di profondità.
- Bokeh: utilizzo delle informazioni di profondità dedotte per simulare un intervallo di messa a fuoco ristretto, simile a una DSLR.
Differenza tra videocamere logiche e fisiche
Comprendere l'API multicamera richiede la differenza tra videocamere logiche e fisiche. Come riferimento, considera un dispositivo con tre fotocamere posteriori. In questo esempio, ognuna delle tre fotocamere posteriori è considerata una videocamera fisica. Una telecamera logica è un gruppo di due o più telecamere fisiche. L'output di una videocamera logica può essere un flusso che proviene da una delle videocamere fisiche sottostanti o un flusso fuso che proviene da più videocamere fisiche sottostanti contemporaneamente. In ogni caso, il flusso viene gestito dall'hardware Abstraction Layer (HAL) della videocamera.
Molti produttori di telefoni sviluppano applicazioni per fotocamere proprietarie, che di solito sono preinstallate sui loro dispositivi. Per utilizzare tutte le funzionalità dell'hardware, è possibile utilizzare API private o nascoste oppure ricevere un trattamento speciale dall'implementazione del driver a cui altre applicazioni non hanno accesso. Alcuni dispositivi implementano il concetto di telecamere logiche fornendo un flusso misto di fotogrammi provenienti dalle diverse videocamere fisiche, ma solo ad alcune applicazioni con privilegi. Spesso solo una delle fotocamere fisiche è esposta all'inquadratura. La situazione degli sviluppatori di terze parti precedenti ad Android 9 è illustrata nel seguente diagramma:
A partire da Android 9, le API private non sono più consentite nelle app per Android. Con l'inclusione nel framework del supporto di più videocamere, le best practice di Android consigliano vivamente ai produttori di telefoni di esporre una fotocamera logica per tutte le fotocamere fisiche rivolte nella stessa direzione. Di seguito è riportato ciò che gli sviluppatori di terze parti dovrebbero aspettarsi sui dispositivi con Android 9 e versioni successive:
Ciò che la videocamera logica fornisce dipende interamente dall'implementazione dell'OEM della fotocamera HAL. Ad esempio, un dispositivo come Pixel 3 implementa la logica della fotocamera in modo da sceglierne una fisica in base alla lunghezza focale richiesta e all'area di ritaglio.
L'API multi-camera
La nuova API aggiunge le nuove costanti, classi e metodi seguenti:
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
CameraCharacteristics.getPhysicalCameraIds()
CameraCharacteristics.getAvailablePhysicalCameraRequestKeys()
CameraDevice.createCaptureSession(SessionConfiguration config)
CameraCharacteritics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
OutputConfiguration
eSessionConfiguration
A causa delle modifiche all'Android Compatibility Definition Document (CDD), l'API multi-camera presenta alcune aspettative da parte degli sviluppatori. I dispositivi con doppia fotocamera esistevano già prima di Android 9, ma l'apertura di più di una fotocamera comportava contemporaneamente tentativi ed errori. Su Android 9 e versioni successive, la modalità multicamera fornisce un insieme di regole per specificare quando è possibile aprire un paio di videocamere fisiche che fanno parte della stessa fotocamera logica.
Nella maggior parte dei casi, i dispositivi con Android 9 e versioni successive espongono tutte le videocamere fisiche (ad eccezione, probabilmente, dei tipi di sensori meno comuni come gli infrarossi) insieme a una videocamera logica più facile da usare. Per ogni combinazione di stream il cui funzionamento è garantito, uno stream appartenente a una videocamera logica può essere sostituito con due stream provenienti dalle videocamere fisiche sottostanti.
Più stream contemporaneamente
L'utilizzo di più stream da videocamere contemporaneamente copre le regole per l'utilizzo di più stream contemporaneamente in una singola videocamera.
Con un'aggiunta importante, le stesse regole si applicano a più videocamere.
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
spiega come sostituire uno stream logico YUV_420_888 o non elaborato con due
flussi fisici. Ciò significa che ogni flusso di tipo YUV o RAW può essere sostituito con due flussi di tipo e dimensione identici. Puoi iniziare con uno stream della videocamera con la seguente configurazione garantita per i dispositivi con videocamera singola:
- Stream 1: tipo YUV, dimensioni
MAXIMUM
da videocamera logicaid = 0
Quindi, un dispositivo con supporto multicamera consente di creare una sessione che sostituisce lo stream YUV logico con due flussi fisici:
- Stream 1: tipo YUV, dimensione
MAXIMUM
dalla videocamera fisicaid = 1
- Stream 2: tipo YUV, dimensione
MAXIMUM
dalla videocamera fisicaid = 2
Puoi sostituire uno stream YUV o RAW con due stream equivalenti solo se
queste due videocamere fanno parte di un raggruppamento logico delle videocamere, elencato nella sezione
CameraCharacteristics.getPhysicalCameraIds()
.
Le garanzie offerte dal framework sono solo il minimo indispensabile per ottenere frame da più videocamere fisiche contemporaneamente. Gli stream aggiuntivi sono supportati nella maggior parte dei dispositivi e a volte consentono anche l'apertura di più videocamere fisiche in modo indipendente. Dal momento che non è una garanzia definitiva dal framework, farlo richiede l'esecuzione di test e ottimizzazione per dispositivo utilizzando prove ed errori.
Creazione di una sessione con più videocamere fisiche
Quando utilizzi fotocamere fisiche su un dispositivo abilitato per più videocamere, apri una singola
CameraDevice
(la videocamera logica) e interagisci con essa durante una singola
sessione. Crea la singola sessione utilizzando l'API CameraDevice.createCaptureSession(SessionConfiguration config)
, che è stata aggiunta nel livello API 28. La configurazione della sessione prevede una serie di configurazioni di output, ognuna delle quali ha un insieme di destinazioni di output e, facoltativamente, un ID videocamera fisica desiderato.
Alle richieste di acquisizione è associato un target di output. Il framework determina a quale videocamera fisica (o logica) vengono inviate le richieste in base alla destinazione di output collegata. Se il target di output corrisponde a uno dei target di output inviati come configurazione di output insieme a un ID videocamera fisica, la videocamera fisica riceve ed elabora la richiesta.
Utilizzo di un paio di videocamere fisiche
Un'altra aggiunta alle API fotocamera per multicamera è la possibilità di identificare le videocamere logiche e trovare le videocamere fisiche dietro di loro. Puoi definire una funzione per identificare meglio le potenziali coppie di videocamere fisiche che puoi utilizzare per sostituire uno degli stream logici delle videocamere:
Kotlin
/** * Helper class used to encapsulate a logical camera and two underlying * physical cameras */ data class DualCamera(val logicalId: String, val physicalId1: String, val physicalId2: String) fun findDualCameras(manager: CameraManager, facing: Int? = null): List{ val dualCameras = MutableList () // Iterate over all the available camera characteristics manager.cameraIdList.map { Pair(manager.getCameraCharacteristics(it), it) }.filter { // Filter by cameras facing the requested direction facing == null || it.first.get(CameraCharacteristics.LENS_FACING) == facing }.filter { // Filter by logical cameras // CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA requires API >= 28 it.first.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!!.contains( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) }.forEach { // All possible pairs from the list of physical cameras are valid results // NOTE: There could be N physical cameras as part of a logical camera grouping // getPhysicalCameraIds() requires API >= 28 val physicalCameras = it.first.physicalCameraIds.toTypedArray() for (idx1 in 0 until physicalCameras.size) { for (idx2 in (idx1 + 1) until physicalCameras.size) { dualCameras.add(DualCamera( it.second, physicalCameras[idx1], physicalCameras[idx2])) } } } return dualCameras }
Java
/** * Helper class used to encapsulate a logical camera and two underlying * physical cameras */ final class DualCamera { final String logicalId; final String physicalId1; final String physicalId2; DualCamera(String logicalId, String physicalId1, String physicalId2) { this.logicalId = logicalId; this.physicalId1 = physicalId1; this.physicalId2 = physicalId2; } } ListfindDualCameras(CameraManager manager, Integer facing) { List dualCameras = new ArrayList<>(); List cameraIdList; try { cameraIdList = Arrays.asList(manager.getCameraIdList()); } catch (CameraAccessException e) { e.printStackTrace(); cameraIdList = new ArrayList<>(); } // Iterate over all the available camera characteristics cameraIdList.stream() .map(id -> { try { CameraCharacteristics characteristics = manager.getCameraCharacteristics(id); return new Pair<>(characteristics, id); } catch (CameraAccessException e) { e.printStackTrace(); return null; } }) .filter(pair -> { // Filter by cameras facing the requested direction return (pair != null) && (facing == null || pair.first.get(CameraCharacteristics.LENS_FACING).equals(facing)); }) .filter(pair -> { // Filter by logical cameras // CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA requires API >= 28 IntPredicate logicalMultiCameraPred = arg -> arg == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA; return Arrays.stream(pair.first.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)) .anyMatch(logicalMultiCameraPred); }) .forEach(pair -> { // All possible pairs from the list of physical cameras are valid results // NOTE: There could be N physical cameras as part of a logical camera grouping // getPhysicalCameraIds() requires API >= 28 String[] physicalCameras = pair.first.getPhysicalCameraIds().toArray(new String[0]); for (int idx1 = 0; idx1 < physicalCameras.length; idx1++) { for (int idx2 = idx1 + 1; idx2 < physicalCameras.length; idx2++) { dualCameras.add( new DualCamera(pair.second, physicalCameras[idx1], physicalCameras[idx2])); } } }); return dualCameras; }
La gestione dello stato delle videocamere fisiche è controllata dalla videocamera logica. Per aprire una "doppia fotocamera", apri la videocamera logica corrispondente alle videocamere fisiche:
Kotlin
fun openDualCamera(cameraManager: CameraManager, dualCamera: DualCamera, // AsyncTask is deprecated beginning API 30 executor: Executor = AsyncTask.SERIAL_EXECUTOR, callback: (CameraDevice) -> Unit) { // openCamera() requires API >= 28 cameraManager.openCamera( dualCamera.logicalId, executor, object : CameraDevice.StateCallback() { override fun onOpened(device: CameraDevice) = callback(device) // Omitting for brevity... override fun onError(device: CameraDevice, error: Int) = onDisconnected(device) override fun onDisconnected(device: CameraDevice) = device.close() }) }
Java
void openDualCamera(CameraManager cameraManager, DualCamera dualCamera, Executor executor, CameraDeviceCallback cameraDeviceCallback ) { // openCamera() requires API >= 28 cameraManager.openCamera(dualCamera.logicalId, executor, new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { cameraDeviceCallback.callback(cameraDevice); } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { cameraDevice.close(); } @Override public void onError(@NonNull CameraDevice cameraDevice, int i) { onDisconnected(cameraDevice); } }); }
Oltre a selezionare la fotocamera da aprire, la procedura equivale ad aprire una fotocamera nelle versioni precedenti di Android. La creazione di una sessione di acquisizione con la nuova API di configurazione della sessione indica al framework di associare determinati target a specifici ID videocamera fisica:
Kotlin
/** * Helper type definition that encapsulates 3 sets of output targets: * * 1. Logical camera * 2. First physical camera * 3. Second physical camera */ typealias DualCameraOutputs = Triple?, MutableList ?, MutableList ?> fun createDualCameraSession(cameraManager: CameraManager, dualCamera: DualCamera, targets: DualCameraOutputs, // AsyncTask is deprecated beginning API 30 executor: Executor = AsyncTask.SERIAL_EXECUTOR, callback: (CameraCaptureSession) -> Unit) { // Create 3 sets of output configurations: one for the logical camera, and // one for each of the physical cameras. val outputConfigsLogical = targets.first?.map { OutputConfiguration(it) } val outputConfigsPhysical1 = targets.second?.map { OutputConfiguration(it).apply { setPhysicalCameraId(dualCamera.physicalId1) } } val outputConfigsPhysical2 = targets.third?.map { OutputConfiguration(it).apply { setPhysicalCameraId(dualCamera.physicalId2) } } // Put all the output configurations into a single flat array val outputConfigsAll = arrayOf( outputConfigsLogical, outputConfigsPhysical1, outputConfigsPhysical2) .filterNotNull().flatMap { it } // Instantiate a session configuration that can be used to create a session val sessionConfiguration = SessionConfiguration( SessionConfiguration.SESSION_REGULAR, outputConfigsAll, executor, object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) = callback(session) // Omitting for brevity... override fun onConfigureFailed(session: CameraCaptureSession) = session.device.close() }) // Open the logical camera using the previously defined function openDualCamera(cameraManager, dualCamera, executor = executor) { // Finally create the session and return via callback it.createCaptureSession(sessionConfiguration) } }
Java
/** * Helper class definition that encapsulates 3 sets of output targets: ** 1. Logical camera * 2. First physical camera * 3. Second physical camera */ final class DualCameraOutputs { private final List
logicalCamera; private final List firstPhysicalCamera; private final List secondPhysicalCamera; public DualCameraOutputs(List logicalCamera, List firstPhysicalCamera, List third) { this.logicalCamera = logicalCamera; this.firstPhysicalCamera = firstPhysicalCamera; this.secondPhysicalCamera = third; } public List getLogicalCamera() { return logicalCamera; } public List getFirstPhysicalCamera() { return firstPhysicalCamera; } public List getSecondPhysicalCamera() { return secondPhysicalCamera; } } interface CameraCaptureSessionCallback { void callback(CameraCaptureSession cameraCaptureSession); } void createDualCameraSession(CameraManager cameraManager, DualCamera dualCamera, DualCameraOutputs targets, Executor executor, CameraCaptureSessionCallback cameraCaptureSessionCallback) { // Create 3 sets of output configurations: one for the logical camera, and // one for each of the physical cameras. List outputConfigsLogical = targets.getLogicalCamera().stream() .map(OutputConfiguration::new) .collect(Collectors.toList()); List outputConfigsPhysical1 = targets.getFirstPhysicalCamera().stream() .map(s -> { OutputConfiguration outputConfiguration = new OutputConfiguration(s); outputConfiguration.setPhysicalCameraId(dualCamera.physicalId1); return outputConfiguration; }) .collect(Collectors.toList()); List outputConfigsPhysical2 = targets.getSecondPhysicalCamera().stream() .map(s -> { OutputConfiguration outputConfiguration = new OutputConfiguration(s); outputConfiguration.setPhysicalCameraId(dualCamera.physicalId2); return outputConfiguration; }) .collect(Collectors.toList()); // Put all the output configurations into a single flat array List outputConfigsAll = Stream.of( outputConfigsLogical, outputConfigsPhysical1, outputConfigsPhysical2 ) .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.toList()); // Instantiate a session configuration that can be used to create a session SessionConfiguration sessionConfiguration = new SessionConfiguration( SessionConfiguration.SESSION_REGULAR, outputConfigsAll, executor, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { cameraCaptureSessionCallback.callback(cameraCaptureSession); } // Omitting for brevity... @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { cameraCaptureSession.getDevice().close(); } }); // Open the logical camera using the previously defined function openDualCamera(cameraManager, dualCamera, executor, (CameraDevice c) -> // Finally create the session and return via callback c.createCaptureSession(sessionConfiguration)); }
Consulta
createCaptureSession
per informazioni su quale combinazione di stream è supportata. La combinazione di flussi è per più stream su una singola videocamera logica. La compatibilità si estende all'utilizzo della stessa configurazione e alla sostituzione di uno di questi flussi con due stream provenienti da due videocamere fisiche che fanno parte della stessa videocamera logica.
Con la sessione della videocamera pronta, invia le richieste di acquisizione desiderate. Ogni target della richiesta di acquisizione riceve i dati dalla videocamera fisica associata, se in uso, oppure utilizza la videocamera logica.
Esempio di caso d'uso di Zoom
È possibile utilizzare l'unione di videocamere fisiche in un unico stream in modo che gli utenti possano passare da una videocamera fisica all'altra per sperimentare un campo visivo diverso, acquisendo in modo efficace un "livello di zoom" diverso.
Inizia selezionando la coppia di videocamere fisiche per consentire agli utenti di passare da una videocamera all'altra. Per ottenere il massimo effetto, puoi scegliere la coppia di fotocamere che forniscono la lunghezza focale minima e massima disponibili.
Kotlin
fun findShortLongCameraPair(manager: CameraManager, facing: Int? = null): DualCamera? { return findDualCameras(manager, facing).map { val characteristics1 = manager.getCameraCharacteristics(it.physicalId1) val characteristics2 = manager.getCameraCharacteristics(it.physicalId2) // Query the focal lengths advertised by each physical camera val focalLengths1 = characteristics1.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F) val focalLengths2 = characteristics2.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F) // Compute the largest difference between min and max focal lengths between cameras val focalLengthsDiff1 = focalLengths2.maxOrNull()!! - focalLengths1.minOrNull()!! val focalLengthsDiff2 = focalLengths1.maxOrNull()!! - focalLengths2.minOrNull()!! // Return the pair of camera IDs and the difference between min and max focal lengths if (focalLengthsDiff1 < focalLengthsDiff2) { Pair(DualCamera(it.logicalId, it.physicalId1, it.physicalId2), focalLengthsDiff1) } else { Pair(DualCamera(it.logicalId, it.physicalId2, it.physicalId1), focalLengthsDiff2) } // Return only the pair with the largest difference, or null if no pairs are found }.maxByOrNull { it.second }?.first }
Java
// Utility functions to find min/max value in float[] float findMax(float[] array) { float max = Float.NEGATIVE_INFINITY; for(float cur: array) max = Math.max(max, cur); return max; } float findMin(float[] array) { float min = Float.NEGATIVE_INFINITY; for(float cur: array) min = Math.min(min, cur); return min; } DualCamera findShortLongCameraPair(CameraManager manager, Integer facing) { return findDualCameras(manager, facing).stream() .map(c -> { CameraCharacteristics characteristics1; CameraCharacteristics characteristics2; try { characteristics1 = manager.getCameraCharacteristics(c.physicalId1); characteristics2 = manager.getCameraCharacteristics(c.physicalId2); } catch (CameraAccessException e) { e.printStackTrace(); return null; } // Query the focal lengths advertised by each physical camera float[] focalLengths1 = characteristics1.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS); float[] focalLengths2 = characteristics2.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS); // Compute the largest difference between min and max focal lengths between cameras Float focalLengthsDiff1 = findMax(focalLengths2) - findMin(focalLengths1); Float focalLengthsDiff2 = findMax(focalLengths1) - findMin(focalLengths2); // Return the pair of camera IDs and the difference between min and max focal lengths if (focalLengthsDiff1 < focalLengthsDiff2) { return new Pair<>(new DualCamera(c.logicalId, c.physicalId1, c.physicalId2), focalLengthsDiff1); } else { return new Pair<>(new DualCamera(c.logicalId, c.physicalId2, c.physicalId1), focalLengthsDiff2); } }) // Return only the pair with the largest difference, or null if no pairs are found .max(Comparator.comparing(pair -> pair.second)).get().first; }
Un'architettura sensata sarebbe avere due SurfaceViews
, uno per ogni stream.
Questi SurfaceViews
vengono scambiati in base all'interazione dell'utente, in modo che solo uno sia visibile in un determinato momento.
Il seguente codice mostra come aprire la videocamera logica, configurare le uscite della videocamera, creare una sessione della videocamera e avviare due stream di anteprima:
Kotlin
val cameraManager: CameraManager = ... // Get the two output targets from the activity / fragment val surface1 = ... // from SurfaceView val surface2 = ... // from SurfaceView val dualCamera = findShortLongCameraPair(manager)!! val outputTargets = DualCameraOutputs( null, mutableListOf(surface1), mutableListOf(surface2)) // Here you open the logical camera, configure the outputs and create a session createDualCameraSession(manager, dualCamera, targets = outputTargets) { session -> // Create a single request which has one target for each physical camera // NOTE: Each target receive frames from only its associated physical camera val requestTemplate = CameraDevice.TEMPLATE_PREVIEW val captureRequest = session.device.createCaptureRequest(requestTemplate).apply { arrayOf(surface1, surface2).forEach { addTarget(it) } }.build() // Set the sticky request for the session and you are done session.setRepeatingRequest(captureRequest, null, null) }
Java
CameraManager manager = ...; // Get the two output targets from the activity / fragment Surface surface1 = ...; // from SurfaceView Surface surface2 = ...; // from SurfaceView DualCamera dualCamera = findShortLongCameraPair(manager, null); DualCameraOutputs outputTargets = new DualCameraOutputs( null, Collections.singletonList(surface1), Collections.singletonList(surface2)); // Here you open the logical camera, configure the outputs and create a session createDualCameraSession(manager, dualCamera, outputTargets, null, (session) -> { // Create a single request which has one target for each physical camera // NOTE: Each target receive frames from only its associated physical camera CaptureRequest.Builder captureRequestBuilder; try { captureRequestBuilder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); Arrays.asList(surface1, surface2).forEach(captureRequestBuilder::addTarget); // Set the sticky request for the session and you are done session.setRepeatingRequest(captureRequestBuilder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } });
Non ti resta che fornire all'utente un'interfaccia utente per passare da una piattaforma all'altra, ad esempio un pulsante o toccando due volte SurfaceView
. Puoi anche eseguire qualche forma di analisi delle scene e passare automaticamente da uno stream all'altro.
Distorsione obiettivo
Tutti gli obiettivi producono una certa distorsione. In Android, puoi eseguire query sulla distorsione creata dagli obiettivi utilizzando CameraCharacteristics.LENS_DISTORTION
, che sostituisce CameraCharacteristics.LENS_RADIAL_DISTORTION
.
Per le videocamere logiche, la distorsione è minima e l'applicazione può utilizzare
i fotogrammi più o meno poiché provengono dalla videocamera. Per le fotocamere fisiche, esistono configurazioni dell'obiettivo potenzialmente molto diverse, in particolare sugli obiettivi grandangolari.
Alcuni dispositivi possono implementare la correzione automatica della distorsione tramite CaptureRequest.DISTORTION_CORRECTION_MODE
.
Per impostazione predefinita, la correzione della distorsione è attiva per la maggior parte dei dispositivi.
Kotlin
val cameraSession: CameraCaptureSession = ... // Use still capture template to build the capture request val captureRequest = cameraSession.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE ) // Determine if this device supports distortion correction val characteristics: CameraCharacteristics = ... val supportsDistortionCorrection = characteristics.get( CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES )?.contains( CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY ) ?: false if (supportsDistortionCorrection) { captureRequest.set( CaptureRequest.DISTORTION_CORRECTION_MODE, CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY ) } // Add output target, set other capture request parameters... // Dispatch the capture request cameraSession.capture(captureRequest.build(), ...)
Java
CameraCaptureSession cameraSession = ...; // Use still capture template to build the capture request CaptureRequest.Builder captureRequestBuilder = null; try { captureRequestBuilder = cameraSession.getDevice().createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE ); } catch (CameraAccessException e) { e.printStackTrace(); } // Determine if this device supports distortion correction CameraCharacteristics characteristics = ...; boolean supportsDistortionCorrection = Arrays.stream( characteristics.get( CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES )) .anyMatch(i -> i == CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY); if (supportsDistortionCorrection) { captureRequestBuilder.set( CaptureRequest.DISTORTION_CORRECTION_MODE, CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY ); } // Add output target, set other capture request parameters... // Dispatch the capture request cameraSession.capture(captureRequestBuilder.build(), ...);
L'impostazione di una richiesta di acquisizione in questa modalità può influire sulla frequenza fotogrammi prodotta dalla fotocamera. Puoi scegliere di impostare la correzione della distorsione solo sulle acquisizioni di immagini fisse.