Tuỳ chọn cấu hình

Bạn định cấu hình từng trường hợp sử dụng CameraX để kiểm soát các chương trình thành phần khác nhau của thao tác trong trường hợp sử dụng.

Ví dụ: Với trường hợp sử dụng tính năng chụp ảnh, bạn có thể đặt tỷ lệ khung hình mục tiêu và chế độ flash. Mã sau đây cho thấy một ví dụ:

Kotlin

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

Java

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

Ngoài các tuỳ chọn cấu hình, một số trường hợp sử dụng còn cho phép các API linh động thay đổi chế độ cài đặt sau khi trường hợp sử dụng đã được tạo. Để biết thông tin về cấu hình cụ thể cho từng trường hợp sử dụng riêng, xem mục Triển khai bản xem trước, Phân tích hình ảnhChụp ảnh.

CameraXConfig

Để dễ sử dụng, CameraX có cấu hình mặc định như các bộ thực thi (executor) và bộ xử lý (handler) nội bộ phù hợp với hầu hết các tình huống sử dụng. Tuy nhiên, nếu ứng dụng của bạn có yêu cầu đặc biệt hoặc muốn tuỳ chỉnh các cấu hình đó, thì CameraXConfig là giao diện phục vụ mục đích đó.

Với CameraXConfig, ứng dụng sẽ có khả năng:

Mô hình sử dụng

Quy trình sau đây mô tả cách sử dụng CameraXConfig:

  1. Tạo đối tượng CameraXConfig có các cấu hình tuỳ chỉnh của bạn.
  2. Triển khai giao diện CameraXConfig.Provider trong Application và trả về đối tượng CameraXConfig trong getCameraXConfig().
  3. Thêm lớp Application vào tệp AndroidManifest.xml, như được mô tả tại đây.

Ví dụ: Mã mẫu sau đây giới hạn phạm vi ghi nhật ký của CameraX ở thông báo lỗi:

Kotlin

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

Giữ một bản sao cục bộ của đối tượng CameraXConfig nếu ứng dụng của bạn cần biết cấu hình CameraX sau khi đặt.

Trình giới hạn máy ảnh

Trong lần gọi ProcessCameraProvider.getInstance() đầu tiên, CameraX liệt kê và truy vấn các đặc điểm của máy ảnh có trên thiết bị. Vì CameraX cần giao tiếp với các thành phần phần cứng, nên quá trình này có thể làm mỗi máy ảnh mất một lượng thời gian không nhỏ, đặc biệt là trên các thiết bị cấp thấp. Nếu ứng dụng của bạn chỉ sử dụng các máy ảnh cụ thể trên thiết bị, chẳng hạn như máy ảnh trước mặc định, bạn có thể thiết lập cho CameraX bỏ qua các máy ảnh khác, nhờ đó giúp giảm độ trễ khởi động cho các máy ảnh mà ứng dụng của bạn sử dụng.

Nếu CameraSelector được truyền đến CameraXConfig.Builder.setAvailableCamerasLimiter() lọc ra một máy ảnh, CameraX sẽ hoạt động như thể máy ảnh đó không tồn tại. Ví dụ: Mã sau đây giới hạn sao cho ứng dụng chỉ sử dụng máy ảnh sau mặc định của thiết bị:

Kotlin

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

Threads

Nhiều API nền tảng là cơ sở CameraX được xây dựng yêu cầu chặn giao tiếp liên quy trình (IPC) với phần cứng đôi khi có thể mất hàng trăm mili giây để phản hồi. Vì lý do này, CameraX chỉ gọi những API này từ các luồng trong nền, giúp đảm bảo luồng chính không bị chặn và giao diện người dùng vẫn hiển thị linh hoạt. CameraX quản lý nội bộ các luồng trong nền này để hoạt động này xuất hiện minh bạch. Tuy nhiên, một số ứng dụng yêu cầu kiểm soát nghiêm ngặt đối với luồng. CameraXConfig cho phép ứng dụng đặt các luồng trong nền sẽ được sử dụng thông qua CameraXConfig.Builder.setCameraExecutor()CameraXConfig.Builder.setSchedulerHandler().

Bộ thực thi máy ảnh

Bộ thực thi máy ảnh được dùng cho mọi lệnh gọi API nền tảng Máy ảnh nội bộ, cũng như cho các lệnh gọi lại từ các API này. CameraX phân bổ và quản lý một Executor nội bộ để thực hiện những nhiệm vụ này. Tuy nhiên, nếu ứng dụng của bạn yêu cầu kiểm soát luồng nghiêm ngặt hơn, hãy sử dụng CameraXConfig.Builder.setCameraExecutor().

Bộ xử lý trình lập lịch biểu

Bộ xử lý trình lập lịch biểu dùng để lên lịch các nhiệm vụ nội bộ trong những khoảng thời gian cố định, chẳng hạn như thử mở lại máy ảnh khi máy ảnh không dùng được. Bộ xử lý này không thực thi các tác vụ và chỉ gửi chúng đến bộ thực thi máy ảnh. Đôi khi, bộ xử lý này cũng được dùng trên các nền tảng API cũ yêu cầu phải có Handler để gọi lại. Trong những trường hợp này, lệnh gọi lại vẫn chỉ được gửi trực tiếp đến bộ thực thi máy ảnh. CameraX phân bổ và quản lý một HandlerThread nội bộ để thực hiện những nhiệm vụ này, nhưng bạn có thể ghi đè bằng CameraXConfig.Builder.setSchedulerHandler().

Ghi nhật ký

Tính năng ghi nhật ký CameraX cho phép các ứng dụng lọc thông báo logcat vì tốt nhất là bạn nên tránh sử dụng thông báo chi tiết trong mã phát hành chính thức. CameraX hỗ trợ bốn cấp độ ghi nhật ký, từ chi tiết nhất đến nghiêm trọng nhất:

  • Log.DEBUG (mặc định)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Tham khảo tài liệu về Nhật ký Android để biết thông tin mô tả chi tiết về các cấp độ nhật ký này. Sử dụng CameraXConfig.Builder.setMinimumLoggingLevel(int) để đặt cấp độ ghi nhật ký thích hợp cho ứng dụng của bạn.

Tự động chọn

CameraX tự động cung cấp chức năng dành riêng cho thiết bị nơi ứng dụng của bạn đang chạy. Ví dụ: CameraX sẽ tự động xác định độ phân giải tốt nhất để sử dụng nếu bạn không chỉ định độ phân giải hoặc nếu độ phân giải mà bạn chỉ định không được hỗ trợ. Tất cả những việc này đều do thư viện xử lý, giúp bạn không cần ghi mã dành riêng cho thiết bị.

Mục tiêu của CameraX là khởi chạy thành công một phiên máy ảnh. Nói cách khác, CameraX sẽ điều chỉnh độ phân giải và tỷ lệ khung hình dựa trên tính năng của thiết bị. Sự điều chỉnh này có thể diễn ra vì:

  • Thiết bị không hỗ trợ độ phân giải mà bạn yêu cầu.
  • Thiết bị gặp các vấn đề về khả năng tương thích, chẳng hạn như các thiết bị cũ yêu cầu một số độ phân giải nhất định thì mới hoạt động chính xác.
  • Một số thiết bị chỉ có một số định dạng theo tỷ lệ khung hình nhất định.
  • Thiết bị có lựa chọn ưu tiên "nearest mod16" (mod16 gần nhất) để mã hoá tệp JPEG hoặc video. Xem SCALER_STREAM_CONFIGURATION_MAP để biết thêm thông tin.

Mặc dù CameraX tạo và quản lý phiên, nhưng bạn phải luôn kiểm tra kích thước hình ảnh được trả về ở dữ liệu đầu ra của trường hợp sử dụng trong mã của mình và điều chỉnh cho phù hợp.

Xoay

Theo mặc định, chế độ xoay của máy ảnh được đặt để khớp với chế độ xoay của màn hình mặc định trong khi tạo trường hợp sử dụng. Trong trường hợp mặc định này, CameraX sẽ tạo ra các dữ liệu đầu ra để cho phép ứng dụng dễ dàng khớp với những gì bạn mong muốn thấy được trong bản xem trước. Bạn có thể thay đổi chế độ xoay thành một giá trị tuỳ chỉnh để hỗ trợ các thiết bị nhiều màn hình bằng cách truyền hướng màn hình hiện tại khi định cấu hình các đối tượng trường hợp sử dụng hoặc truyền linh động sau khi tạo các đối tượng đó.

Ứng dụng của bạn có thể đặt chế độ xoay mục tiêu thông qua các chế độ cài đặt cấu hình. Khi đó, ứng dụng có thể cập nhật chế độ cài đặt xoay bằng cách sử dụng các phương thức trong API trường hợp sử dụng (chẳng hạn như ImageAnalysis.setTargetRotation()), ngay cả khi vòng đời ở trạng thái đang chạy. Bạn có thể sử dụng tính năng này khi ứng dụng bị khoá ở chế độ chân dung – và do đó không có hoạt động cấu hình lại nào diễn ra khi xoay – nhưng trường hợp sử dụng ảnh hoặc phân tích cần biết về chế độ xoay hiện tại của thiết bị. Ví dụ: Ứng dụng có thể cần nhận biết chế độ xoay để các khuôn mặt được hướng đúng cách giúp phát hiện khuôn mặt hoặc ảnh được đặt ở chế độ ngang hoặc chân dung.

Dữ liệu cho hình ảnh đã chụp có thể được lưu trữ mà không có thông tin về chế độ xoay. Dữ liệu Exif chứa thông tin về chế độ xoay để các ứng dụng thư viện có thể hiển thị hình ảnh theo đúng hướng sau khi lưu.

Để hiển thị dữ liệu xem trước theo hướng chính xác, bạn có thể sử dụng đầu ra siêu dữ liệu từ Preview.PreviewOutput() để tạo biến đổi.

Mã mẫu sau đây cho biết cách cài đặt chế độ xoay trên một sự kiện (event) hướng:

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();
}

Dựa trên chế độ xoay đã đặt, mỗi trường hợp sử dụng sẽ xoay dữ liệu hình ảnh trực tiếp hoặc cung cấp siêu dữ liệu về chế độ xoay cho người dùng dữ liệu hình ảnh không xoay.

  • Xem trước: Đầu ra siêu dữ liệu được cung cấp để xác định chế độ xoay độ phân giải mục tiêu bằng cách sử dụng Preview.getTargetRotation().
  • ImageAnalysis: Đầu ra siêu dữ liệu được cung cấp để xác định toạ độ vùng đệm hình ảnh so với toạ độ màn hình hiển thị.
  • ImageCapture: Siêu dữ liệu, vùng đệm Exif hình ảnh hoặc cả vùng đệm và siêu dữ liệu sẽ được thay đổi để ghi chú lại cài đặt chế độ xoay. Giá trị thay đổi tuỳ thuộc vào việc triển khai HAL (Lớp trừu tượng phần cứng).

Kích cỡ khuôn hình chữ nhật cắt ảnh

Theo mặc định, kích cỡ khuôn hình chữ nhật cắt ảnh là kích cỡ khuôn hình chữ nhật vùng đệm đầy đủ. Bạn có thể tuỳ chỉnh kích cỡ khuôn hình chữ nhật cắt ảnh bằng ViewPortUseCaseGroup. Bằng cách nhóm các trường hợp sử dụng và cài đặt khung nhìn, CameraX đảm bảo rằng kích cỡ khuôn hình chữ nhật cắt ảnh của tất cả các trường hợp sử dụng trong nhóm đó trỏ đến cùng một khu vực trong cảm biến của máy ảnh.

Đoạn mã sau đây cho thấy cách sử dụng hai lớp này:

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 xác định kích cỡ khuôn hình chữ nhật vùng đệm mà người dùng cuối nhìn thấy. Sau đó, CameraX tính toán kích cỡ khuôn hình chữ nhật cắt ảnh lớn nhất có thể dựa trên các thuộc tính của khung nhìn và các trường hợp sử dụng đính kèm. Thông thường, để đạt được hiệu ứng WYSIWYG, bạn nên định cấu hình khung nhìn dựa trên trường hợp sử dụng xem trước. Một cách đơn giản để tải khung nhìn là sử dụng PreviewView.

Các đoạn mã sau đây cho thấy cách tải đối tượng ViewPort:

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

Trong ví dụ trước, kết quả ứng dụng nhận được từ ImageAnalysisImageCapture khớp với kết quả mà người dùng cuối nhìn thấy trong PreviewView, giả sử loại tỷ lệ của PreviewView được đặt là giá trị mặc định, FILL_CENTER. Sau khi áp dụng kích cỡ khuôn hình chữ nhật cắt ảnh và chế độ xoay cho vùng đệm đầu ra, hình ảnh trong tất cả các trường hợp sử dụng sẽ giống nhau, mặc dù có thể có độ phân giải khác nhau. Để biết thêm thông tin về cách áp dụng thông tin biến đổi, xem đầu ra biến đổi.

Lựa chọn máy ảnh

CameraX tự động chọn thiết bị máy ảnh phù hợp nhất với các trường hợp sử dụng và yêu cầu của ứng dụng. Nếu muốn sử dụng một thiết bị khác với thiết bị được chọn cho mình, bạn có một số lựa chọn:

Mã mẫu sau đây minh hoạ cách tạo một CameraSelector để tác động đến việc lựa chọn thiết bị:

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)

Độ phân giải của máy ảnh

Bạn có thể chọn cho phép CameraX đặt độ phân giải hình ảnh dựa trên tổ hợp tính năng của thiết bị, cấp độ phần cứng được hỗ trợ của thiết bị, trường hợp sử dụng và tỷ lệ khung hình được cung cấp. Ngoài ra, bạn có thể đặt độ phân giải mục tiêu cụ thể hoặc tỷ lệ khung hình cụ thể trong các trường hợp sử dụng hỗ trợ cấu hình đó.

Độ phân giải tự động

CameraX có thể tự động xác định các chế độ cài đặt độ phân giải tốt nhất dựa trên các trường hợp sử dụng được chỉ định trong cameraProcessProvider.bindToLifecycle(). Bất cứ khi nào có thể, hãy chỉ định tất cả các trường hợp sử dụng cần thiết để chạy đồng thời trong một phiên duy nhất ở một lệnh gọi bindToLifecycle() duy nhất. CameraX sẽ xác định độ phân giải dựa trên nhóm trường hợp sử dụng bị ràng buộc bằng cách xem xét cấp độ phần cứng được hỗ trợ của thiết bị và bằng cách tính đến phương sai dành riêng cho thiết bị (trong đó thiết bị có thể vượt quá hoặc không đáp ứng cấu hình luồng có sẵn). Mục đích là để cho phép ứng dụng chạy trên nhiều loại thiết bị đồng thời giảm thiểu các đường dẫn mã dành riêng cho thiết bị.

Tỷ lệ khung hình mặc định cho các trường hợp sử dụng tính năng chụp ảnh và phân tích hình ảnh là 4:3.

Các trường hợp sử dụng có tỷ lệ khung hình có thể định cấu hình để cho phép ứng dụng chỉ định tỷ lệ khung hình mong muốn dựa trên thiết kế giao diện người dùng. Đầu ra CameraX sẽ được tạo để khớp với tỷ lệ khung hình được yêu cầu gần như tỷ lệ khung hình mà thiết bị hỗ trợ. Nếu không có độ phân giải khớp chính xác được hỗ trợ, thì độ phân giải đáp ứng nhiều điều kiện nhất sẽ được chọn. Do đó, ứng dụng sẽ chỉ ra cách máy ảnh hiển thị trong ứng dụng và CameraX xác định chế độ cài đặt độ phân giải máy ảnh tốt nhất để đáp ứng điều đó trên các thiết bị khác nhau.

Ví dụ: Một ứng dụng có thể thực hiện bất kỳ thao tác nào sau đây:

  • Chỉ định độ phân giải mục tiêu là 4:3 hoặc 16:9 cho một trường hợp sử dụng
  • Chỉ định độ phân giải tuỳ chỉnh mà CameraX sẽ cố gắng tìm kết quả khớp nhất
  • Chỉ định tỷ lệ khung hình cắt ảnh cho ImageCapture

CameraX sẽ tự động chọn độ phân giải bề mặt Camera2 bên trong. Bảng sau đây trình bày các độ phân giải:

Trường hợp sử dụng Độ phân giải bề mặt bên trong Độ phân giải dữ liệu đầu ra
Xem trước Tỷ lệ khung hình: Độ phân giải giúp mục tiêu phù hợp nhất với chế độ cài đặt. Độ phân giải bề mặt bên trong. Siêu dữ liệu được cung cấp để cho phép một chế độ Xem cắt, điều chỉnh theo tỷ lệ và xoay để đạt được tỷ lệ khung hình mục tiêu.
Độ phân giải mặc định: Độ phân giải xem trước cao nhất hoặc độ phân giải cao nhất mà thiết bị ưu tiên khớp với tỷ lệ khung hình ở trên.
Độ phân giải tối đa: Kích thước xem trước, tức là kích thước phù hợp nhất với độ phân giải màn hình của thiết bị hoặc với độ phân giải 1080p (1920x1080), tuỳ theo kích thước nào nhỏ hơn.
Phân tích hình ảnh Tỷ lệ khung hình: Độ phân giải giúp mục tiêu phù hợp nhất với chế độ cài đặt. Độ phân giải bề mặt bên trong.
Độ phân giải mặc định: Chế độ cài đặt độ phân giải mục tiêu mặc định là 640x480. Khi điều chỉnh cả độ phân giải mục tiêu và tỷ lệ khung hình tương ứng, kết quả sẽ là độ phân giải được hỗ trợ tốt nhất.
Độ phân giải tối đa: Độ phân giải đầu ra tối đa của thiết bị máy ảnh ở định dạng YUV_420_888 được lấy từ StreamConfigurationMap.getOutputSizes(). Theo mặc định, độ phân giải mục tiêu được đặt là 640x480, nên nếu muốn có độ phân giải lớn hơn 640x480, bạn phải sử dụng setTargetResolution()setTargetAspectRatio() để lấy giá trị gần nhất trong số các độ phân giải được hỗ trợ.
Chụp ảnh Tỷ lệ khung hình: Tỷ lệ khung hình phù hợp nhất với chế độ cài đặt. Độ phân giải bề mặt bên trong.
Độ phân giải mặc định: Độ phân giải cao nhất hiện có hoặc độ phân giải cao nhất mà thiết bị ưu tiên khớp với tỷ lệ khung hình ở trên.
Độ phân giải tối đa: Độ phân giải đầu ra tối đa của thiết bị máy ảnh ở định dạng JPEG. Sử dụng StreamConfigurationMap.getOutputSizes() để truy xuất dữ liệu này.

Chỉ định độ phân giải

Bạn có thể đặt độ phân giải cụ thể khi xây dựng các trường hợp sử dụng bằng cách sử dụng phương thức setTargetResolution(Size resolution), như trong mã mẫu sau đây:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

Bạn không thể đặt cả tỷ lệ khung hình mục tiêu và độ phân giải mục tiêu cho cùng một trường hợp sử dụng. Làm vậy sẽ tung ra (throw) IllegalArgumentException khi tạo đối tượng định cấu hình (config).

Nêu rõ độ phân giải Size trong khung toạ độ sau khi xoay các kích thước được hỗ trợ bằng chế độ xoay mục tiêu. Ví dụ: Một thiết bị có hướng mặc định theo chiều dọc đang xoay mục tiêu mặc định cần hình ảnh dọc có thể chỉ định 480x640, và thiết bị tương tự được xoay 90 độ và nhắm mục tiêu đến hướng ngang có thể chỉ định 640x480.

Độ phân giải mục tiêu cố gắng thiết lập một giới hạn tối thiểu cho độ phân giải hình ảnh. Độ phân giải hình ảnh thực tế sẽ là độ phân giải có sẵn gần nhất không nhỏ hơn độ phân giải mục tiêu, như được xác định trong quá trình triển khai Máy ảnh. Tuy nhiên, nếu không có độ phân giải nào bằng hoặc lớn hơn độ phân giải mục tiêu, thì chọn độ phân giải có sẵn gần nhất nhỏ hơn độ phân giải mục tiêu. Các độ phân giải có cùng tỷ lệ khung hình Size được cung cấp được ưu tiên hơn so với các độ phân giải có các tỷ lệ khung hình khác nhau.

Dựa trên yêu cầu, CameraX sẽ áp dụng độ phân giải phù hợp nhất. Nếu nhu cầu chính là để đáp ứng tỷ lệ khung hình, thì chỉ xác định setTargetAspectRatio, còn CameraX sẽ xác định độ phân giải cụ thể phù hợp với thiết bị. Nếu nhu cầu chính của ứng dụng là chỉ định độ phân giải để xử lý hình ảnh hiệu quả hơn (ví dụ: hình ảnh cỡ nhỏ hoặc cỡ trung dựa trên tính năng xử lý của thiết bị), thì sử dụng setTargetResolution(Size resolution).

Nếu ứng dụng của bạn yêu cầu độ phân giải chính xác thì hãy xem bảng trong createCaptureSession() để xác định độ phân giải tối đa được hỗ trợ theo từng cấp độ phần cứng. Để kiểm tra các độ phân giải cụ thể mà thiết bị hiện tại hỗ trợ, xem StreamConfigurationMap.getOutputSizes(int).

Nếu ứng dụng của bạn đang chạy trên Android 10 trở lên, bạn có thể sử dụng isSessionConfigurationSupported() để xác minh một SessionConfiguration cụ thể.

Kiểm soát đầu ra của máy ảnh

Ngoài việc cho phép bạn định cấu hình đầu ra của máy ảnh theo nhu cầu cho từng trường hợp sử dụng riêng, CameraX còn triển khai các giao diện sau đây để hỗ trợ các thao tác của máy ảnh phổ biến cho tất cả các trường hợp sử dụng ràng buộc:

  • CameraControl cho phép bạn định cấu hình các tính năng máy ảnh phổ biến.
  • CameraInfo cho phép bạn truy vấn trạng thái của những tính năng máy ảnh phổ biến đó.

Sau đây là các tính năng máy ảnh được hỗ trợ với CameraControl:

  • Thu phóng
  • Đèn pin
  • Lấy nét và đo sáng (nhấn để lấy nét)
  • Bù phơi sáng

Nhận thực thể của CameraControl và CameraInfo

Truy xuất các phiên bản của CameraControlCameraInfo bằng cách sử dụng đối tượng Camera do ProcessCameraProvider.bindToLifecyle() trả về. Mã sau đây cho thấy một ví dụ:

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()

Ví dụ: Bạn có thể gửi thao tác thu phóng và các thao tác CameraControl khác sau khi gọi bindToLifecycle(). Sau khi bạn dừng hoặc huỷ hoạt động dùng để liên kết thực thể máy ảnh, CameraControl sẽ không thực thi được các thao tác nữa và trả về một ListenableFuture không thành công.

Thu phóng

CameraControl cung cấp hai phương thức thay đổi mức thu phóng:

  • setZoomRatio() đặt mức thu phóng theo tỷ lệ thu phóng.

    Tỷ lệ này phải nằm trong phạm vi từ CameraInfo.getZoomState().getValue().getMinZoomRatio() đến CameraInfo.getZoomState().getValue().getMaxZoomRatio(). Nếu không, hàm sẽ trả về ListenableFuture không thành công.

  • setLinearZoom() đặt mức thu phóng hiện tại với giá trị thu phóng tuyến tính trong khoảng từ 0 đến 1,0.

    Ưu điểm của tính năng thu phóng tuyến tính là đảm bảo điều chỉnh theo tỷ lệ trường nhìn (FOV) với thay đổi về tỷ lệ thu phóng. Nhờ vậy mà đây là tính năng lý tưởng để sử dụng với chế độ xem Slider.

CameraInfo.getZoomState() trả về một LiveData của trạng thái thu phóng hiện tại. Giá trị thay đổi khi máy ảnh được khởi chạy hoặc nếu mức thu phóng được đặt bằng setZoomRatio() hoặc setLinearZoom(). Khi gọi một trong hai phương thức, bạn sẽ đặt giá trị hỗ trợ ZoomState.getZoomRatio()ZoomState.getLinearZoom(). Việc này rất hữu ích nếu bạn muốn hiển thị văn bản tỷ lệ thu phóng cùng với thanh trượt. Chỉ cần quan sát ZoomState LiveData để cập nhật cả hai mà không cần thực hiện chuyển đổi.

ListenableFuture mà cả hai API trả về đều cung cấp cho ứng dụng tuỳ chọn nhận thông báo khi hoàn tất yêu cầu lặp lại với giá trị thu phóng được chỉ định. Ngoài ra, nếu bạn đặt một giá trị thu phóng mới trong khi thao tác trước đó vẫn đang thực thi, ListenableFuture của thao tác thu phóng trước đó sẽ ngay lập tức bị lỗi.

Đèn pin

CameraControl.enableTorch(boolean) bật hoặc tắt đèn pin (còn gọi là đèn flash).

Bạn có thể dùng CameraInfo.getTorchState() để truy vấn trạng thái đèn pin hiện tại. Bạn có thể kiểm tra giá trị được CameraInfo.hasFlashUnit() trả về để xác định xem có đèn pin hay không. Nếu không, thao tác gọi CameraControl.enableTorch(boolean) khiến ListenableFuture được trả về ngay lập tức hoàn tất với kết quả không thành công và đặt trạng thái đèn pin thành TorchState.OFF.

Khi được bật, đèn pin sẽ vẫn duy trì trạng thái bật trong khi chụp ảnh và quay video bất kể chế độ cài đặt flashMode. flashMode trong ImageCapture chỉ hoạt động khi đèn pin bị tắt.

Lấy nét và đo sáng

CameraControl.startFocusAndMetering() kích hoạt chế độ tự động lấy nét và đo độ phơi sáng bằng cách đặt vùng đo sáng AF/AE/AWB dựa trên FocusMeteringAction được cho. Tính năng này thường dùng để triển khai tính năng "tap to focus" (nhấn để lấy nét) trong nhiều ứng dụng máy ảnh.

MeteringPoint

Để bắt đầu, hãy tạo một MeteringPoint bằng MeteringPointFactory.createPoint(float x, float y, float size). MeteringPoint đại diện cho một điểm duy nhất trên máy ảnh Surface. Điểm này được lưu trữ ở dạng chuẩn hoá để có thể dễ dàng chuyển đổi sang toạ độ cảm biến nhằm chỉ định vùng AF/AE/AWB.

Kích thước của MeteringPoint dao động từ 0 đến 1, với kích thước mặc định là 0,15f. Khi gọi MeteringPointFactory.createPoint(float x, float y, float size), CameraX tạo một vùng hình chữ nhật có tâm tại (x, y) cho size được cung cấp.

Mã sau đây minh hoạ cách tạo 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 may 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 và FocusMeteringAction

Để gọi startFocusAndMetering(), các ứng dụng phải xây dựng một FocusMeteringAction, bao gồm một hoặc nhiều MeteringPoints có kết hợp chế độ đo sáng không bắt buộc từ FLAG_AF, FLAG_AE, FLAG_AWB. Mã sau đây minh hoạ cách sử dụng này:

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 will be 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)

Như đã trình bày trong mã ở trên, startFocusAndMetering() sẽ lấy một FocusMeteringAction bao gồm một MeteringPoint cho vùng đo sáng AF/AE/AWB và một MeteringPoint khác chỉ dành cho AF và AE.

Trong nội bộ, CameraX chuyển đổi mã thành Camera2 MeteringRectangles và đặt các tham số CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS tương ứng với yêu cầu chụp.

Vì không phải mọi thiết bị đều hỗ trợ AF/AE/AWB và nhiều vùng, nên CameraX gắng hết sức thực thi FocusMeteringAction. CameraX sẽ sử dụng số lượng MeteringPoint tối đa được hỗ trợ theo thứ tự mà điểm được thêm vào. Tất cả MeteringPoint được thêm sau khi bỏ qua số lượng tối đa. Ví dụ: Nếu FocusMeteringAction được cung cấp cùng với 3 MeteringPoint trên nền tảng chỉ hỗ trợ 2, thì chỉ 2 MeteringPoint đầu tiên được sử dụng. MeteringPoint cuối cùng bị CameraX bỏ qua.

Bù phơi sáng

Tính năng bù phơi sáng rất hữu ích khi các ứng dụng cần tinh chỉnh các giá trị phơi sáng (EV) ngoài kết quả đầu ra phơi sáng tự động (AE). Các giá trị bù phơi sáng được kết hợp theo cách sau để xác định mức phơi sáng cần thiết cho các điều kiện hình ảnh hiện tại:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX cung cấp chức năng Camera.CameraControl.setExposureCompensationIndex() để đặt giá trị bù phơi sáng dưới dạng trị số.

Trị số dương làm cho hình ảnh sáng hơn, trong khi trị số âm làm mờ hình ảnh. Các ứng dụng có thể truy vấn phạm vi được hỗ trợ bằng CameraInfo.ExposureState.exposureCompensationRange() như được mô tả trong phần tiếp theo. Nếu giá trị được hỗ trợ, ListenableFuture được trả về sẽ hoàn tất khi giá trị được bật thành công trong yêu cầu chụp; nếu trị số đã chỉ định nằm ngoài phạm vi được hỗ trợ, setExposureCompensationIndex() sẽ khiến ListenableFuture được trả về ngay lập tức hoàn tất với kết quả không thành công.

CameraX chỉ giữ lại yêu cầu setExposureCompensationIndex() chưa xử lý mới nhất và gọi hàm nhiều lần trước khi yêu cầu trước đó được thực thi dẫn đến việc huỷ bỏ.

Đoạn mã sau đây sẽ thiết lập một trị số bù phơi sáng và đăng ký một lệnh gọi lại khi đã thực thi yêu cầu thay đổi độ phơi sáng:

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it may 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() truy xuất ExposureState hiện tại, bao gồm:

    • Khả năng hỗ trợ của tính năng kiểm soát bù phơi sáng.
    • Trị số bù phơi sáng hiện tại.
    • Phạm vi trị số bù phơi sáng.
    • Bước bù phơi sáng được dùng trong phép tính giá trị bù phơi sáng.

Ví dụ: Mã sau đây khởi chạy chế độ cài đặt cho SeekBar phơi sáng với các giá trị ExposureState hiện tại:

Kotlin

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

Tài nguyên khác

Để tìm hiểu thêm về CameraX, hãy tham khảo các tài nguyên bổ sung sau đây.

Lớp học lập trình

  • Bắt đầu sử dụng CameraX
  • Mã mẫu

  • Ứng dụng mẫu CameraX
  • Cộng đồng các nhà phát triển

    Nhóm thảo luận của Android CameraX