이미지 분석

이미지 분석 사용 사례에서는 이미지 처리, 컴퓨터 비전 또는 머신러닝 추론을 진행할 수 있도록 CPU에서 액세스 가능한 이미지를 앱에 제공합니다. 애플리케이션은 각 프레임에서 실행되는 analyze() 메서드를 구현합니다.

작동 모드

애플리케이션의 분석 파이프라인이 CameraX의 프레임 속도 요구사항을 따라갈 수 없는 경우 다음 방법 중 하나로 프레임을 드롭하도록 CameraX를 구성할 수 있습니다.

  • 비차단(기본값): 이 모드에서는 애플리케이션이 이전 이미지를 분석하는 동안 실행자가 항상 최신 이미지를 이미지 버퍼(깊이가 1인 큐와 유사)에 캐시합니다. 애플리케이션이 처리를 완료하기 전에 CameraX가 새 이미지를 수신하면 새 이미지가 동일한 버퍼에 저장되면서 이전 이미지를 덮어씁니다. 참고로, ImageAnalysis.Builder.setImageQueueDepth()는 이 시나리오와 별다른 관련이 없으며 버퍼 콘텐츠를 항상 덮어 쓰게 됩니다. STRATEGY_KEEP_ONLY_LATESTsetBackpressureStrategy()를 호출하여 이 비차단 모드를 사용 설정할 수 있습니다. 실행자가 미치는 영향에 관한 자세한 내용은 STRATEGY_KEEP_ONLY_LATEST 참조 문서를 확인하세요.

  • 차단: 이 모드에서는 내부 실행자가 내부 이미지 큐에 여러 개의 이미지를 추가할 수 있고, 큐가 가득 찬 경우에만 프레임을 드롭하기 시작합니다. 차단은 전체 카메라 기기 범위에서 이루어집니다. 즉, 카메라 기기에 결합된 사용 사례가 여러 개 있다면 CameraX가 이러한 이미지를 처리하는 동안 관련 사용 사례는 모두 차단됩니다. 예를 들어 미리보기와 이미지 분석이 모두 카메라 기기에 결합된 경우 CameraX가 이미지를 처리하는 동안 미리보기도 차단됩니다. STRATEGY_BLOCK_PRODUCERsetBackpressureStrategy()에 전달하여 차단 모드를 사용 설정할 수 있습니다. ImageAnalysis.Builder.setImageQueueDepth()를 사용하여 이미지 큐 깊이를 구성할 수도 있습니다.

지연 시간이 짧고 고성능 분석 도구를 사용해 총 이미지 분석 시간이 CameraX 프레임의 기간(예: 60fps의 경우 16ms)보다 짧은 경우 어떤 작동 모드를 사용해도 전반적으로 매끄러운 환경이 제공됩니다. 아주 짧은 시스템 지터를 처리할 때와 같은 일부 시나리오에서는 차단 모드가 여전히 유용할 수 있습니다.

지연 시간이 긴 고성능 분석 도구를 사용할 경우 지연 시간을 보완하려면 큐가 더 길게 설정된 차단 모드가 필요합니다. 하지만 애플리케이션에서 모든 프레임을 계속 처리할 수는 있습니다.

지연 시간이 길고 시간이 오래 걸리는 분석 도구(분석 도구에서 모든 프레임을 처리할 수는 없음)를 사용할 경우, 분석 경로를 위해 프레임을 드롭해야 하기 때문에 비차단 모드가 더 적합한 선택일 수 있습니다. 하지만 결합된 다른 동시 사용 사례에서 계속해서 모든 프레임이 인식될 수 있습니다.

구현

애플리케이션에서 이미지 분석을 사용하려면 다음 단계를 따르세요.

결합되면 바로 CameraX에서 이미지를 등록된 분석 도구에 전송합니다. 분석을 완료한 후 분석을 중지하려면 ImageAnalysis.clearAnalyzer()를 호출하거나 ImageAnalysis 사용 사례를 결합 해제합니다.

ImageAnalysis 사용 사례 빌드

ImageAnalysis가 분석 도구(이미지 소비자)를 이미지 생산자인 CameraX에 연결합니다. 애플리케이션은 ImageAnalysis.Builder를 사용하여 ImageAnalysis 객체를 빌드할 수 있습니다. 애플리케이션은 ImageAnalysis.Builder를 사용하여 다음을 구성할 수 있습니다.

애플리케이션은 해상도 또는 가로세로 비율을 설정할 수 있지만 둘 다 설정할 수는 없습니다. 정확한 출력 해상도는 애플리케이션의 요청된 크기(또는 가로세로 비율)와 하드웨어 기능에 따라 다릅니다. 그리고 요청된 크기 또는 비율과 다를 수 있습니다. 해상도 일치 알고리즘에 관한 자세한 내용은 setTargetResolution() 문서를 참고하세요.

애플리케이션은 출력 이미지 픽셀을 YUV(기본값) 또는 RGBA 색상 공간 형식으로 구성할 수 있습니다. RGBA 출력 형식을 설정할 때 CameraX는 내부적으로 이미지를 YUV에서 RGBA 색상 공간으로 변환하고 다음 시퀀스를 사용하여 ImageProxy의 첫 번째 평면(다른 2개의 평면은 사용되지 않음)의 ByteBuffer에 이미지 비트를 채웁니다.

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

기기가 프레임 속도를 따라갈 수 없는 복잡한 이미지 분석을 실행할 경우 이 주제의 작동 모드 섹션에 설명된 전략을 활용해 프레임을 드롭하도록 CameraX를 구성할 수 있습니다.

분석 도구 만들기

애플리케이션은 ImageAnalysis.Analyzer 인터페이스를 구현하고 analyze(ImageProxy image)를 재정의하여 분석 도구를 만들 수 있습니다. 각 분석 도구에서 애플리케이션은 Media.Image의 래퍼에 해당하는 ImageProxy를 수신합니다. 이미지 형식은 ImageProxy.getFormat()으로 쿼리할 수 있습니다. 형식은 애플리케이션이 ImageAnalysis.Builder와 함께 제공하는 다음 값 중 하나입니다.

  • ImageFormat.RGBA_8888(앱이 OUTPUT_IMAGE_FORMAT_RGBA_8888을 요청한 경우)
  • ImageFormat.YUV_420_888(앱이 OUTPUT_IMAGE_FORMAT_YUV_420_888을 요청한 경우)

색상 공간 구성과 픽셀 바이트를 가져올 수 있는 위치와 관련해서는 ImageAnalysis 사용 사례 빌드를 참고하세요.

분석 도구 내에서 애플리케이션은 다음을 실행해야 합니다.

  1. 가능한 한 빨리 주어진 프레임을 분석해야 합니다. 주어진 프레임 속도 시간 제한(예: 30fps 사례의 경우 32ms 미만) 내에서 하는 것이 좋습니다. 애플리케이션이 프레임을 빠르게 분석하지 못하는 경우 지원되는 프레임 드롭 메커니즘 중 하나를 사용하는 것이 좋습니다.
  2. ImageProxy.close()를 호출하여 ImageProxy를 CameraX에 해제해야 합니다. 래핑된 Media.Image의 close 함수(Media.Image.close())를 호출해서는 안 됩니다.

애플리케이션은 ImageProxy 내에서 래핑된 Media.Image를 직접 사용할 수 있습니다. 래핑된 이미지에 Media.Image.close()를 호출해서는 안 됩니다. CameraX 내의 이미지 공유 메커니즘이 중단되기 때문입니다. 대신 ImageProxy.close()를 사용하여 기본 Media.Image를 CameraX에 해제합니다.

ImageAnalysis를 위해 분석 도구 구성

분석 도구를 만든 후 분석을 시작하려면 ImageAnalysis.setAnalyzer()를 사용하여 분석 도구를 등록합니다. 분석이 완료되면 ImageAnalysis.clearAnalyer()를 사용하여 등록된 분석 도구를 삭제하면 됩니다.

이미지 분석에는 활성 분석 도구를 하나만 구성할 수 있습니다. ImageAnalysis.setAnalyzer()를 호출하면 이미 있는 등록된 분석 도구는 교체됩니다. 애플리케이션에서는 사용 사례 결합 전이나 후에 언제든지 새 분석 도구를 설정할 수 있습니다.

Lifecycle에 ImageAnalysis 결합

ProcessCameraProvider.bindToLifecycle() 함수를 사용하여 ImageAnalysis를 기존 AndroidX 수명 주기에 결합하는 것이 좋습니다. bindToLifecycle() 함수는 선택된 Camera 기기를 반환하고, 반환된 기기는 노출 등의 고급 설정을 미세 조정하는 데 사용할 수 있습니다. 카메라 출력을 제어하는 방법은 이 가이드에서 자세히 알아보세요.

다음은 이전 단계의 모든 작업 즉, CameraX ImageAnalysisPreview 사용 사례를 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)

자바

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

추가 리소스

CameraX에 관해 자세히 알아보려면 다음 추가 리소스를 참조하세요.

Codelab

  • CameraX 시작하기
  • 코드 샘플

  • CameraX 샘플 앱