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

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

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

Kotlin

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

Java

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

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

CameraXConfig

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

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

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

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

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

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

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

Kotlin

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

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

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

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

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

Kotlin

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

ชุดข้อความ

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

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

โปรแกรมที่ใช้เรียกใช้กล้องจะใช้กับการเรียก API ของแพลตฟอร์มกล้องภายในทั้งหมด รวมถึงการเรียกกลับจาก 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 ในกรณีเริ่มต้นนี้ 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() ซึ่งจะเป็นประโยชน์หากคุณต้องการแสดงข้อความอัตราส่วนการซูมควบคู่ไปกับแถบเลื่อน เพียงดูที่ 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
  • ตัวอย่างโค้ด

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

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