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

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

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

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

  • 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 ที่ 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 ที่แสดงผลจะ 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