API กล้องหลายตัว

หมายเหตุ: หน้านี้เกี่ยวข้องกับแพ็กเกจ camera2 เราขอแนะนำให้ใช้ cameraX เว้นแต่ว่าแอปของคุณต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป

กล้องหลายตัวเปิดตัวใน Android 9 (API ระดับ 28) ตั้งแต่เปิดตัว ได้ออกสู่ตลาดที่รองรับ API แล้ว กรณีการใช้งานกล้องหลายตัว ทำงานคู่กับการกำหนดค่าฮาร์ดแวร์ที่เฉพาะเจาะจงอย่างเหนียวแน่น กล่าวคือ ไม่ใช่ กรณีการใช้งานทั้งหมดใช้ได้กับอุปกรณ์ทุกเครื่อง ซึ่งทำให้ฟีเจอร์กล้องหลายตัว เป็นตัวเลือกที่ดีสำหรับฟีเจอร์ Play การนำส่ง

กรณีการใช้งานทั่วไปมีดังนี้

  • ซูม: สลับระหว่างกล้องต่างๆ โดยขึ้นอยู่กับพื้นที่ที่ครอบตัดหรือโฟกัสที่ต้องการ
  • ความลึก: การใช้กล้องหลายตัวเพื่อสร้างแผนที่ที่มีความลึก
  • โบเก้: ใช้ข้อมูลความลึกที่อนุมานเพื่อจำลองภาพมุมแคบเหมือนกล้อง DSLR ช่วงโฟกัส

ความแตกต่างระหว่างกล้องเชิงตรรกะและกล้องจริง

การทำความเข้าใจเกี่ยวกับ API กล้องหลายตัวจำเป็นต้องมีความเข้าใจในความแตกต่างระหว่าง กล้องตรรกะและกล้องจริง สำหรับการอ้างอิง ให้ลองพิจารณาอุปกรณ์ที่มี กล้องหลัง ในตัวอย่างนี้ กล้องหลัง 3 ตัว เป็นกล้องจริง กล้องเชิงตรรกะคือ การจัดกลุ่ม 2 กลุ่มขึ้นไป ของกล้องตัวอื่นๆ เอาต์พุตของตรรกะ กล้องอาจเป็นสตรีม ที่มาจากกล้องจริงตัวใดตัวหนึ่ง หรือสตรีมรวมที่มาจากกล้องจริงมากกว่า 1 ตัว พร้อมกัน ไม่ว่าจะเลือกวิธีใด ฮาร์ดแวร์กล้องจะเป็นผู้จัดการสตรีม Abstraction Layer (HAL)

ผู้ผลิตโทรศัพท์หลายรายพัฒนาแอปพลิเคชันกล้องบุคคลที่หนึ่ง ซึ่งโดยปกติแล้ว ติดตั้งมาล่วงหน้าบนอุปกรณ์ของตน หากต้องการใช้ความสามารถทั้งหมดของฮาร์ดแวร์ อาจใช้ API ส่วนตัวหรือ API ที่ซ่อนไว้ หรือได้รับการปฏิบัติเป็นพิเศษจาก การใช้งานไดรเวอร์ที่แอปพลิเคชันอื่นๆ ไม่มีสิทธิ์เข้าถึง ใช้บ้าง จะใช้แนวคิดเรื่องกล้องลอจิคัลโดยมอบสตรีมการผสมผสานระหว่าง เฟรมต่างๆ จากกล้องที่ติดมากับกล้องที่ต่างกัน หรือเป็นสิทธิ์เฉพาะบางอย่าง แอปพลิเคชัน บ่อยครั้งที่กล้องเพียงตัวใดตัวหนึ่งได้สัมผัสกับ สถานการณ์สำหรับนักพัฒนาซอฟต์แวร์บุคคลที่สามก่อน Android 9 คือ ที่แสดงในแผนภาพต่อไปนี้

วันที่
รูปที่ 1 ความสามารถของกล้องโดยทั่วไปจะใช้ได้เฉพาะกับ แอปพลิเคชันที่ได้รับสิทธิ์

ตั้งแต่ Android 9 เป็นต้นไป ระบบจะไม่อนุญาตให้ใช้ API ส่วนตัวในแอป Android อีกต่อไป ด้วยการรวมการสนับสนุนกล้องหลายตัวไว้ในเฟรมเวิร์ก Android ควรให้ผู้ผลิตโทรศัพท์ใช้กล้องเชิงตรรกะ สำหรับกล้องทุกตัวที่หันไปในทิศทางเดียวกัน ต่อไปนี้คือสิ่งที่ นักพัฒนาซอฟต์แวร์บุคคลที่สามควรคาดหวังว่าจะได้เห็นในอุปกรณ์ที่ใช้ Android 9 และ สูงกว่า:

วันที่
รูปที่ 2 นักพัฒนาซอฟต์แวร์มีสิทธิ์เข้าถึงอุปกรณ์กล้องทั้งหมดอย่างเต็มรูปแบบ เริ่มตั้งแต่ Android 9

สิ่งที่กล้องเชิงตรรกะมอบให้จะขึ้นอยู่กับการใช้งาน OEM ทั้งหมด ของ HAL ของกล้อง เช่น อุปกรณ์อย่าง Pixel 3 นำการใช้งานเชิงตรรกะมาใช้ ในลักษณะที่จะเลือกกล้องจริงตัวใดตัวหนึ่งจาก ความยาวโฟกัสและบริเวณที่ครอบตัดที่ขอ

API กล้องหลายตัว

โดย API ใหม่นี้จะเพิ่มค่าคงที่ คลาส และเมธอดใหม่ๆ ต่อไปนี้

เนื่องจากมีการเปลี่ยนแปลงเอกสารคำจำกัดความความเข้ากันได้ของ Android (CDD) นอกจากนี้ API กล้องหลายตัวยังมาพร้อมกับความคาดหวังบางอย่างจากนักพัฒนาซอฟต์แวร์ อุปกรณ์ ที่มีกล้องคู่มาก่อน Android 9 แต่การเปิดกล้องมากกว่า 1 ตัว เป็นการลองผิดลองถูก ใน Android 9 ขึ้นไป กล้องหลายตัว ให้ชุดกฎที่ระบุว่าคุณสามารถเปิด ที่เป็นส่วนหนึ่งของกล้องแบบลอจิคัลเดียวกัน

ในกรณีส่วนใหญ่ อุปกรณ์ที่ใช้ Android 9 ขึ้นไปจะเผยให้เห็นถึง กล้อง (ยกเว้นสำหรับเซ็นเซอร์ประเภทที่ไม่ค่อยเป็นที่นิยม เช่น อินฟราเรด) กล้องเชิงตรรกะที่ใช้งานง่าย สำหรับชุดสตรีมที่ รับประกันได้ว่าจะใช้งานได้ สตรีมหนึ่งที่เป็นของกล้องเชิงตรรกะสามารถแทนที่ได้โดย 2 สตรีมจากกล้องจริงที่อยู่ด้านล่าง

สตรีมหลายรายการพร้อมกัน

การใช้สตรีมจากกล้องหลายตัวพร้อมกัน ครอบคลุมกฎในการใช้สตรีมหลายรายการพร้อมกันในกล้องตัวเดียว ด้วยการเพิ่มที่สำคัญ 1 รายการ กฎเดียวกันจะใช้กับกล้องหลายตัว CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA อธิบายวิธีแทนที่ YUV_420_888 หรือสตรีมดิบเชิงตรรกะด้วย อุปกรณ์ต่างๆ กล่าวคือ สตรีม YUV หรือ RAW แต่ละประเภทสามารถแทนที่ด้วย สตรีมสองสตรีมที่มีประเภทและขนาดเหมือนกัน เริ่มจากสตรีมกล้องของ การกำหนดค่าที่รับประกันการแสดงผลสำหรับอุปกรณ์กล้องเดียวมีดังนี้

  • สตรีม 1: ประเภท YUV, MAXIMUM ขนาดจากกล้องเชิงตรรกะ id = 0

จากนั้น อุปกรณ์ที่รองรับกล้องหลายตัวจะช่วยให้คุณสร้างเซสชัน แทนที่สตรีม YUV เชิงตรรกะด้วยสตรีมทางกายภาพ 2 รายการ:

  • สตรีม 1: ประเภท YUV ขนาด MAXIMUM จากกล้องจริง id = 1
  • สตรีม 2: ประเภท YUV, MAXIMUM ขนาดจากกล้องจริง id = 2

คุณสามารถแทนที่สตรีม YUV หรือ RAW ด้วยสตรีม 2 รายการที่เทียบเท่ากันในกรณีที่และ กล้องทั้ง 2 ตัวนี้เป็นส่วนหนึ่งของการจัดกลุ่มกล้องเชิงตรรกะ ซึ่งแสดงอยู่ในส่วน CameraCharacteristics.getPhysicalCameraIds()

การรับประกันที่ระบุในเฟรมเวิร์กนี้เป็นเพียงข้อกำหนดขั้นต่ำเท่านั้น รับเฟรมจากกล้องจริงมากกว่า 1 ตัวได้พร้อมกัน สตรีมเพิ่มเติม ได้รับการรองรับในอุปกรณ์ส่วนใหญ่ บางครั้งก็อนุญาตให้เปิด อุปกรณ์กล้องแยกต่างหาก เนื่องจากไม่ใช่การรับประกันที่ยากจาก ซึ่งทำได้โดยต้องมีการทดสอบและปรับแต่งในแต่ละอุปกรณ์ การลองผิดลองถูก

การสร้างเซสชันที่มีกล้องหลายตัว

เมื่อใช้กล้องจริงในอุปกรณ์ที่เปิดใช้กล้องหลายตัว ให้เปิด CameraDevice (กล้องตรรกะ) และโต้ตอบกับกล้องภายใน เซสชัน สร้างเซสชันเดียวโดยใช้ API CameraDevice.createCaptureSession(SessionConfiguration config) เดิมคือ เพิ่มเข้ามาใน API ระดับ 28 การกำหนดค่าเซสชันมีเอาต์พุตจำนวนหนึ่ง การกำหนดค่า โดยแต่ละแบบจะมีชุดของเป้าหมายเอาต์พุต และอาจมี รหัสกล้องจริงที่ต้องการ

วันที่
รูปที่ 3 โมเดล SessionConfiguration และ OutputConfiguration

คำขอการบันทึกมีเป้าหมายเอาต์พุตเชื่อมโยงอยู่ เฟรมเวิร์ก กำหนดกล้องจริง (หรือกล้องที่ถูกต้อง) ที่ระบบจะส่งคำขอ สิ่งที่แนบกับเป้าหมายเอาต์พุต หากเป้าหมายเอาต์พุตสอดคล้องกับหนึ่งใน เป้าหมายเอาต์พุตที่ส่งเป็นการกำหนดค่าเอาต์พุตพร้อมกับเอาต์พุตจริง กล้อง กล้องตัวดังกล่าวจะได้รับและดำเนินการตามคำขอ

การใช้กล้องจริง 2 ตัว

อีกหนึ่งสิ่งที่เพิ่มเข้ามาจาก API กล้องถ่ายรูปสำหรับกล้องหลายตัวคือความสามารถในการระบุ กล้องตรรกะและค้นหากล้องจริงที่อยู่ด้านหลัง คุณสามารถกำหนด เพื่อช่วยระบุกล้องจริงที่อาจใช้ได้ เพื่อเปลี่ยนสตรีมจากกล้องลอจิคัล:

KotlinJava
/**
     * 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
/**
     * 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

การจัดการสถานะของกล้องจริงจะควบคุมด้วยกล้องตรรกะ ถึง เปิด "กล้องคู่" ให้เปิดกล้องเชิงตรรกะที่สอดคล้องกับ กล้อง:

KotlinJava
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()
           
})
   
}
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);
           
}
       
});
   
}

นอกจากการเลือกกล้องที่จะเปิดแล้ว กระบวนการจะเหมือนกับการเปิด กล้องใน Android เวอร์ชันก่อนหน้านี้ การสร้างเซสชันการจับภาพโดยใช้ การกำหนดค่าเซสชัน API จะบอกเฟรมเวิร์กให้เชื่อมโยงเป้าหมายบางอย่างกับ รหัสกล้องจริงที่เฉพาะเจาะจง:

KotlinJava
/**
 * Helper type definition that encapsulates 3 sets of output targets:
 *
 *   1. Logical camera
 *   2. First physical camera
 *   3. Second physical camera
 */

typealias DualCameraOutputs =
       
Triple
/**
 * 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

โปรดดู createCaptureSession เพื่อดูข้อมูลว่ารองรับชุดสตรีมใด การรวมสตรีม มีไว้สำหรับสตรีมหลายรายการในกล้องแบบลอจิคัลเดียว ความสามารถในการใช้งานร่วมกันรวมถึง โดยใช้การกำหนดค่าเดียวกันและแทนที่สตรีมใดสตรีมหนึ่งด้วย 2 สตรีม จากกล้องจริง 2 ตัวที่อยู่ในกล้องตรรกะเดียวกัน

ด้วยฟังก์ชัน เซสชันกล้อง พร้อมแล้ว ส่ง การเก็บข้อมูล ชิ้น เป้าหมายของคำขอบันทึกได้รับข้อมูลของตนจากทางกายภาพที่เกี่ยวข้อง กล้องถ่ายรูป หากมีการใช้งานอยู่ หรือถอยกลับไปใช้กล้องเชิงตรรกะ

ตัวอย่างกรณีการใช้งานของ Zoom

คุณสามารถใช้การรวมกล้องจริงไว้ในสตรีมเดียว ที่ผู้ใช้สามารถสลับไปมาระหว่างกล้องจริงแบบต่างๆ เพื่อสัมผัสประสบการณ์ ขอบเขตการมองเห็นที่แตกต่างกัน โดยจับภาพ "ระดับการซูม" ที่แตกต่างกันได้อย่างมีประสิทธิภาพ

วันที่
รูปที่ 4 ตัวอย่างการสลับกล้องเป็นกรณีการใช้งานระดับการซูม (จากโฆษณา Pixel 3)

เริ่มต้นด้วยการเลือกกล้องจริง 2 ตัวเพื่ออนุญาตให้ผู้ใช้เปลี่ยน ระหว่าง คุณสามารถเลือกกล้องคู่ที่ให้ผลลัพธ์สูงสุด ความยาวโฟกัสต่ำสุดและสูงสุดที่มี

KotlinJava
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
}
// 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;
   
}

สถาปัตยกรรมที่เหมาะสมในกรณีนี้คือต้องมี SurfaceViews - 1 รายการต่อสตรีม ระบบจะสลับ SurfaceViews เหล่านี้ตามการโต้ตอบของผู้ใช้เพื่อให้มีเพียงรายการเดียวเท่านั้น ที่มองเห็นได้ ณ เวลาใดเวลาหนึ่ง

โค้ดต่อไปนี้แสดงวิธีเปิดกล้องตรรกะ กำหนดค่ากล้อง สร้างเซสชันกล้อง และเริ่มสตรีมตัวอย่าง 2 รายการ ดังนี้

KotlinJava
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)
}
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();
           
}
       
});

ที่เหลือก็แค่จัดเตรียม UI เพื่อให้ผู้ใช้สลับระหว่าง เช่น ปุ่มหรือการแตะสองครั้งที่ SurfaceView คุณสามารถ วิเคราะห์ฉากในรูปแบบใดรูปแบบหนึ่งและสลับระหว่างสตรีมทั้งสอง โดยอัตโนมัติ

การบิดเบี้ยวของเลนส์

เลนส์ทุกตัวทำให้เกิดการบิดเบี้ยวในปริมาณหนึ่ง ใน Android คุณสามารถค้นหา การบิดเบี้ยวที่เกิดจากเลนส์โดยใช้ CameraCharacteristics.LENS_DISTORTION โดยจะแทนที่แท็ก CameraCharacteristics.LENS_RADIAL_DISTORTION สําหรับกล้องแบบลอจิคัล ความผิดเพี้ยนจะน้อยและแอปพลิเคชันของคุณสามารถใช้ จะได้เฟรมมากขึ้นหรือน้อยลง เพราะมาจากกล้อง สำหรับกล้องจริง อาจมีการกำหนดค่าเลนส์แตกต่างกันมาก โดยเฉพาะสำหรับเลนส์มุมกว้าง เลนส์

อุปกรณ์บางเครื่องอาจใช้การแก้ไขการบิดเบี้ยวอัตโนมัติผ่าน CaptureRequest.DISTORTION_CORRECTION_MODE การแก้ไขการผิดเพี้ยนจะเปิดอยู่โดยค่าเริ่มต้นในอุปกรณ์ส่วนใหญ่

KotlinJava
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(), ...)
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(), ...);

การตั้งค่าคำขอบันทึกในโหมดนี้อาจส่งผลต่ออัตราเฟรมที่ ที่ได้จากกล้อง คุณอาจเลือกที่จะตั้งค่าการแก้ไขการบิดเบี้ยว การจับภาพนิ่ง