คุณกำหนดค่า 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 ของ Use Case (เช่น 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 การจัดกลุ่ม Use Case และการตั้งค่าวิวพอร์ตช่วยให้ CameraX รับประกันได้ว่าสี่เหลี่ยมผืนผ้าครอบตัดของ Use Case ทั้งหมดในกลุ่มจะชี้ไปยังพื้นที่เดียวกันในเซ็นเซอร์กล้อง
ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ 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 ตั้งค่าความละเอียดของรูปภาพตามความสามารถของอุปกรณ์ ระดับฮาร์ดแวร์ที่อุปกรณ์รองรับ กรณีการใช้งาน และสัดส่วนภาพที่ระบุได้ หรือจะตั้งค่าความละเอียดเป้าหมายที่เฉพาะเจาะจงหรือสัดส่วนภาพที่เฉพาะเจาะจงในกรณีการใช้งานที่รองรับการกำหนดค่าดังกล่าวก็ได้
การแก้ปัญหาอัตโนมัติ
CameraX สามารถกำหนดการตั้งค่าความละเอียดที่ดีที่สุดโดยอัตโนมัติตาม Use Case ที่ระบุไว้ใน cameraProcessProvider.bindToLifecycle() เมื่อใดก็ตามที่ทำได้ ให้ระบุกรณีการใช้งานทั้งหมดที่จำเป็นต้องเรียกใช้พร้อมกันในเซสชันเดียวในการเรียก bindToLifecycle() ครั้งเดียว CameraX จะกำหนดความละเอียดตามชุดกรณีการใช้งานที่เชื่อมโยงโดยพิจารณาจากระดับฮาร์ดแวร์ที่อุปกรณ์รองรับและคำนึงถึงความแปรปรวนเฉพาะอุปกรณ์ (ซึ่งอุปกรณ์มีประสิทธิภาพเกินหรือต่ำกว่าการกำหนดค่าสตรีมที่ใช้ได้)
จุดประสงค์คือเพื่อให้แอปพลิเคชันทำงานบนอุปกรณ์ที่หลากหลายได้โดยลดเส้นทางโค้ดเฉพาะอุปกรณ์ให้เหลือน้อยที่สุด
สัดส่วนภาพเริ่มต้นสำหรับ 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
เมื่อเปิดใช้ไฟฉาย ไฟฉายจะยังคงเปิดอยู่ระหว่างการถ่ายภาพและวิดีโอ ไม่ว่าจะตั้งค่า 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 อีก 1 รายการสำหรับ AF และ AE เท่านั้น
ภายใน CameraX จะแปลงเป็น Camera2
MeteringRectangles
และตั้งค่าพารามิเตอร์
CONTROL_AF_REGIONS
/
CONTROL_AE_REGIONS
/
CONTROL_AWB_REGIONS
ที่เกี่ยวข้องกับคำขอจับภาพ
เนื่องจากอุปกรณ์บางรุ่นไม่รองรับ AF/AE/AWB และหลายภูมิภาค CameraX จึงดำเนินการ FocusMeteringAction อย่างเต็มความสามารถ CameraX จะใช้ MeteringPoint สูงสุดที่รองรับตามลำดับที่เพิ่มจุด ระบบจะละเว้น 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() ที่ค้างอยู่ล่าสุดเท่านั้น และการเรียกใช้ฟังก์ชันหลายครั้งก่อนที่คำขอก่อนหน้าจะได้รับการดำเนินการจะส่งผลให้มีการยกเลิกคำขอดังกล่าว
ข้อมูลโค้ดต่อไปนี้จะตั้งค่าดัชนีการชดเชยแสงและลงทะเบียน Callback สำหรับเมื่อมีการดำเนินการตามคำขอเปลี่ยนการรับแสง
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
ตัวอย่างโค้ด
ชุมชนนักพัฒนาซอฟต์แวร์
กลุ่มสนทนาเกี่ยวกับ Android CameraX