ตัวเลือกการกำหนดค่า

คุณกําหนดค่า Use Case ของ CameraX แต่ละรายการเพื่อควบคุมแง่มุมต่างๆ ของการดำเนินการของ Use Case ได้

ตัวอย่างเช่น ในกรณีการใช้งานการจับภาพ คุณสามารถตั้งค่าสัดส่วนภาพเป้าหมายและโหมดแฟลชได้ โค้ดต่อไปนี้แสดงตัวอย่างหนึ่ง

Kotlin

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Java

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

นอกจากตัวเลือกการกําหนดค่าแล้ว บาง Use Case ยังแสดง API เพื่อเปลี่ยนแปลงการตั้งค่าแบบไดนามิกหลังจากที่สร้าง Use Case แล้ว ดูข้อมูลเกี่ยวกับการกําหนดค่าเฉพาะสําหรับกรณีการใช้งานแต่ละรายการได้ที่ใช้ตัวอย่าง วิเคราะห์รูปภาพ และการจับภาพรูปภาพ

CameraXConfig

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

CameraXConfig ช่วยให้แอปพลิเคชันทำสิ่งต่อไปนี้ได้

  • เพิ่มประสิทธิภาพเวลาในการตอบสนองของการเปิดใช้งานด้วย setAvailableCameraLimiter()
  • ให้ผู้ดำเนินการของแอปพลิเคชันแก่ CameraX ด้วย setCameraExecutor()
  • แทนที่เครื่องจัดการเครื่องจัดตารางเวลาเริ่มต้นด้วย setSchedulerHandler()
  • เปลี่ยนระดับการบันทึกด้วย setMinimumLoggingLevel()

รูปแบบการใช้งาน

ขั้นตอนต่อไปนี้จะอธิบายวิธีใช้ CameraXConfig

  1. สร้างออบเจ็กต์ CameraXConfig ด้วยการกําหนดค่าที่กําหนดเอง
  2. ใช้อินเตอร์เฟซ CameraXConfig.Provider ใน Application และแสดงผลออบเจ็กต์ CameraXConfig ใน getCameraXConfig()
  3. เพิ่มชั้นเรียน Application ลงในไฟล์ AndroidManifest.xml ตามที่อธิบายไว้ที่นี่

ตัวอย่างเช่น ตัวอย่างโค้ดต่อไปนี้จะจำกัดการบันทึกของ CameraX ไว้เฉพาะข้อความแสดงข้อผิดพลาดเท่านั้น

Kotlin

class CameraApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
           .setMinimumLoggingLevel(Log.ERROR).build()
   }
}

เก็บสำเนาของออบเจ็กต์ CameraXConfig ไว้ในเครื่องหากแอปพลิเคชันของคุณจำเป็นต้องทราบการกำหนดค่า CameraX หลังจากตั้งค่า

ตัวจำกัดกล้อง

ในระหว่างการเรียกใช้ ProcessCameraProvider.getInstance() ครั้งแรก CameraX จะแสดงรายการและค้นหาลักษณะของกล้องที่ใช้ได้ในอุปกรณ์ เนื่องจาก CameraX ต้องสื่อสารกับคอมโพเนนต์ฮาร์ดแวร์ กระบวนการนี้จึงอาจใช้เวลานานพอสมควรสำหรับกล้องแต่ละตัว โดยเฉพาะในอุปกรณ์ระดับล่าง หากแอปพลิเคชันใช้เฉพาะกล้องบางตัวในอุปกรณ์ เช่น กล้องหน้าเริ่มต้น คุณสามารถตั้งค่า CameraX ให้ละเว้นกล้องอื่นๆ ได้ ซึ่งจะช่วยลดเวลาในการตอบสนองในการเริ่มต้นของกล้องที่แอปพลิเคชันใช้

หาก CameraSelector ที่ส่งผ่านให้กับ CameraXConfig.Builder.setAvailableCamerasLimiter() กรองกล้องออก CameraX จะทํางานราวกับว่ากล้องนั้นไม่มีอยู่ ตัวอย่างเช่น โค้ดต่อไปนี้จะจํากัดแอปพลิเคชันให้ใช้ได้เฉพาะกล้องหลังเริ่มต้นของอุปกรณ์

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

ชุดข้อความ

API ของแพลตฟอร์มหลายตัวที่สร้าง CameraX กำหนดให้มีการบล็อกการสื่อสารระหว่างโปรเซส (IPC) กับฮาร์ดแวร์ที่บางครั้งอาจใช้เวลาหลายร้อยมิลลิวินาทีในการตอบสนอง ด้วยเหตุนี้ CameraX จึงเรียกใช้ API เหล่านี้จากเธรดเบื้องหลังเท่านั้น เพื่อไม่ให้เธรดหลักถูกบล็อกและ UI ยังคงทำงานได้อย่างราบรื่น CameraX จะจัดการเธรดเบื้องหลังเหล่านี้ภายในเพื่อให้ลักษณะการทำงานนี้ดูโปร่งใส อย่างไรก็ตาม แอปพลิเคชันบางรายการจำเป็นต้องมีการควบคุมเธรดอย่างเข้มงวด CameraXConfig ช่วยให้แอปพลิเคชันตั้งค่าเธรดพื้นหลังที่ใช้ผ่าน CameraXConfig.Builder.setCameraExecutor() และ CameraXConfig.Builder.setSchedulerHandler() ได้

ผู้ดำเนินการของกล้อง

ผู้ดำเนินการกล้องใช้สำหรับการเรียก API แพลตฟอร์มกล้องถ่ายรูปภายในทั้งหมด รวมทั้งสำหรับ Callback จาก API เหล่านี้ CameraX จะจัดสรรและจัดการ Executor ภายในเพื่อทำงานเหล่านี้ อย่างไรก็ตาม หากแอปพลิเคชันของคุณต้องใช้การควบคุมเธรดอย่างเข้มงวดมากขึ้น ให้ใช้ CameraXConfig.Builder.setCameraExecutor()

ตัวแฮนเดิลเครื่องจัดตารางเวลา

แฮนเดิลตัวจัดตารางเวลาใช้เพื่อกำหนดเวลางานภายในเป็นระยะๆ ที่แน่นอน เช่น การลองเปิดกล้องอีกครั้งเมื่อกล้องไม่พร้อมใช้งาน แฮนเดิลนี้จะไม่ดําเนินการงาน แต่จะส่งงานไปยังผู้ดําเนินการของกล้องเท่านั้น นอกจากนี้ยังใช้ในแพลตฟอร์ม API เดิมที่ต้องใช้ Handler สำหรับ Callback ได้ด้วย ในกรณีเหล่านี้ ระบบจะยังคงส่งการเรียกกลับไปยังผู้ดำเนินการของกล้องโดยตรงเท่านั้น CameraX จะจัดสรรและจัดการHandlerThread ภายในเพื่อทำงานเหล่านี้ แต่คุณลบล้างได้ด้วย CameraXConfig.Builder.setSchedulerHandler()

การบันทึก

การบันทึก CameraX ช่วยให้แอปพลิเคชันกรองข้อความ Logcat ได้ เนื่องจากเป็นแนวทางปฏิบัติที่ดีในการหลีกเลี่ยงข้อความแบบละเอียดในโค้ดการผลิต CameraX รองรับการบันทึกในระดับต่างๆ 4 ระดับ ตั้งแต่การบันทึกแบบละเอียดที่สุดไปจนถึงการบันทึกแบบละเอียดน้อยที่สุด ดังนี้

  • Log.DEBUG (ค่าเริ่มต้น)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

ดูคำอธิบายระดับการบันทึกเหล่านี้โดยละเอียดได้ในเอกสารประกอบบันทึกของ Android ใช้ CameraXConfig.Builder.setMinimumLoggingLevel(int) เพื่อตั้งค่าระดับการบันทึกที่เหมาะสมสำหรับแอปพลิเคชันของคุณ

การเลือกอัตโนมัติ

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

เป้าหมายของ CameraX คือเริ่มต้นเซสชันกล้องให้สําเร็จ ซึ่งหมายความว่า CameraX จะลดความละเอียดและสัดส่วนการแสดงผลตามความสามารถของอุปกรณ์ การประนีประนอมอาจเกิดขึ้นเนื่องจากสาเหตุต่อไปนี้

  • อุปกรณ์ไม่รองรับการแก้ปัญหาที่ขอ
  • อุปกรณ์มีปัญหาเรื่องความเข้ากันได้ เช่น อุปกรณ์เดิมที่ต้องมีความละเอียดบางอย่างเพื่อให้ทำงานได้อย่างถูกต้อง
  • ในอุปกรณ์บางรุ่น รูปแบบบางอย่างจะใช้ได้เฉพาะในบางสัดส่วนเท่านั้น
  • อุปกรณ์มีค่ากำหนด "mod16 ที่ใกล้ที่สุด" สําหรับการเข้ารหัส JPEG หรือวิดีโอ ดูข้อมูลเพิ่มเติมได้ที่ SCALER_STREAM_CONFIGURATION_MAP

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

การหมุน

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

แอปของคุณตั้งค่าการหมุนเวียนเป้าหมายได้โดยใช้การตั้งค่าการกําหนดค่า จากนั้นจะอัปเดตการตั้งค่าการหมุนโดยใช้เมธอดจาก Use Case API (เช่น ImageAnalysis.setTargetRotation()) ได้แม้ว่าวงจรจะอยู่ในสถานะทํางานก็ตาม คุณอาจใช้ตัวเลือกนี้เมื่อแอปล็อกอยู่ให้อยู่ในโหมดแนวตั้ง และไม่มีการกำหนดค่าใหม่เกิดขึ้นในการหมุนเวียน แต่กรณีการใช้งานรูปภาพหรือการวิเคราะห์จะต้องคำนึงถึงการหมุนในปัจจุบันของอุปกรณ์ เช่น คุณอาจต้องใช้การตรวจจับการหมุนเพื่อให้ใบหน้าอยู่ในแนวที่ถูกต้องสำหรับการจดจำใบหน้า หรือตั้งค่ารูปภาพเป็นแนวนอนหรือแนวตั้ง

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

หากต้องการแสดงข้อมูลตัวอย่างในแนวที่ถูกต้อง คุณสามารถใช้ข้อมูลเมตาที่แสดงผลจาก Preview.PreviewOutput() เพื่อสร้างการเปลี่ยนรูปแบบ

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีตั้งค่าการหมุนในเหตุการณ์การวางแนว

Kotlin

override fun onCreate() {
    val imageCapture = ImageCapture.Builder().build()

    val orientationEventListener = object : OrientationEventListener(this as Context) {
        override fun onOrientationChanged(orientation : Int) {
            // Monitors orientation values to determine the target rotation value
            val rotation : Int = when (orientation) {
                in 45..134 -> Surface.ROTATION_270
                in 135..224 -> Surface.ROTATION_180
                in 225..314 -> Surface.ROTATION_90
                else -> Surface.ROTATION_0
            }

            imageCapture.targetRotation = rotation
        }
    }
    orientationEventListener.enable()
}

Java

@Override
public void onCreate() {
    ImageCapture imageCapture = new ImageCapture.Builder().build();

    OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
       @Override
       public void onOrientationChanged(int orientation) {
           int rotation;

           // Monitors orientation values to determine the target rotation value
           if (orientation >= 45 && orientation < 135) {
               rotation = Surface.ROTATION_270;
           } else if (orientation >= 135 && orientation < 225) {
               rotation = Surface.ROTATION_180;
           } else if (orientation >= 225 && orientation < 315) {
               rotation = Surface.ROTATION_90;
           } else {
               rotation = Surface.ROTATION_0;
           }

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

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

  • แสดงตัวอย่าง: มีการระบุเอาต์พุตข้อมูลเมตาเพื่อให้ทราบถึงการหมุนความละเอียดเป้าหมายโดยใช้ Preview.getTargetRotation()
  • ImageAnalysis: แสดงผลข้อมูลเมตาเพื่อให้ทราบพิกัดบัฟเฟอร์รูปภาพซึ่งสัมพันธ์กับพิกัดการแสดงผล
  • ImageCapture: ระบบจะแก้ไขข้อมูลเมตา Exif ของรูปภาพ บัฟเฟอร์ หรือทั้งบัฟเฟอร์และข้อมูลเมตาเพื่อบันทึกการตั้งค่าการหมุน ค่าที่เปลี่ยนแปลงจะขึ้นอยู่กับการใช้งาน HAL

สี่เหลี่ยมผืนผ้าครอบตัด

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

ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้คลาสทั้ง 2 คลาสนี้

Kotlin

val viewPort =  ViewPort.Builder(Rational(width, height), display.rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

Java

ViewPort viewPort = new ViewPort.Builder(
         new Rational(width, height),
         getDisplay().getRotation()).build();
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build();
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);

ViewPort กําหนดสี่เหลี่ยมผืนผ้าบัฟเฟอร์ที่ผู้ใช้ปลายทางมองเห็น จากนั้น CameraX จะคํานวณสี่เหลี่ยมผืนผ้าครอบตัดที่ใหญ่ที่สุดที่เป็นไปได้ตามพร็อพเพอร์ตี้ของวิวพอร์ตและ Use Case ที่แนบมา โดยปกติแล้ว หากต้องการใช้ผลลัพธ์ WYSIWYG คุณสามารถกําหนดค่าวิวพอร์ตตาม Use Case ของตัวอย่างได้ วิธีง่ายๆ ในการรับวิวพอร์ตคือใช้ PreviewView

ข้อมูลโค้ดต่อไปนี้แสดงวิธีรับออบเจ็กต์ ViewPort

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

ในตัวอย่างก่อนหน้านี้ สิ่งที่แอปได้รับจาก ImageAnalysis และ ImageCapture ตรงกับสิ่งที่ผู้ใช้ปลายทางเห็นใน PreviewView โดยสมมติว่าประเภทสเกลของ PreviewView ตั้งเป็นค่าเริ่มต้น FILL_CENTER หลังจากใช้สี่เหลี่ยมผืนผ้าครอบตัดและการหมุนกับบัฟเฟอร์เอาต์พุตแล้ว รูปภาพจาก Use Case ทั้งหมดจะเหมือนกัน แม้ว่าอาจมีความละเอียดต่างกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้ข้อมูลการเปลี่ยนรูปแบบได้ที่ transform output

การเลือกกล้อง

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

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้าง CameraSelector เพื่อสร้างอิทธิพลต่อการเลือกอุปกรณ์

Kotlin

fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? {
   val cam2Infos = provider.availableCameraInfos.map {
       Camera2CameraInfo.from(it)
   }.sortedByDescending {
       // HARDWARE_LEVEL is Int type, with the order of:
       // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL
       it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
   }

   return when {
       cam2Infos.isNotEmpty() -> {
           CameraSelector.Builder()
               .addCameraFilter {
                   it.filter { camInfo ->
                       // cam2Infos[0] is either EXTERNAL or best built-in camera
                       val thisCamId = Camera2CameraInfo.from(camInfo).cameraId
                       thisCamId == cam2Infos[0].cameraId
                   }
               }.build()
       }
       else -> null
    }
}

// create a CameraSelector for the USB camera (or highest level internal camera)
val selector = selectExternalOrBestCamera(processCameraProvider)
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

เลือกกล้องหลายตัวพร้อมกัน

ตั้งแต่ CameraX 1.3 เป็นต้นไป คุณยังเลือกกล้องหลายตัวพร้อมกันได้ด้วย เช่น คุณสามารถเชื่อมโยงกับกล้องหน้าและกล้องหลังเพื่อถ่ายภาพหรือบันทึกวิดีโอจากทั้ง 2 มุมมองพร้อมกัน

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

Kotlin

// Build ConcurrentCameraConfig
val primary = ConcurrentCamera.SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val secondary = ConcurrentCamera.SingleCameraConfig(
    secondaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val concurrentCamera = cameraProvider.bindToLifecycle(
    listOf(primary, secondary)
)

val primaryCamera = concurrentCamera.cameras[0]
val secondaryCamera = concurrentCamera.cameras[1]

Java

// Build ConcurrentCameraConfig
SingleCameraConfig primary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

SingleCameraConfig secondary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

ConcurrentCamera concurrentCamera =  
    mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary));

Camera primaryCamera = concurrentCamera.getCameras().get(0);
Camera secondaryCamera = concurrentCamera.getCameras().get(1);

ความละเอียดของกล้อง

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

การแก้ปัญหาอัตโนมัติ

CameraX สามารถกำหนดการตั้งค่าความละเอียดที่ดีที่สุดโดยอัตโนมัติตามกรณีการใช้งานที่ระบุใน cameraProcessProvider.bindToLifecycle() ระบุ Use Case ทั้งหมดที่จําเป็นต้องทํางานพร้อมกันในเซสชันเดียวในbindToLifecycle()การเรียกใช้ครั้งเดียวทุกครั้งที่เป็นไปได้ CameraX จะกำหนดความละเอียดตามชุดกรณีการใช้งานที่เกี่ยวข้อง โดยพิจารณาระดับฮาร์ดแวร์ที่รองรับของอุปกรณ์ และพิจารณาความแปรปรวนเฉพาะของอุปกรณ์ (ที่อุปกรณ์มีการทำงานเกินขีดจำกัดหรือไม่ตรงตามการกำหนดค่าสตรีมที่ใช้ได้) จุดประสงค์คือให้แอปพลิเคชันทำงานบนอุปกรณ์หลากหลายประเภทโดยลดเส้นทางของโค้ดเฉพาะอุปกรณ์

สัดส่วนภาพเริ่มต้นสำหรับ Use Case การจับภาพและการวิเคราะห์รูปภาพคือ 4:3

Use Case มีอัตราส่วนภาพที่กําหนดค่าได้เพื่อให้แอปพลิเคชันระบุอัตราส่วนที่ต้องการตามการออกแบบ UI เอาต์พุต CameraX ผลิตขึ้นเพื่อให้ตรงกับสัดส่วนภาพที่ขออย่างใกล้เคียงกับที่อุปกรณ์รองรับ หากระบบไม่รองรับการแก้ไขแบบตรงทั้งหมด ระบบจะเลือกการแก้ไขที่ตรงกับเงื่อนไขมากที่สุด ดังนั้น แอปพลิเคชันจะกำหนดวิธีที่กล้องจะปรากฏในแอป และ CameraX จะกำหนดการตั้งค่าความละเอียดของกล้องที่ดีที่สุดเพื่อตอบสนองต่อการตั้งค่าดังกล่าวในอุปกรณ์ที่แตกต่างกัน

เช่น แอปอาจดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้

  • ระบุความละเอียดเป้าหมาย 4:3 หรือ 16:9 สำหรับ Use Case
  • ระบุความละเอียดที่กำหนดเอง ซึ่ง CameraX จะพยายามค้นหา รายการที่ตรงกันมากที่สุด
  • ระบุสัดส่วนภาพสำหรับการครอบตัดของ ImageCapture

CameraX จะเลือกความละเอียดพื้นผิวภายใน Camera2 โดยอัตโนมัติ ตารางต่อไปนี้แสดงวิธีแก้ปัญหา

กรณีการใช้งาน ความละเอียดของพื้นผิวภายใน ความละเอียดของข้อมูลเอาต์พุต
แสดงตัวอย่าง สัดส่วนภาพ: ความละเอียดที่เหมาะกับการตั้งค่าเป้าหมายมากที่สุด ความละเอียดของพื้นผิวภายใน ข้อมูลเมตามีไว้เพื่อให้มุมมองครอบตัด ปรับขนาด และบิดสัดส่วนภาพตามสัดส่วนภาพเป้าหมาย
ความละเอียดเริ่มต้น: ความละเอียดของตัวอย่างสูงสุด หรือความละเอียดที่อุปกรณ์ต้องการสูงสุดซึ่งตรงกับสัดส่วนภาพของตัวอย่าง
ความละเอียดสูงสุด: ขนาดตัวอย่าง ซึ่งหมายถึงขนาดที่ตรงกับความละเอียดหน้าจอของอุปกรณ์หรือ 1080p (1920x1080) มากที่สุด ขึ้นอยู่กับว่าขนาดใดเล็กกว่า
การวิเคราะห์รูปภาพ สัดส่วนภาพ: ความละเอียดที่เหมาะกับการตั้งค่าเป้าหมายมากที่สุด ความละเอียดของพื้นผิวภายใน
ความละเอียดเริ่มต้น: การตั้งค่าความละเอียดเป้าหมายเริ่มต้นคือ 640x480 การปรับทั้งความละเอียดเป้าหมายและสัดส่วนภาพที่เกี่ยวข้องจะให้ความละเอียดที่รองรับได้ดีที่สุด
ความละเอียดสูงสุด: ความละเอียดเอาต์พุตสูงสุดของอุปกรณ์กล้องในรูปแบบ YUV_420_888 ซึ่งดึงมาจาก StreamConfigurationMap.getOutputSizes() ระบบจะตั้งค่าความละเอียดเป้าหมายเป็น 640x480 โดยค่าเริ่มต้น ดังนั้นหากต้องการความละเอียดที่มากกว่า 640x480 คุณต้องใส่ค่า setTargetResolution() และ setTargetAspectRatio() เพื่อรับความละเอียดที่ใกล้เคียงที่สุดจากความละเอียดที่รองรับ
การจับภาพ สัดส่วนภาพ: สัดส่วนภาพที่เหมาะกับการตั้งค่ามากที่สุด ความละเอียดของพื้นผิวภายใน
ความละเอียดเริ่มต้น: ความละเอียดสูงสุดที่มี หรือความละเอียดสูงสุดที่อุปกรณ์กำหนดซึ่งตรงกับสัดส่วนภาพของ ImageCapture
ความละเอียดสูงสุด: ความละเอียดเอาต์พุตสูงสุดของอุปกรณ์กล้องในรูปแบบ JPEG ใช้ StreamConfigurationMap.getOutputSizes() เพื่อเรียกข้อมูลนี้

ระบุความละเอียด

คุณสามารถตั้งค่าความละเอียดที่เฉพาะเจาะจงเมื่อสร้าง Use Case โดยใช้เมธอด setTargetResolution(Size resolution) ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

คุณไม่สามารถตั้งค่าทั้งสัดส่วนภาพเป้าหมายและความละเอียดเป้าหมายใน Use Case เดียวกัน ซึ่งจะทำให้เกิด IllegalArgumentException เมื่อสร้างออบเจ็กต์การกําหนดค่า

แสดงความละเอียด Size ในเฟรมพิกัดหลังจากหมุนขนาดที่รองรับตามการหมุนเป้าหมาย ตัวอย่างเช่น อุปกรณ์ที่มีการวางแนวตั้งตามธรรมชาติในการหมุนเป้าหมายที่เป็นธรรมชาติที่ขอรูปภาพแนวตั้งสามารถระบุเป็น 480x640 และอุปกรณ์เดียวกัน หมุน 90 องศาและกำหนดเป้าหมายในแนวนอนสามารถระบุเป็น 640x480

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

อย่างไรก็ตาม หากไม่มีความละเอียดที่เท่ากับหรือใหญ่กว่าความละเอียดเป้าหมาย ระบบจะเลือกความละเอียดที่เล็กกว่าความละเอียดเป้าหมายซึ่งใกล้เคียงที่สุด ความละเอียดที่มีสัดส่วนภาพเดียวกับSizeที่ระบุจะมีลำดับความสำคัญสูงกว่าความละเอียดที่มีสัดส่วนภาพต่างกัน

CameraX ใช้ความละเอียดที่เหมาะสมที่สุดโดยอิงตามคำขอ หากความต้องการหลักคือต้องเป็นไปตามสัดส่วนภาพ ให้ระบุเฉพาะ setTargetAspectRatio แล้ว CameraX จะกำหนดความละเอียดที่เฉพาะเจาะจงซึ่งเหมาะกับอุปกรณ์ หากความต้องการหลักของแอปคือการระบุความละเอียดเพื่อให้การประมวลผลรูปภาพมีประสิทธิภาพมากขึ้น (เช่น รูปภาพขนาดเล็กหรือขนาดกลางตามความสามารถในการประมวลผลของอุปกรณ์) ให้ใช้ setTargetResolution(Size resolution)

หากแอปต้องใช้ความละเอียดที่แน่นอน โปรดดูตารางใน createCaptureSession() เพื่อดูความละเอียดสูงสุดที่ฮาร์ดแวร์แต่ละระดับรองรับ หากต้องการตรวจสอบความละเอียดที่อุปกรณ์ปัจจุบันรองรับ โปรดดู StreamConfigurationMap.getOutputSizes(int)

หากแอปทำงานบน Android 10 ขึ้นไป คุณจะใช้ isSessionConfigurationSupported() เพื่อยืนยัน SessionConfiguration ที่เฉพาะเจาะจงได้

ควบคุมเอาต์พุตของกล้อง

นอกจากจะช่วยให้คุณกําหนดค่าเอาต์พุตของกล้องได้ตามต้องการสําหรับแต่ละกรณีการใช้งานแล้ว CameraX ยังใช้อินเทอร์เฟซต่อไปนี้เพื่อรองรับการดําเนินการของกล้องที่ใช้ร่วมกันใน Use Case ที่เชื่อมโยงทั้งหมดด้วย

  • CameraControl ช่วยให้คุณกำหนดค่าฟีเจอร์ทั่วไปของกล้องได้
  • CameraInfo ช่วยให้คุณค้นหาสถานะของฟีเจอร์กล้องทั่วไปเหล่านั้นได้

ฟีเจอร์กล้องที่ CameraControl รองรับมีดังนี้

  • Zoom
  • คบเพลิง
  • โฟกัสและการวัดแสง (แตะเพื่อโฟกัส)
  • การชดเชยแสง

รับอินสแตนซ์ของ CameraControl และ CameraInfo

ดึงข้อมูลอินสแตนซ์ของ CameraControl และ CameraInfo โดยใช้ออบเจ็กต์ Camera ที่ ProcessCameraProvider.bindToLifecycle() แสดงผล โค้ดต่อไปนี้แสดงตัวอย่าง

Kotlin

val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo

Java

Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl()
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo()

ตัวอย่างเช่น คุณสามารถส่งการซูมและการดำเนินการอื่นๆ ของ CameraControl ได้หลังจากเรียกใช้ bindToLifecycle() หลังจากที่คุณหยุดหรือทำลายกิจกรรมที่ใช้เชื่อมโยงอินสแตนซ์ของกล้อง CameraControl จะไม่สามารถดำเนินการต่างๆ ได้อีกต่อไปและแสดงผล ListenableFuture ที่ล้มเหลว

Zoom

CameraControl มีวิธีการเปลี่ยนระดับการซูม 2 วิธี ได้แก่

  • setZoomRatio() ตั้งค่าการซูมตามอัตราส่วนการซูม

    โดยอัตราส่วนต้องอยู่ในช่วง CameraInfo.getZoomState().getValue().getMinZoomRatio() ถึง CameraInfo.getZoomState().getValue().getMaxZoomRatio() ไม่เช่นนั้น ฟังก์ชันจะแสดงผล ListenableFuture ที่ดำเนินการไม่สำเร็จ

  • setLinearZoom() ตั้งค่าการซูมปัจจุบันด้วยค่าการซูมเชิงเส้นตั้งแต่ 0 ถึง 1.0

    ข้อดีของการซูมแบบเส้นตรงคือทำให้มุมมอง (FOV) ปรับขนาดตามการเปลี่ยนแปลงการซูม ซึ่งเหมาะสําหรับใช้กับมุมมอง Slider

CameraInfo.getZoomState() แสดงผล LiveData ของสถานะการซูมปัจจุบัน ค่าจะเปลี่ยนแปลงเมื่อเริ่มต้นกล้องหรือหากตั้งค่าระดับการซูมโดยใช้ setZoomRatio() หรือ setLinearZoom() การเรียกใช้เมธอดใดเมธอดหนึ่งจะตั้งค่าที่รองรับ ZoomState.getZoomRatio() และ ZoomState.getLinearZoom() ซึ่งจะเป็นประโยชน์หากคุณต้องการแสดงข้อความอัตราส่วนการซูมควบคู่ไปกับแถบเลื่อน เพียงสังเกต LiveData ของ ZoomState เพื่ออัปเดตทั้ง 2 รายการโดยไม่ต้องทำ Conversion

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

คบเพลิง

CameraControl.enableTorch(boolean) เปิดหรือปิดใช้ไฟฉาย (หรือที่เรียกว่าไฟฉาย)

CameraInfo.getTorchState() ใช้เพื่อค้นหาสถานะไฟฉายปัจจุบันได้ คุณสามารถตรวจสอบค่าที่แสดงผลโดย CameraInfo.hasFlashUnit() เพื่อดูว่าไฟฉายพร้อมใช้งานหรือไม่ หากไม่ การเรียกใช้ CameraControl.enableTorch(boolean) จะทำให้ ListenableFuture ที่แสดงผลเสร็จสมบูรณ์ทันทีโดยที่ผลลัพธ์ล้มเหลวและตั้งค่าสถานะไฟฉายเป็น TorchState.OFF

เมื่อเปิดใช้ไฟฉาย ไฟฉายจะยังคงเปิดอยู่ขณะถ่ายภาพและวิดีโอ ไม่ว่าจะตั้งค่า flashMode เป็นค่าใดก็ตาม flashMode ใน ImageCapture จะทำงานก็ต่อเมื่อปิดไฟฉายอยู่เท่านั้น

โฟกัสและการวัดแสง

CameraControl.startFocusAndMetering() เรียกใช้โฟกัสอัตโนมัติและการวัดแสงโดยการตั้งค่าพื้นที่การวัด AF/AE/AWB ตาม FocusMeteringAction ที่ระบุ ซึ่งมักใช้เพื่อใช้ฟีเจอร์ "แตะเพื่อโฟกัส" ในแอปพลิเคชันกล้องหลายแอป

จุดวัด

เริ่มต้นด้วยการสร้าง MeteringPoint โดยใช้ MeteringPointFactory.createPoint(float x, float y, float size) MeteringPoint แสดงจุดเดียวบนกล้อง Surface โดยจัดเก็บในรูปแบบมาตรฐานเพื่อให้แปลงเป็นพิกัดเซ็นเซอร์สำหรับการระบุภูมิภาค AF/AE/AWB ได้โดยง่าย

ขนาดของ MeteringPoint อยู่ในช่วง 0 ถึง 1 โดยมีขนาดเริ่มต้นเป็น 0.15f เมื่อเรียกใช้ MeteringPointFactory.createPoint(float x, float y, float size) แล้ว CameraX จะสร้างพื้นที่สี่เหลี่ยมผืนผ้าที่กึ่งกลางอยู่ที่ (x, y) สำหรับ size ที่ระบุ

โค้ดต่อไปนี้แสดงวิธีสร้าง MeteringPoint

Kotlin

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener((view, motionEvent) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)
…
}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please note that if the preview is scaled or cropped in the View,
// it’s the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint might need to be adjusted with
// the offsets.
val meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
     imageWidth,
     imageHeight,
     imageAnalysis)

StartFocusAndMetering และ FocusMeteringAction

หากต้องการเรียกใช้ startFocusAndMetering() แอปพลิเคชันต้องสร้าง FocusMeteringAction ซึ่งประกอบด้วย MeteringPoints อย่างน้อย 1 รายการที่มีโหมดการตรวจสอบการใช้งานที่ไม่บังคับ ชุดค่าผสมจาก FLAG_AF, FLAG_AE, FLAG_AWB โค้ดต่อไปนี้แสดงการใช้งาน

Kotlin

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action is canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

ดังที่แสดงในโค้ดก่อนหน้า startFocusAndMetering() ใช้ FocusMeteringAction ที่ประกอบด้วย MeteringPoint 1 รายการสําหรับภูมิภาคการวัดแสง AF/AE/AWB และ MeteringPoint อีกรายการสําหรับ AF และ AE เท่านั้น

ภายใน CameraX จะแปลงเป็น Camera2 MeteringRectangles และตั้งค่าพารามิเตอร์ที่เกี่ยวข้อง CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS ในคำขอจับภาพ

เนื่องจากอุปกรณ์บางเครื่องไม่รองรับ AF/AE/AWB และในหลายภูมิภาค CameraX จะใช้ FocusMeteringAction อย่างดีที่สุด CameraX ใช้จำนวน MeteringPoints สูงสุดที่รองรับ ตามลำดับการเพิ่มจุด ระบบจะละเว้น MeteringPoint ทั้งหมดที่เพิ่มหลังจากจํานวนสูงสุด เช่น หากFocusMeteringActionมี MeteringPoint 3 รายการในแพลตฟอร์มที่รองรับเพียง 2 รายการ ระบบจะใช้เฉพาะ MeteringPoint 2 รายการแรก CameraX จะละเว้น MeteringPoint ตัวสุดท้าย

การชดเชยแสง

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

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX มีฟังก์ชัน Camera.CameraControl.setExposureCompensationIndex() สำหรับตั้งค่าชดเชยการรับแสงเป็นค่าดัชนี

ค่าดัชนีที่เป็นบวกจะทำให้รูปภาพสว่างขึ้น ส่วนค่าลบจะทำให้รูปภาพสลัวลง แอปพลิเคชันจะค้นหาช่วงที่ได้รับการรองรับได้โดยใช้ CameraInfo.ExposureState.exposureCompensationRange() ที่อธิบายไว้ในส่วนถัดไป หากระบบรองรับค่าดังกล่าว ListenableFuture ที่แสดงผลจะเสร็จสิ้นเมื่อเปิดใช้ค่าในคำขอแคปเจอร์สำเร็จ หากดัชนีที่ระบุอยู่นอกช่วงที่รองรับ setExposureCompensationIndex() จะทำให้ ListenableFuture ที่แสดงผลเสร็จสมบูรณ์ทันทีโดยมีผลลัพธ์ที่ล้มเหลว

CameraX จะเก็บเฉพาะsetExposureCompensationIndex()คำขอที่รอดำเนินการล่าสุดไว้ และเรียกใช้ฟังก์ชันหลายครั้งก่อนที่คำขอก่อนหน้าจะดำเนินการจะส่งผลให้คำขอถูกยกเลิก

ข้อมูลโค้ดต่อไปนี้จะตั้งค่าดัชนีการชดเชยแสงและลงทะเบียนการเรียกกลับเมื่อดำเนินการตามคำขอเปลี่ยนการเปิดรับแสง

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it might be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      …
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() ดึงข้อมูลปัจจุบันจากExposureState ซึ่งรวมถึง

    • การรองรับการควบคุมการชดเชยแสง
    • ดัชนีการชดเชยการรับแสงปัจจุบัน
    • ช่วงดัชนีการชดเชยการรับแสง
    • ขั้นตอนการชดเชยการสัมผัสที่ใช้ในการคำนวณค่าชดเชยการรับแสง

ตัวอย่างเช่น โค้ดต่อไปนี้จะเริ่มต้นการตั้งค่าสำหรับการแสดงผล SeekBar ด้วยค่า ExposureState ในปัจจุบัน

Kotlin

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับ CameraX ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

Codelab

  • การเริ่มต้นใช้งาน CameraX
  • ตัวอย่างโค้ด

  • ตัวอย่างแอป CameraX
  • ชุมชนนักพัฒนาแอป

    กลุ่มสนทนาเกี่ยวกับ CameraX ของ Android