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

คุณกำหนดค่ากรณีการใช้งาน CameraX แต่ละกรณีเพื่อควบคุมแง่มุมต่างๆ ของการดำเนินงานของกรณีการใช้งาน

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

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 ของแพลตฟอร์มกล้องภายในทั้งหมด รวมถึงสำหรับการเรียกกลับจาก 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 หรือแบบไดนามิกหลังจากสร้างแล้ว

แอปสามารถตั้งค่าการหมุนเป้าหมายได้โดยใช้การตั้งค่าการกําหนดค่า จากนั้นจะอัปเดตการตั้งค่าการหมุนเวียนโดยใช้เมธอดจาก 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();
}

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

  • แสดงตัวอย่าง: แสดงเอาต์พุตข้อมูลเมตาเพื่อให้ทราบการหมุนของความละเอียดเป้าหมายโดยใช้ 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 อย่างดังนี้

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้าง 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

เริ่มต้นด้วยการสร้าง 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