Phân tích hình ảnh

Trường hợp sử dụng phân tích hình ảnh sẽ cung cấp cho ứng dụng của bạn một hình ảnh mà CPU có thể truy cập để bạn có thể thực hiện quá trình xử lý hình ảnh, thị giác máy tính hoặc dự đoán máy học. Ứng dụng sẽ triển khai phương thức analyze() được chạy trên mỗi khung hình.

Chế độ hoạt động

Khi quy trình phân tích của ứng dụng không thể đáp ứng các yêu cầu về tốc độ khung hình của CameraX, bạn có thể thiết lập CameraX để bỏ khung hình theo một trong những cách sau:

  • không chặn (mặc định): Trong chế độ này, trình thực thi sẽ luôn lưu hình ảnh mới nhất vào bộ đệm hình ảnh (tương tự như hàng đợi có chiều sâu là một) khi ứng dụng phân tích hình ảnh trước đó. Nếu CameraX nhận được hình ảnh mới trước khi ứng dụng kết thúc quá trình xử lý, hình ảnh mới sẽ được lưu vào cùng một bộ đệm, ghi đè hình ảnh trước đó. Xin lưu ý rằng ImageAnalysis.Builder.setImageQueueDepth() không có ảnh hưởng trong trường hợp này và nội dung của bộ đệm sẽ luôn bị ghi đè. Bạn có thể bật chế độ không chặn này bằng cách gọi setBackpressureStrategy() với STRATEGY_KEEP_ONLY_LATEST. Để biết thêm thông tin về các ngụ ý của trình thực thi, hãy xem tài liệu tham khảo dành cho STRATEGY_KEEP_ONLY_LATEST.

  • chặn: Trong chế độ này, trình thực thi nội bộ có thể thêm nhiều hình ảnh vào hàng đợi hình ảnh nội bộ và chỉ bắt đầu bỏ khung hình khi hàng đợi đã đầy. Quá trình chặn xảy ra trên toàn bộ phạm vi của thiết bị máy ảnh: nếu thiết bị máy ảnh có nhiều trường hợp sử dụng liên kết, thì tất cả các trường hợp sử dụng đó đều bị chặn khi CameraX xử lý các hình ảnh này. Ví dụ: khi cả chức năng xem trước và phân tích hình ảnh đều được liên kết với một thiết bị Máy ảnh, thì chức năng xem trước cũng sẽ bị chặn khi CameraX xử lý hình ảnh. Bạn có thể bật chế độ chặn bằng cách chuyển STRATEGY_BLOCK_PRODUCER vào setBackpressureStrategy(). Bạn cũng có thể thiết lập chiều sâu của hàng đợi hình ảnh bằng cách sử dụng ImageAnalysis.Builder.setImageQueueDepth().

Với trình phân tích có độ trễ thấp và hiệu suất cao trong đó tổng thời gian phân tích hình ảnh nhỏ hơn thời lượng của khung hình CameraX (ví dụ: 16 mili giây cho 60 khung hình/giây), thì chế độ hoạt động sẽ mang lại trải nghiệm chung mượt mà. Chế độ chặn vẫn có thể hữu ích trong một số trường hợp, chẳng hạn như khi xử lý các dao động rất ngắn của hệ thống.

Với trình phân tích có độ trễ cao và hiệu suất cao, thì chế độ chặn có hàng đợi dài hơn là cần thiết để bù độ trễ. Tuy nhiên, hãy lưu ý rằng ứng dụng vẫn có thể xử lý tất cả các khung hình.

Với trình phân tích có độ trễ cao và tốn nhiều thời gian (trình phân tích không thể xử lý tất cả khung hình), thì chế độ không chặn có thể là lựa chọn thích hợp hơn vì cần phải bỏ khung hình cho đường dẫn phân tích. Tuy nhiên, các trường hợp sử dụng liên kết đồng thời khác vẫn có thể thấy tất cả khung hình.

Triển khai

Để dùng chức năng phân tích hình ảnh trong ứng dụng của bạn, hãy làm theo các bước sau:

Ngay sau khi liên kết, CameraX sẽ gửi hình ảnh đến trình phân tích mà bạn đã đăng ký. Sau khi hoàn tất quá trình phân tích, hãy gọi ImageAnalysis.clearAnalyzer() hoặc huỷ liên kết trường hợp sử dụng ImageAnalysis để ngừng phân tích.

Xây dựng trường hợp sử dụng ImageAnalysis

ImageAnalysis kết nối trình phân tích của bạn (ứng dụng dùng hình ảnh) với CameraX (ứng dụng tạo hình ảnh). Các ứng dụng có thể dùng ImageAnalysis.Builder để xây dựng đối tượng ImageAnalysis. Với ImageAnalysis.Builder, ứng dụng có thể định cấu hình các mục sau:

Các ứng dụng có thể đặt độ phân giải hoặc tỷ lệ khung hình, nhưng không thể đặt cả hai. Độ phân giải đầu ra chính xác phụ thuộc vào kích thước (hoặc tỷ lệ khung hình) và chức năng phần cứng mà ứng dụng yêu cầu, cũng như có thể khác với kích thước hoặc tỷ lệ yêu cầu. Để biết thông tin về thuật toán khớp độ phân giải, hãy xem tài liệu dành cho setTargetResolution()

Một ứng dụng có thể thiết lập điểm ảnh đầu ra theo hệ màu YUV (mặc định) hoặc RGBA. Khi đặt định dạng đầu ra RGBA, CameraX sẽ chuyển đổi nội bộ hình ảnh từ hệ màu YUV sang RGBA và gói bit hình ảnh vào ByteBuffer trong mặt phẳng đầu tiên của ImageProxy (hai mặt phẳng khác không được sử dụng) theo trình tự sau:

ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...

Khi thực hiện quá trình phân tích hình ảnh phức tạp mà thiết bị không thể bắt kịp tốc độ khung hình, bạn có thể thiết lập CameraX để bỏ khung hình bằng các chiến lược được mô tả trong phần Chế độ hoạt động của chủ đề này.

Tạo trình phân tích

Các ứng dụng có thể tạo trình phân tích bằng cách triển khai giao diện ImageAnalysis.Analyzer và ghi đè analyze(ImageProxy image). Trong mỗi trình phân tích, các ứng dụng sẽ nhận được ImageProxy. Đây là một trình bao bọc cho Media.Image. Bạn có thể truy vấn định dạng hình ảnh bằng ImageProxy.getFormat(). Định dạng là một trong những giá trị sau đây mà ứng dụng cung cấp thông qua ImageAnalysis.Builder:

  • ImageFormat.RGBA_8888 nếu ứng dụng yêu cầu OUTPUT_IMAGE_FORMAT_RGBA_8888.
  • ImageFormat.YUV_420_888 nếu ứng dụng yêu cầu OUTPUT_IMAGE_FORMAT_YUV_420_888.

Hãy xem phần Xây dựng trường hợp sử dụng ImageAnalysis để biết cấu hình hệ màu và nơi có thể truy xuất byte điểm ảnh.

Bên trong một trình phân tích, ứng dụng sẽ có chức năng sau:

  1. Phân tích một khung hình đã cho nhanh nhất có thể, tốt nhất là trong giới hạn thời gian tốc độ khung hình nhất định (ví dụ: dưới 32 mili giây cho 30 khung hình/giây). Nếu ứng dụng không thể phân tích một khung hình đủ nhanh, hãy cân nhắc áp dụng một trong những cơ chế bỏ khung hình được hỗ trợ.
  2. Chuyển ImageProxy sang cho CameraX bằng cách gọi ImageProxy.close(). Xin lưu ý rằng bạn không nên gọi hàm đóng của Media.Image (Media.Image.close()) đã bọc.

Các ứng dụng có thể dùng trực tiếp Media.Image đã bọc bên trong ImageProxy. Đừng gọi Media.Image.close() trên hình ảnh đã bọc vì việc này sẽ làm hỏng cơ chế chia sẻ hình ảnh bên trong CameraX. Thay vào đó, hãy dùng ImageProxy.close() để chuyển Media.Image cơ bản sang cho CameraX.

Định cấu hình trình phân tích cho ImageAnalysis

Sau khi bạn tạo một trình phân tích, hãy dùng ImageAnalysis.setAnalyzer() để đăng ký trình phân tích đó nhằm bắt đầu phân tích. Sau khi bạn phân tích xong, hãy dùng ImageAnalysis.clearAnalyzer() để xoá trình phân tích đã đăng ký.

Bạn chỉ có thể định cấu hình một trình phân tích đang hoạt động để phân tích hình ảnh. Thao tác gọi ImageAnalysis.setAnalyzer() sẽ thay thế trình phân tích đã đăng ký (nếu có). Các ứng dụng có thể đặt một trình phân tích mới bất cứ lúc nào, trước hoặc sau khi liên kết trường hợp sử dụng.

Liên kết ImageAnalysis với vòng đời

Bạn nên liên kết ImageAnalysis của mình với một vòng đời AndroidX hiện có bằng hàm ProcessCameraProvider.bindToLifecycle(). Xin lưu ý rằng hàm bindToLifecycle() trả về thiết bị Camera đã chọn. Bạn có thể dùng thiết bị đó để tinh chỉnh chế độ cài đặt nâng cao như độ phơi sáng và các chế độ khác. Hãy xem hướng dẫn này để biết thêm thông tin về cách kiểm soát đầu ra của máy ảnh.

Ví dụ sau đây kết hợp mọi thứ từ những bước trước, liên kết trường hợp sử dụng ImageAnalysisPreview của CameraX với chủ sở hữu lifeCycle:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    // enable the following line if RGBA output is needed.
    // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
    .setTargetResolution(Size(1280, 720))
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()
imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy ->
    val rotationDegrees = imageProxy.imageInfo.rotationDegrees
    // insert your code here.
    ...
    // after done, release the ImageProxy object
    imageProxy.close()
})

cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)

Java

ImageAnalysis imageAnalysis =
    new ImageAnalysis.Builder()
        // enable the following line if RGBA output is needed.
        //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
        .setTargetResolution(new Size(1280, 720))
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .build();

imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
    @Override
    public void analyze(@NonNull ImageProxy imageProxy) {
        int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
            // insert your code here.
            ...
            // after done, release the ImageProxy object
            imageProxy.close();
        }
    });

cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);

Tài nguyên khác

Để tìm hiểu thêm về CameraX, hãy xem 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