Nota: questa pagina fa riferimento al pacchetto Camera2. A meno che la tua app non richieda funzionalità specifiche di basso livello di Camera2, ti consigliamo di utilizzare CameraX. Sia CameraX che Camera2 supportano Android 5.0 (livello API 21) e versioni successive.
La funzionalità multicamera è stata introdotta con Android 9 (livello API 28). Dalla sua uscita, sono stati introdotti sul mercato dispositivi che supportano l'API. Molti casi d'uso con più fotocamere sono strettamente legati a una configurazione hardware specifica. In altre parole, non tutti i casi d'uso sono compatibili con ogni dispositivo, il che rende le funzionalità multi-fotocamera un buon candidato per Play Feature Delivery.
Alcuni casi d'uso tipici includono:
- Zoom: passaggio da una fotocamera all'altra a seconda della regione di ritaglio o della lunghezza focale scelta.
- Profondità: utilizza più fotocamere per creare una mappa di profondità.
- Bokeh: utilizza le informazioni sulla profondità dedotte per simulare un intervallo di messa a fuoco ridotto simile a quello di una fotocamera reflex digitale.
La differenza tra fotocamere logiche e fisiche
Per comprendere l'API multi-camera, è necessario comprendere la differenza tra fotocamere logiche e fisiche. Ad esempio, considera un dispositivo con tre fotocamere posteriori. In questo esempio, ciascuna delle tre fotocamere posteriori è considerata una fotocamera fisica. Una fotocamera logica è quindi un raggruppamento di due o più di queste fotocamere fisiche. L'output della fotocamera logica può essere uno stream proveniente da una delle fotocamere fisiche sottostanti o uno stream combinato proveniente da più fotocamere fisiche sottostanti contemporaneamente. In entrambi i casi, lo stream viene gestito dall'Hardware Abstraction Layer (HAL) della fotocamera.
Molti produttori di smartphone sviluppano applicazioni per fotocamere proprietarie, che di solito sono preinstallate sui loro dispositivi. Per utilizzare tutte le funzionalità dell'hardware, potrebbero utilizzare API private o nascoste o ricevere un trattamento speciale dall'implementazione del driver a cui altre applicazioni non hanno accesso. Alcuni dispositivi implementano il concetto di fotocamere logiche fornendo un flusso combinato di frame provenienti dalle diverse fotocamere fisiche, ma solo ad alcune applicazioni con privilegi. Spesso, solo una delle fotocamere fisiche è esposta al framework. La situazione per gli sviluppatori di terze parti prima di 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 del supporto della modalità multicamera nel framework, le best practice di Android consigliano vivamente ai produttori di smartphone 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 di vedere sui dispositivi con Android 9 e versioni successive:
Ciò che fornisce la fotocamera logica dipende interamente dall'implementazione OEM dell'HAL della fotocamera. Ad esempio, un dispositivo come Pixel 3 implementa la sua fotocamera logica in modo da scegliere una delle sue fotocamere fisiche in base alla lunghezza focale e alla regione di ritaglio richieste.
API multi-camera
La nuova API aggiunge le seguenti nuove costanti, classi e metodi:
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERACameraCharacteristics.getPhysicalCameraIds()CameraCharacteristics.getAvailablePhysicalCameraRequestKeys()CameraDevice.createCaptureSession(SessionConfiguration config)CameraCharacteritics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPEOutputConfigurationeSessionConfiguration
A causa delle modifiche al documento di definizione della compatibilità (CDD) di Android, l'API multicamera prevede anche alcune aspettative da parte degli sviluppatori. I dispositivi con doppia fotocamera esistevano prima di Android 9, ma l'apertura di più fotocamere contemporaneamente comportava tentativi ed errori. Su Android 9 e versioni successive, la funzionalità multicamera fornisce un insieme di regole per specificare quando è possibile aprire una coppia di fotocamere fisiche che fanno parte della stessa fotocamera logica.
Nella maggior parte dei casi, i dispositivi con Android 9 e versioni successive espongono tutte le fotocamere fisiche (ad eccezione, probabilmente, dei tipi di sensori meno comuni come gli infrarossi) insieme a una fotocamera logica più facile da usare. Per ogni combinazione di stream che funzionano, uno stream appartenente a una videocamera logica può essere sostituito da due stream delle videocamere fisiche sottostanti.
Più stream contemporaneamente
Utilizzo di più stream della videocamera contemporaneamente illustra 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 YUV_420_888 o RAW logico con due stream
fisici. In altre parole, ogni stream di tipo YUV o RAW può essere sostituito con due stream di tipo e dimensioni identici. Puoi iniziare con uno stream della videocamera con la seguente configurazione garantita per i dispositivi con una sola videocamera:
- Stream 1: tipo YUV, dimensioni
MAXIMUMdalla fotocamera logicaid = 0
Un dispositivo con supporto della modalità multicamera consente di creare una sessione sostituendo lo stream YUV logico con due stream fisici:
- Stream 1: tipo YUV, dimensioni
MAXIMUMdalla fotocamera fisicaid = 1 - Stream 2: tipo YUV, dimensioni
MAXIMUMdalla fotocamera fisicaid = 2
Puoi sostituire uno stream YUV o RAW con due stream equivalenti se e solo se
queste due fotocamere fanno parte di un raggruppamento di fotocamere logiche, elencato in
CameraCharacteristics.getPhysicalCameraIds().
Le garanzie fornite dal framework sono solo il minimo indispensabile per ottenere frame da più di una fotocamera fisica contemporaneamente. La maggior parte dei dispositivi supporta stream aggiuntivi, a volte consentendo anche l'apertura indipendente di più dispositivi con fotocamera fisica. Poiché non si tratta di una garanzia assoluta del framework, per farlo è necessario eseguire test e ottimizzazioni per ogni dispositivo utilizzando il metodo di prova ed errore.
Creazione di una sessione con più fotocamere fisiche
Quando utilizzi fotocamere fisiche su un dispositivo con più fotocamere, apri una singola CameraDevice (la fotocamera logica) e interagisci con questa in un'unica sessione. Crea la singola sessione utilizzando l'API CameraDevice.createCaptureSession(SessionConfiguration config), che è stata aggiunta nel livello API 28. La configurazione della sessione ha un numero di configurazioni di output, ognuna delle quali ha un insieme di target di output e, facoltativamente, un ID fotocamera fisica scelto.
Alle richieste di acquisizione è associata una destinazione di output. Il framework determina a quale fotocamera fisica (o logica) vengono inviate le richieste in base alla destinazione di output collegata. Se la destinazione di output corrisponde a una delle destinazioni di output inviate come configurazione di output insieme a un ID fotocamera fisica, la fotocamera fisica riceve ed elabora la richiesta.
Utilizzo di un paio di videocamere fisiche
Un'altra aggiunta alle API della fotocamera per la multicamera è la possibilità di identificare le fotocamere logiche e trovare le fotocamere fisiche dietro di esse. Puoi definire una funzione per identificare le potenziali coppie di fotocamere fisiche che puoi utilizzare per sostituire uno degli stream della videocamera logica:
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<DualCamera> {
val dualCameras = MutableList<DualCamera>()
// 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;
}
}
List<DualCamera> findDualCameras(CameraManager manager, Integer facing) {
List<DualCamera> dualCameras = new ArrayList<>();
List<String> 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 fotocamere fisiche è controllata dalla fotocamera logica. Per aprire una "doppia fotocamera", apri la fotocamera logica corrispondente alle fotocamere 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 è la stessa di quella per aprire una fotocamera nelle versioni precedenti di Android. La creazione di una sessione di acquisizione utilizzando la nuova API di configurazione della sessione indica al framework di associare determinati target a ID di fotocamere fisiche specifici:
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<Surface>?, MutableList<Surface>?, MutableList<Surface>?>
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 using the callback
it.createCaptureSession(sessionConfiguration)
}
}
Java
/**
* Helper class definition that encapsulates 3 sets of output targets:
* <p>
* 1. Logical camera
* 2. First physical camera
* 3. Second physical camera
*/
final class DualCameraOutputs {
private final List<Surface> logicalCamera;
private final List<Surface> firstPhysicalCamera;
private final List<Surface> secondPhysicalCamera;
public DualCameraOutputs(List<Surface> logicalCamera, List<Surface> firstPhysicalCamera, List<Surface> third) {
this.logicalCamera = logicalCamera;
this.firstPhysicalCamera = firstPhysicalCamera;
this.secondPhysicalCamera = third;
}
public List<Surface> getLogicalCamera() {
return logicalCamera;
}
public List<Surface> getFirstPhysicalCamera() {
return firstPhysicalCamera;
}
public List<Surface> 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<OutputConfiguration> outputConfigsLogical = targets.getLogicalCamera().stream()
.map(OutputConfiguration::new)
.collect(Collectors.toList());
List<OutputConfiguration> outputConfigsPhysical1 = targets.getFirstPhysicalCamera().stream()
.map(s -> {
OutputConfiguration outputConfiguration = new OutputConfiguration(s);
outputConfiguration.setPhysicalCameraId(dualCamera.physicalId1);
return outputConfiguration;
})
.collect(Collectors.toList());
List<OutputConfiguration> 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<OutputConfiguration> 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 using the callback
c.createCaptureSession(sessionConfiguration));
}
Per informazioni sulla combinazione di stream supportata, consulta createCaptureSession. La combinazione di stream è destinata a più stream su una singola videocamera logica. La compatibilità si estende all'utilizzo della stessa configurazione e alla sostituzione di uno di questi stream con due stream provenienti da due fotocamere fisiche che fanno parte della stessa fotocamera logica.
Con la sessione della fotocamera pronta, invia le richieste di acquisizione scelte. Ogni target della richiesta di acquisizione riceve i dati dalla fotocamera fisica associata, se in uso, o torna alla fotocamera logica.
Caso d'uso di esempio di Zoom
È possibile unire le 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 di fatto un "livello di zoom" diverso.
Per prima cosa, seleziona la coppia di fotocamere fisiche tra cui gli utenti possono passare. Per ottenere il massimo effetto, puoi scegliere la coppia di fotocamere che fornisce la lunghezza focale minima e massima disponibile.
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 per questo caso sarebbe avere due SurfaceViews, uno per ogni stream. Questi SurfaceViews vengono scambiati in base all'interazione dell'utente, in modo che ne sia visibile solo uno alla volta.
Il seguente codice mostra come aprire la fotocamera logica, configurare le uscite della fotocamera, creare una sessione della fotocamera 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 resta che fornire un'interfaccia utente che consenta all'utente di passare da una superficie all'altra, ad esempio un pulsante o un doppio tocco su SurfaceView. Potresti persino eseguire una sorta di analisi della scena e passare automaticamente da uno stream all'altro.
Distorsione dell'obiettivo
Tutti gli obiettivi producono una certa quantità di distorsione. In Android, puoi eseguire query sulla distorsione creata dagli obiettivi utilizzando CameraCharacteristics.LENS_DISTORTION, che sostituisce CameraCharacteristics.LENS_RADIAL_DISTORTION, ora ritirato. Per le fotocamere logiche, la distorsione è minima e l'applicazione può utilizzare i frame più o meno così come provengono dalla fotocamera. Per le fotocamere fisiche, esistono configurazioni degli obiettivi potenzialmente molto diverse, in particolare per gli obiettivi grandangolari.
Alcuni dispositivi potrebbero implementare la correzione automatica delle distorsioni utilizzando
CaptureRequest.DISTORTION_CORRECTION_MODE.
La correzione delle distorsioni è attiva per impostazione predefinita 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 che può essere prodotta dalla fotocamera. Puoi scegliere di impostare la correzione delle distorsioni solo per l'acquisizione di immagini fisse.