Catatan: Halaman ini merujuk ke paket Camera2. Kecuali aplikasi Anda memerlukan fitur tingkat rendah tertentu dari Camera2, sebaiknya gunakan CameraX. CameraX dan Camera2 mendukung Android 5.0 (level API 21) dan versi yang lebih baru.
Multi-kamera diperkenalkan dengan Android 9 (API level 28). Sejak dirilis, telah hadir ke pasar yang mendukung API. Banyak kasus penggunaan multi-kamera dikaitkan erat dengan konfigurasi perangkat keras tertentu. Dengan kata lain, tidak semua kasus penggunaan kompatibel dengan setiap perangkat, sehingga membuat multi-kamera memiliki kandidat yang baik untuk Fitur Play Penayangan.
Beberapa kasus penggunaan umum antara lain:
- Zoom: beralih antarkamera bergantung pada area pemangkasan atau fokus yang diinginkan paruh.
- Depth: menggunakan beberapa kamera untuk membuat peta kedalaman.
- Bokeh: menggunakan informasi kedalaman yang disimpulkan untuk menyimulasikan foto sempit seperti DSLR rentang fokus yang tepat.
Perbedaan antara kamera logis dan fisik
Untuk memahami API multi-kamera, Anda harus memahami perbedaan antara kamera logis dan fisik. Sebagai referensi, pertimbangkan perangkat dengan tiga kamera belakang. Dalam contoh ini, masing-masing dari tiga kamera belakang dianggap sebagai kamera fisik. Kamera logis kemudian adalah pengelompokan dua atau lebih kamera fisik tersebut. Output dari logika kamera dapat berupa aliran yang berasal dari salah satu kamera fisik yang mendasarinya, atau aliran gabungan yang berasal dari lebih dari satu kamera fisik yang mendasarinya secara bersamaan. Apa pun itu, streaming akan ditangani oleh Hardware kamera Lapisan Abstraksi (HAL).
Banyak produsen ponsel mengembangkan aplikasi kamera pihak pertama, yang biasanya telah terinstal sebelumnya di perangkat mereka. Untuk menggunakan semua kemampuan perangkat keras, mereka dapat menggunakan API pribadi atau tersembunyi atau menerima perlakuan khusus dari implementasi {i>driver<i} yang tidak dapat diakses oleh aplikasi lain. Agak besar perangkat menerapkan konsep kamera logis dengan menyediakan aliran gabungan dari {i>frame<i} dari kamera fisik yang berbeda, tetapi hanya untuk hak istimewa tertentu menggunakan berbagai aplikasi obrolan. Sering kali, hanya satu dari kamera fisik yang terpapar Google Workspace for Education. Situasi untuk developer pihak ketiga sebelum Android 9 adalah diilustrasikan dalam diagram berikut:
Mulai Android 9, API pribadi tidak lagi diizinkan di aplikasi Android. Dengan disertakannya dukungan multi-kamera dalam framework, Android sangat menyarankan agar produsen ponsel mengekspos kamera yang logis untuk semua kamera fisik yang menghadap ke arah yang sama. Berikut ini adalah developer pihak ketiga akan melihatnya di perangkat yang menjalankan Android 9 dan lebih tinggi:
Fitur yang disediakan kamera logis sepenuhnya bergantung pada penerapan OEM dari Camera HAL. Misalnya, perangkat seperti Pixel 3 mengimplementasikan logika kamera sedemikian rupa sehingga ia memilih salah satu kamera fisiknya berdasarkan panjang fokus dan area pemangkasan yang diminta.
API multi-kamera
API baru menambahkan konstanta, class, dan metode baru berikut:
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
CameraCharacteristics.getPhysicalCameraIds()
CameraCharacteristics.getAvailablePhysicalCameraRequestKeys()
CameraDevice.createCaptureSession(SessionConfiguration config)
CameraCharacteritics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
OutputConfiguration
danSessionConfiguration
Karena perubahan pada Compatibility Definition Document (CDD) Android, multi-kamera API juga dilengkapi dengan ekspektasi tertentu dari developer. Perangkat dengan kamera ganda sudah ada sebelum Android 9, namun membuka lebih dari satu kamera secara bersamaan melakukan {i>trial and error<i}. Pada Android 9 dan yang lebih baru, multi-kamera memberikan seperangkat aturan untuk menentukan kapan memungkinkan untuk membuka sepasang kamera yang merupakan bagian dari kamera logis yang sama.
Pada umumnya, perangkat yang menjalankan Android 9 dan yang lebih tinggi mengekspos semua kamera (kecuali kemungkinan jenis sensor yang kurang umum seperti inframerah) beserta kamera logis yang lebih mudah digunakan. Untuk setiap kombinasi aliran data yang dijamin berfungsi, satu streaming milik kamera logis dapat diganti dengan dua aliran dari kamera fisik yang mendasarinya.
Beberapa streaming secara bersamaan
Menggunakan beberapa streaming kamera secara bersamaan
membahas aturan untuk menggunakan beberapa streaming secara bersamaan dalam satu kamera.
Dengan satu tambahan penting, aturan yang sama berlaku untuk beberapa kamera.
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
menjelaskan cara mengganti YUV_420_888 atau streaming mentah yang logis dengan dua
secara fisik. Artinya, setiap aliran data jenis YUV atau RAW dapat diganti dengan
dua {i>stream<i} dengan jenis
dan ukuran yang identik. Anda dapat memulai dengan aliran kamera
konfigurasi dijamin berikut untuk perangkat kamera tunggal:
- Streaming 1: Jenis YUV, ukuran
MAXIMUM
dari kamera logisid = 0
Kemudian, perangkat dengan dukungan multi-kamera memungkinkan Anda membuat sesi mengganti streaming YUV yang logis dengan dua aliran fisik:
- Streaming 1: Jenis YUV,
MAXIMUM
ukuran dari kamera fisikid = 1
- Streaming 2: Jenis YUV,
MAXIMUM
ukuran dari kamera fisikid = 2
Anda dapat mengganti streaming YUV atau RAW dengan dua streaming yang setara jika dan hanya jika
kedua kamera itu adalah bagian dari pengelompokan kamera logis, yang tercantum di bawah
CameraCharacteristics.getPhysicalCameraIds()
Jaminan yang diberikan oleh kerangka kerja ini hanyalah minimum yang diperlukan untuk mendapatkan {i>frame<i} dari lebih dari satu kamera fisik secara bersamaan. Streaming tambahan didukung di sebagian besar perangkat, kadang-kadang bahkan memungkinkan membuka beberapa perangkat perangkat kamera secara terpisah. Karena itu bukan jaminan dari Google Cloud, sehingga perlu melakukan pengujian dan tuning per perangkat menggunakan coba-coba.
Membuat sesi dengan beberapa kamera fisik
Saat menggunakan kamera fisik pada perangkat yang mendukung multi-kamera, buka satu
CameraDevice
(kamera logis) dan berinteraksi dengannya dalam satu
sesi. Membuat satu sesi menggunakan API
CameraDevice.createCaptureSession(SessionConfiguration config)
, yang sebelumnya
ditambahkan di API level 28. Konfigurasi sesi memiliki sejumlah output
konfigurasi yang berbeda, yang masing-masing memiliki
serangkaian target output dan, secara opsional,
ID kamera fisik yang diinginkan.
Permintaan pengambilan memiliki target output yang terkait dengannya. Kerangka kerja menentukan kamera fisik (atau logis) yang menjadi tujuan pengiriman permintaan berdasarkan target output apa yang terpasang. Jika target output sesuai dengan salah satu target output yang dikirim sebagai konfigurasi output beserta ID kamera, kemudian kamera fisik itu menerima dan memproses permintaan.
Menggunakan sepasang kamera fisik
Tambahan lain untuk API kamera untuk multi-kamera adalah kemampuan untuk mengidentifikasi kamera logis dan menemukan kamera fisik di belakangnya. Anda dapat menentukan untuk membantu mengidentifikasi pasangan kamera fisik yang potensial yang dapat Anda gunakan untuk mengganti salah satu streaming kamera logis:
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; }
Penanganan status kamera fisik dikontrol oleh kamera logis. Kepada membuka "kamera ganda", membuka kamera logis yang sesuai dengan kamera:
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); } }); }
Selain memilih kamera yang akan dibuka, prosesnya sama dengan membuka kamera dalam versi Android sebelumnya. Membuat sesi pengambilan gambar menggunakan API konfigurasi sesi memberi tahu kerangka kerja untuk mengaitkan target tertentu dengan ID kamera fisik tertentu:
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)); }
Lihat
createCaptureSession
untuk mendapatkan informasi tentang kombinasi streaming yang didukung. Menggabungkan stream
untuk beberapa streaming pada satu kamera logis. Kompatibilitas mencakup
menggunakan konfigurasi yang sama dan mengganti salah satu streaming tersebut dengan dua streaming
dari dua kamera fisik yang merupakan
bagian dari kamera logis yang sama.
Dengan sesi kamera siap, kirim pesan yang permintaan pengambilan. Masing-masing target permintaan pengambilan akan menerima datanya dari permintaan fisik kamera, jika ada yang sedang digunakan, atau beralih kembali ke kamera logis.
Contoh kasus penggunaan Zoom
Dimungkinkan untuk menggunakan penggabungan kamera fisik menjadi satu {i>stream<i} sehingga di mana pengguna dapat beralih antar kamera fisik yang berbeda untuk merasakan ruang pandang yang berbeda, secara efektif menangkap “tingkat zoom” yang berbeda.
Mulai dengan memilih sepasang kamera fisik untuk memungkinkan pengguna beralih di antara keduanya. Untuk efek maksimum, Anda dapat memilih sepasang kamera yang menyediakan panjang fokus minimum dan maksimum yang tersedia.
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; }
Arsitektur yang masuk akal untuk
ini adalah memiliki dua
SurfaceViews
—satu untuk setiap aliran data.
SurfaceViews
ini ditukar berdasarkan interaksi pengguna sehingga hanya satu yang
terlihat di waktu tertentu.
Kode berikut menunjukkan cara membuka kamera logis, mengonfigurasi kamera output, membuat sesi kamera, dan memulai dua streaming pratinjau:
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(); } });
Yang perlu dilakukan hanyalah menyediakan UI bagi pengguna untuk beralih di antara keduanya
platform, seperti tombol atau mengetuk dua kali SurfaceView
. Anda bahkan dapat
melakukan beberapa bentuk analisis suasana dan beralih di antara dua streaming
secara otomatis.
Distorsi lensa
Semua lensa menghasilkan sejumlah distorsi tertentu. Di Android, Anda dapat melakukan kueri
distorsi yang ditimbulkan oleh lensa menggunakan
CameraCharacteristics.LENS_DISTORTION
,
yang menggantikan model
CameraCharacteristics.LENS_RADIAL_DISTORTION
.
Untuk kamera logis, distorsi minimal dan aplikasi dapat menggunakan
lebih banyak atau lebih sedikit {i>frame<i}
yang berasal dari kamera. Untuk kamera fisik,
berpotensi memiliki konfigurasi lensa yang sangat
berbeda, terutama pada lensa
lensa.
Beberapa perangkat dapat mengimplementasikan koreksi distorsi otomatis melalui
CaptureRequest.DISTORTION_CORRECTION_MODE
Koreksi distorsi secara default aktif di sebagian besar perangkat.
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(), ...);
Menyetel permintaan pengambilan dalam mode ini dapat memengaruhi kecepatan frame yang yang dihasilkan kamera. Anda dapat memilih untuk menyetel koreksi distorsi hanya pada pengambilan gambar diam.