คุณกำหนดค่า Use Case ของ CameraX แต่ละรายการเพื่อควบคุมแง่มุมต่างๆ ของการดำเนินการของ Use Case
ตัวอย่างเช่น ในกรณีการใช้งานการจับภาพ คุณสามารถตั้งค่าสัดส่วนภาพเป้าหมายและโหมดแฟลชได้ โค้ดต่อไปนี้แสดงตัวอย่างหนึ่ง
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
นอกเหนือจากตัวเลือกการกำหนดค่าแล้ว กรณีการใช้งานบางอย่างยังแสดง API เพื่อเปลี่ยนแปลงการตั้งค่าแบบไดนามิกหลังจากสร้างกรณีการใช้งานแล้ว ดูข้อมูลเกี่ยวกับการกำหนดค่าที่เจาะจงสำหรับกรณีการใช้งานแต่ละกรณีได้ที่ใช้การแสดงตัวอย่าง วิเคราะห์รูปภาพ และจับภาพ
CameraXConfig
เพื่อความสะดวก CameraX มีการกำหนดค่าเริ่มต้น เช่น ตัวดำเนินการและตัวแฮนเดิลภายในที่เหมาะกับสถานการณ์การใช้งานส่วนใหญ่ อย่างไรก็ตาม หากแอปพลิเคชันของคุณมีข้อกำหนดพิเศษหรือต้องการปรับแต่งการกำหนดค่าเหล่านั้น CameraXConfig คืออินเทอร์เฟซสำหรับวัตถุประสงค์ดังกล่าว
เมื่อใช้ CameraXConfig แอปพลิเคชันจะทำสิ่งต่อไปนี้ได้
- เพิ่มประสิทธิภาพเวลาในการตอบสนองของการเริ่มต้นด้วย
setAvailableCameraLimiter() - ระบุ Executor ของแอปพลิเคชันให้กับ 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() ได้
Camera Executor
ระบบจะใช้ Camera Executor สำหรับการเรียก API ของแพลตฟอร์มกล้องภายในทั้งหมด รวมถึงการเรียกกลับจาก API เหล่านี้ด้วย CameraX จะจัดสรรและจัดการ Executor ภายในเพื่อทำงานเหล่านี้
อย่างไรก็ตาม หากแอปพลิเคชันของคุณต้องการการควบคุมชุดข้อความที่เข้มงวดมากขึ้น ให้ใช้ CameraXConfig.Builder.setCameraExecutor()
เครื่องจัดการตัวจัดกำหนดการ
ตัวแฮนเดิลตัวจัดกำหนดการใช้เพื่อกำหนดเวลางานภายในตามช่วงเวลาที่กำหนด เช่น การลองเปิดกล้องอีกครั้งเมื่อกล้องไม่พร้อมใช้งาน ตัวแฮนเดิลนี้จะไม่เรียกใช้งาน แต่จะส่งงานไปยังผู้ดำเนินการกล้องเท่านั้น นอกจากนี้ บางครั้งยังใช้ในแพลตฟอร์ม API เดิมที่ต้องใช้ Handler สำหรับ Callback ด้วย ในกรณีเหล่านี้ ระบบจะยังคงส่ง Callback ไปยังผู้ดำเนินการกล้องโดยตรงเท่านั้น CameraX จะจัดสรรและจัดการ HandlerThread ภายในเพื่อทำงานเหล่านี้ แต่คุณสามารถลบล้างด้วย CameraXConfig.Builder.setSchedulerHandler() ได้
การบันทึก
การบันทึก CameraX ช่วยให้แอปพลิเคชันกรองข้อความ Logcat ได้ เนื่องจากเป็นแนวทางปฏิบัติที่ดีในการหลีกเลี่ยงข้อความที่ละเอียดเกินไปในโค้ดเวอร์ชันที่ใช้งานจริง CameraX รองรับการบันทึก 4 ระดับ ตั้งแต่ระดับที่มีรายละเอียดมากที่สุดไปจนถึงระดับที่รุนแรงที่สุด ดังนี้
Log.DEBUG(ค่าเริ่มต้น)Log.INFOLog.WARNLog.ERROR
ดูคำอธิบายโดยละเอียดของระดับบันทึกเหล่านี้ได้ในเอกสารประกอบเกี่ยวกับบันทึกของ Android ใช้
CameraXConfig.Builder.setMinimumLoggingLevel(int)
เพื่อตั้งค่าระดับการบันทึกที่เหมาะสมสำหรับแอปพลิเคชัน
การเลือกแบบอัตโนมัติ
CameraX จะมีฟังก์ชันการทำงานที่เจาะจงสำหรับอุปกรณ์ที่แอปของคุณทำงานอยู่โดยอัตโนมัติ ตัวอย่างเช่น CameraX จะกำหนดความละเอียดที่ดีที่สุดที่จะใช้โดยอัตโนมัติหากคุณไม่ได้ระบุความละเอียด หรือหากไม่รองรับความละเอียดที่คุณระบุ ไลบรารีจะจัดการทั้งหมดนี้ ทำให้คุณไม่ต้องเขียนโค้ดเฉพาะอุปกรณ์
เป้าหมายของ CameraX คือการเริ่มต้นเซสชันกล้องให้สำเร็จ ซึ่งหมายความว่า CameraX จะลดความละเอียดและสัดส่วนภาพตามความสามารถของอุปกรณ์ การบุกรุกอาจเกิดขึ้นเนื่องจากสาเหตุต่อไปนี้
- อุปกรณ์ไม่รองรับความละเอียดที่ขอ
- อุปกรณ์มีปัญหาด้านความเข้ากันได้ เช่น อุปกรณ์รุ่นเดิมที่ต้องใช้ความละเอียดบางอย่างเพื่อให้ทำงานได้อย่างถูกต้อง
- ในอุปกรณ์บางรุ่น บางรูปแบบจะใช้ได้เฉพาะในบางสัดส่วนภาพเท่านั้น
- อุปกรณ์มีค่ากำหนดสำหรับ "mod16 ที่ใกล้ที่สุด" สำหรับการเข้ารหัส JPEG หรือวิดีโอ ดูข้อมูลเพิ่มเติมได้ที่
SCALER_STREAM_CONFIGURATION_MAP
แม้ว่า CameraX จะสร้างและจัดการเซสชัน แต่ให้ตรวจสอบขนาดรูปภาพที่แสดงผลในเอาต์พุต Use Case ในโค้ดและปรับตามความเหมาะสมเสมอ
การหมุน
โดยค่าเริ่มต้น ระบบจะตั้งค่าการหมุนกล้องให้ตรงกับการหมุนของจอแสดงผลเริ่มต้นในระหว่างการสร้าง 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 ทั้งหมดจะเหมือนกัน แต่อาจมีความละเอียดต่างกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้ข้อมูลการเปลี่ยนรูปแบบได้ที่เปลี่ยนรูปแบบเอาต์พุต
การเลือกกล้อง
CameraX จะเลือกอุปกรณ์กล้องที่ดีที่สุดสำหรับข้อกำหนดและ Use Case ของแอปพลิเคชันโดยอัตโนมัติ หากต้องการใช้อุปกรณ์อื่นนอกเหนือจากที่เลือกไว้ให้ คุณมีตัวเลือกดังนี้
- ขอใช้กล้องหน้าเริ่มต้นด้วย
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() เมื่อใดก็ตามที่ทำได้ ให้ระบุกรณีการใช้งานทั้งหมดที่จำเป็นต้องเรียกใช้พร้อมกันในเซสชันเดียวในการเรียก bindToLifecycle() ครั้งเดียว CameraX จะกำหนดความละเอียดตามชุด Use Case ที่เชื่อมโยงโดยพิจารณาจากระดับฮาร์ดแวร์ที่อุปกรณ์รองรับและคำนึงถึงความแปรปรวนเฉพาะอุปกรณ์ (ซึ่งอุปกรณ์มีประสิทธิภาพเกินหรือต่ำกว่าการกำหนดค่าสตรีมที่ใช้ได้)
จุดประสงค์คือเพื่อให้แอปพลิเคชันทำงานบนอุปกรณ์ที่หลากหลายได้โดยลดเส้นทางโค้ดเฉพาะอุปกรณ์ให้เหลือน้อยที่สุด
สัดส่วนภาพเริ่มต้นสำหรับ Use Case การจับภาพและการวิเคราะห์รูปภาพคือ 4:3
กรณีการใช้งานมีสัดส่วนภาพที่กำหนดค่าได้เพื่อให้แอปพลิเคชันระบุสัดส่วนภาพที่ต้องการตามการออกแบบ UI เอาต์พุต CameraX จะสร้างขึ้นเพื่อให้ตรงกับสัดส่วนภาพที่ขอมากที่สุดเท่าที่อุปกรณ์รองรับ หากไม่มีความละเอียดที่ตรงกันทุกประการ ระบบจะเลือกความละเอียดที่ตรงตามเงื่อนไขมากที่สุด ดังนั้น แอปพลิเคชันจะกำหนดลักษณะของกล้องในแอป และ CameraX จะกำหนดการตั้งค่าความละเอียดของกล้องที่ดีที่สุดเพื่อให้เป็นไปตามนั้นในอุปกรณ์ต่างๆ
ตัวอย่างเช่น แอปสามารถทำสิ่งต่อไปนี้ได้
- ระบุความละเอียดเป้าหมายเป็น 4:3 หรือ 16:9 สำหรับกรณีการใช้งาน
- ระบุความละเอียดที่กำหนดเอง ซึ่ง CameraX จะพยายามค้นหาความละเอียดที่ตรงกันมากที่สุด
- ระบุสัดส่วนภาพสำหรับการครอบตัด
ImageCapture
CameraX จะเลือกความละเอียดของแพลตฟอร์ม Camera2 ภายในโดยอัตโนมัติ ตารางต่อไปนี้แสดงวิธีแก้ไข
| กรณีการใช้งาน | ความละเอียดของพื้นผิวภายใน | ความละเอียดของข้อมูลเอาต์พุต |
|---|---|---|
| แสดงตัวอย่าง | สัดส่วนภาพ: ความละเอียดที่เหมาะสมที่สุดสำหรับเป้าหมายในการตั้งค่า | ความละเอียดของพื้นผิวภายใน ระบบจะให้ข้อมูลเมตาเพื่อให้ View ครอบตัด ปรับขนาด และหมุนตามสัดส่วนภาพเป้าหมาย |
| ความละเอียดเริ่มต้น: ความละเอียดสูงสุดของตัวอย่าง หรือความละเอียดสูงสุดที่อุปกรณ์ต้องการซึ่งตรงกับสัดส่วนภาพของตัวอย่าง | ||
| ความละเอียดสูงสุด: ขนาดตัวอย่าง ซึ่งหมายถึงขนาดที่ตรงกับความละเอียดหน้าจอของอุปกรณ์มากที่สุด หรือ 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 ที่เฉพาะเจาะจงได้
ควบคุมเอาต์พุตของกล้อง
นอกจากจะช่วยให้คุณกำหนดค่าเอาต์พุตกล้องตามที่จำเป็นสำหรับ Use Case แต่ละรายการแล้ว CameraX ยังใช้อินเทอร์เฟซต่อไปนี้เพื่อรองรับการทำงานของกล้องที่ใช้ร่วมกันใน Use Case ที่เชื่อมโยงทั้งหมดด้วย
CameraControlช่วยให้คุณกำหนดค่าฟีเจอร์กล้องทั่วไปได้CameraInfoช่วยให้คุณค้นหาสถานะของฟีเจอร์กล้องทั่วไปเหล่านั้นได้
ฟีเจอร์กล้องที่รองรับ CameraControl มีดังนี้
- ซูม
- ไฟฉาย รวมถึงความสว่างของไฟฉายในอุปกรณ์ที่รองรับ
- การเพิ่มแสงในสภาวะแสงน้อยในอุปกรณ์ที่รองรับ
- โฟกัสและการวัดแสง (แตะเพื่อโฟกัส)
- การชดเชยการรับแสง
รับอินสแตนซ์ของ 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 ที่ไม่สำเร็จได้อีกต่อไป
ซูม
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 ที่แสดงผลโดย API ทั้ง 2 รายการมีตัวเลือกให้แอปพลิเคชันได้รับการแจ้งเตือนเมื่อคำขอซ้ำที่มีค่าการซูมที่ระบุเสร็จสมบูรณ์ นอกจากนี้ หากคุณตั้งค่าการซูมใหม่ขณะที่การดำเนินการก่อนหน้ายังคงทำงานอยู่ ListenableFuture ของการดำเนินการซูมก่อนหน้าจะดำเนินการไม่สำเร็จทันที
คบเพลิง
CameraControl.enableTorch(boolean)
เปิดหรือปิดไฟฉาย
CameraInfo.getTorchState()
ใช้เพื่อค้นหาสถานะไฟฉายปัจจุบันได้ คุณสามารถตรวจสอบค่าที่แสดงผลโดย CameraInfo.hasFlashUnit() เพื่อดูว่ามีไฟฉายพร้อมใช้งานหรือไม่ หากไม่ การเรียกใช้ CameraControl.enableTorch(boolean) จะทำให้ ListenableFuture ที่แสดงผลเสร็จสมบูรณ์ทันทีพร้อมผลลัพธ์ที่ล้มเหลว และตั้งค่าสถานะไฟฉายเป็น TorchState.OFF
เมื่อเปิดใช้ไฟฉาย ไฟฉายจะยังคงเปิดอยู่ระหว่างการถ่ายภาพและวิดีโอ ไม่ว่าจะตั้งค่าโหมดแฟลชไว้เป็นแบบใดก็ตาม โหมดแฟลชที่ตั้งค่าด้วย
ImageCapture.setFlashMode()
จะทำงานเมื่อปิดใช้ไฟฉายเท่านั้น
โฟกัสและการวัดแสง
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
// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener { view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_UP) {
val meteringPoint = previewView.meteringPointFactory
.createPoint(motionEvent.x, motionEvent.y)
// Build and start a FocusMeteringAction with meteringPoint.
view.performClick()
}
true
}
// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used
// for preview. 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 displayFactory = DisplayOrientedMeteringPointFactory(
surfaceView.display,
camera.cameraInfo,
surfaceView.width.toFloat(),
surfaceView.height.toFloat()
)
// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val surfaceFactory = SurfaceOrientedMeteringPointFactory(
imageWidth.toFloat(),
imageHeight.toFloat(),
imageAnalysis
)
startFocusAndMetering และ FocusMeteringAction
หากต้องการเรียกใช้ startFocusAndMetering()
แอปพลิเคชันต้องสร้าง FocusMeteringAction
ซึ่งประกอบด้วยออบเจ็กต์ MeteringPoint อย่างน้อย 1 รายการที่มีชุดค่าผสมโหมดการวัดแสงที่ไม่บังคับจาก FLAG_AF, FLAG_AE และ
FLAG_AWB โค้ดต่อไปนี้แสดงการใช้งานนี้
// meteringPointFactory is any MeteringPointFactory from the previous example.
val meteringPoint1 = meteringPointFactory.createPoint(x1, y1)
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 or 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 อีก 1 รายการสำหรับ AF และ AE เท่านั้น
เนื่องจากอุปกรณ์บางรุ่นไม่รองรับ AF/AE/AWB และหลายภูมิภาค CameraX จึงเรียกใช้ FocusMeteringAction อย่างเต็มความสามารถ CameraX จะใช้ออบเจ็กต์ MeteringPoint จำนวนสูงสุดที่รองรับตามลำดับที่มีการเพิ่มจุด ระบบจะละเว้นจุดทั้งหมดที่เพิ่มหลังจากจำนวนสูงสุด เช่น หากมีการระบุ FocusMeteringAction ที่มีออบเจ็กต์ MeteringPoint 3 รายการในแพลตฟอร์มที่รองรับเพียง 2 รายการ ระบบจะใช้เฉพาะจุด 2 จุดแรกเท่านั้น CameraX จะไม่สนใจ MeteringPoint สุดท้าย
การชดเชยการรับแสง
การชดเชยการรับแสงมีประโยชน์เมื่อแอปพลิเคชันต้องปรับแต่งค่าการรับแสง (EV) นอกเหนือจากผลลัพธ์การรับแสงอัตโนมัติ (AE) ค่าการชดเชยการรับแสงจะรวมกันในลักษณะต่อไปนี้เพื่อกำหนดค่าการรับแสงที่จำเป็นสำหรับสภาพรูปภาพปัจจุบัน
Exposure = ExposureCompensationIndex * ExposureCompensationStep
CameraX มีฟังก์ชัน CameraControl.setExposureCompensationIndex() สำหรับตั้งค่าการชดเชยการรับแสงเป็นค่าดัชนี
ค่าดัชนีที่เป็นบวกจะทำให้รูปภาพสว่างขึ้น ส่วนค่าที่เป็นลบจะทำให้รูปภาพมืดลง แอปพลิเคชันสามารถค้นหาช่วงที่รองรับได้ด้วย ExposureState.getExposureCompensationRange()
หากรองรับค่าดังกล่าว ListenableFuture ที่แสดงผลจะเสร็จสมบูรณ์เมื่อเปิดใช้ค่าในคำขอจับภาพสำเร็จ หากดัชนีที่ระบุอยู่นอกช่วงที่รองรับ setExposureCompensationIndex() จะทำให้ ListenableFuture ที่แสดงผลเสร็จสมบูรณ์ทันทีพร้อมผลลัพธ์ที่ล้มเหลว
CameraX จะเก็บเฉพาะคำขอ setExposureCompensationIndex() ที่ค้างอยู่ล่าสุดเท่านั้น หากคุณเรียกใช้ฟังก์ชันอีกครั้งก่อนที่ CameraX จะใช้คำขอก่อนหน้า CameraX จะยกเลิกคำขอก่อนหน้า
ข้อมูลโค้ดต่อไปนี้จะตั้งค่าดัชนีการชดเชยแสงและลงทะเบียน Callback สำหรับเมื่อมีการดำเนินการตามคำขอเปลี่ยนการรับแสง
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
// Handle the updated exposure compensation index.
}, mainExecutor)
CameraInfo.getExposureState()จะดึงข้อมูลExposureStateปัจจุบัน ซึ่งรวมถึงข้อมูลต่อไปนี้- ความสามารถในการรองรับการควบคุมการชดเชยการรับแสง
- ดัชนีการชดเชยการรับแสงปัจจุบัน
- ช่วงดัชนีการชดเชยแสง
- ขั้นตอนการชดเชยการรับแสงที่ใช้ในการคำนวณค่าการชดเชยการรับแสง
ตัวอย่างเช่น โค้ดต่อไปนี้จะเริ่มต้นการตั้งค่าสำหรับการเปิดรับแสง SeekBar ด้วยค่า ExposureState ปัจจุบัน เนื่องจากค่าความคืบหน้า SeekBar เริ่มต้นที่ 0 โค้ดจึงชดเชยค่าดังกล่าวด้วยขอบเขตล่างของช่วงการชดเชยการรับแสง
val exposureState = camera.cameraInfo.exposureState
val range = exposureState.exposureCompensationRange
binding.seekBar.apply {
isEnabled = exposureState.isExposureCompensationSupported
max = range.upper - range.lower
progress = exposureState.exposureCompensationIndex - range.lower
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
camera.cameraControl.setExposureCompensationIndex(progress + range.lower)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ CameraX ได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้
Codelab
ตัวอย่างโค้ด
ชุมชนนักพัฒนาซอฟต์แวร์
กลุ่มสนทนาเกี่ยวกับ Android CameraX