คุณกําหนดค่า 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
- สร้างออบเจ็กต์
CameraXConfig
ด้วยการกําหนดค่าที่กําหนดเอง - ใช้อินเตอร์เฟซ
CameraXConfig.Provider
ในApplication
และแสดงผลออบเจ็กต์CameraXConfig
ในgetCameraXConfig()
- เพิ่มชั้นเรียน
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
สําหรับการเรียกกลับในบางครั้งด้วย ในกรณีเหล่านี้ ระบบจะยังคงส่งการเรียกกลับไปยังผู้ดำเนินการของกล้องโดยตรงเท่านั้น 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 ในโค้ดของคุณเสมอและปรับให้เหมาะสม
การหมุน
โดยค่าเริ่มต้น ระบบจะตั้งค่าการหมุนกล้องให้ตรงกับการหมุนของจอแสดงผลเริ่มต้นขณะสร้าง 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.DEFAULT_FRONT_CAMERA
- ขอกล้องหลังเริ่มต้นด้วย
CameraSelector.DEFAULT_BACK_CAMERA
- กรองรายการอุปกรณ์ที่ใช้ได้ตาม
CameraCharacteristics
ด้วยCameraSelector.Builder.addCameraFilter()
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้าง 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()
ซึ่งจะเป็นประโยชน์หากคุณต้องการแสดงข้อความอัตราส่วนการซูมควบคู่ไปกับแถบเลื่อน
เพียงดูที่ ZoomState
LiveData
เพื่ออัปเดตทั้ง 2 รายการโดยไม่ต้องแปลง
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 ของ Android