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

คุณกำหนดค่า 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()

ตัวแฮนเดิลเครื่องจัดตารางเวลา

ตัวแฮนเดิลของตัวกำหนดเวลาใช้ในการกำหนดเวลางานภายในในช่วงเวลาที่แน่นอน เช่น การลองเปิดกล้องอีกครั้งเมื่อไม่พร้อมใช้งาน ตัวแฮนเดิลนี้จะไม่เรียกใช้ Job และจะส่งไปยังตัวเรียกใช้กล้องเท่านั้น นอกจากนี้ยังใช้ในแพลตฟอร์ม 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 จะลดความละเอียดและสัดส่วนการแสดงผลตามความสามารถของอุปกรณ์ การประนีประนอมอาจเกิดขึ้นเนื่องจากสาเหตุต่อไปนี้

  • อุปกรณ์ไม่รองรับความละเอียดที่ขอ
  • อุปกรณ์มีปัญหาด้านความเข้ากันได้ เช่น อุปกรณ์รุ่นเก่าที่ต้องใช้ความละเอียด บางอย่างจึงจะทำงานได้อย่างถูกต้อง
  • ในอุปกรณ์บางรุ่น รูปแบบบางอย่างจะใช้ได้เฉพาะในอัตราส่วน ภาพบางอย่างเท่านั้น
  • อุปกรณ์มีค่ากำหนดเป็น "nearest 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 การจัดกลุ่มกรณีการใช้งานและการตั้งค่า Viewport ช่วยให้ 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 ที่แนบมา โดยปกติแล้ว คุณสามารถกำหนดค่าวิวพอร์ตตาม Use Case ของการแสดงตัวอย่างเพื่อให้ได้เอฟเฟกต์ WYSIWYG วิธีง่ายๆ ในการรับวิวพอร์ตคือ การใช้ 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 หลังจากใช้ สี่เหลี่ยมผืนผ้าครอบตัดและการหมุนกับบัฟเฟอร์เอาต์พุตแล้ว รูปภาพจากกรณีการใช้งานทั้งหมด จะเหมือนกัน แม้ว่าความละเอียดอาจแตกต่างกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้ข้อมูลการเปลี่ยนรูปแบบได้ที่เปลี่ยนรูปแบบ เอาต์พุต

การเลือกกล้อง

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

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้าง 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 และวิธีรับออบเจ็กต์กล้องทั้ง 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 สามารถกำหนดการตั้งค่าความละเอียดที่ดีที่สุดโดยอัตโนมัติตาม กรณีการใช้งานที่ระบุไว้ใน cameraProcessProvider.bindToLifecycle() เมื่อใดก็ตามที่เป็นไปได้ ให้ระบุ Use Case ทั้งหมดที่จำเป็นต่อการเรียกใช้พร้อมกันในเซสชันเดียวในbindToLifecycle()การเรียกใช้เดียว CameraX จะกำหนดความละเอียด โดยอิงตามชุดกรณีการใช้งานที่ผูกไว้ด้วยการพิจารณาระดับฮาร์ดแวร์ที่อุปกรณ์รองรับ และโดยคำนึงถึงความแปรปรวนเฉพาะอุปกรณ์ (ในกรณีที่อุปกรณ์ มีค่าเกินหรือต่ำกว่าการกำหนดค่าสตรีม ที่ใช้ได้) โดยมีจุดประสงค์เพื่อให้แอปพลิเคชันทำงานได้ในอุปกรณ์หลากหลายประเภท ขณะเดียวกันก็ลดเส้นทางโค้ดเฉพาะอุปกรณ์ให้เหลือน้อยที่สุด

สัดส่วนภาพเริ่มต้นสำหรับ Use Case การจับภาพและการวิเคราะห์รูปภาพคือ 4:3

Use Case มีอัตราส่วนภาพที่กำหนดค่าได้เพื่อให้แอปพลิเคชันระบุ อัตราส่วนภาพที่ต้องการตามการออกแบบ 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() เพื่อเรียกข้อมูลนี้

ระบุความละเอียด

คุณสามารถตั้งค่าความละเอียดที่เฉพาะเจาะจงเมื่อสร้างกรณีการใช้งานโดยใช้เมธอด 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 ยังใช้ส่วนติดต่อต่อไปนี้เพื่อรองรับการทำงานของกล้องที่ใช้ร่วมกันในกรณีการใช้งานที่เชื่อมโยงทั้งหมดด้วย

  • 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 หนึ่งรายการสำหรับภูมิภาคการวัด AF/AE/AWB และ MeteringPoint อีกรายการสำหรับ AF และ AE เท่านั้น

ภายใน CameraX จะแปลงเป็น Camera2 MeteringRectangles และตั้งค่า CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS ที่สอดคล้องกันให้กับคำขอจับภาพ

เนื่องจากอุปกรณ์บางรุ่นไม่รองรับ AF/AE/AWB และหลายภูมิภาค CameraX จึงดำเนินการ FocusMeteringAction อย่างเต็มที่ CameraX ใช้จำนวนสูงสุด ของ MeteringPoint ที่รองรับตามลำดับการเพิ่มจุด ระบบจะละเว้น MeteringPoint ทั้งหมดที่เพิ่มหลังจากถึงจำนวนสูงสุด เช่น หากระบุ a 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

  • เริ่มต้นใช้งาน CameraX
  • ตัวอย่างโค้ด

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

    กลุ่มสนทนา Android CameraX