Analiza obrazu

Przypadek użycia analizy obrazu zapewnia aplikacji obraz dostępny dla procesora, na którym można przeprowadzić przetwarzanie obrazu, rozpoznawanie obrazów lub wnioskowanie uczenia maszynowego. Aplikacja implementuje metodę analyze(), która jest uruchamiana w każdej klatce.

Aby dowiedzieć się, jak zintegrować ML Kit od Google z aplikacją CameraX, zapoznaj się z artykułem ML Kit Analyzer.

Tryby pracy

Gdy potok analizy aplikacji nie nadąża za wymaganiami CameraX dotyczącymi liczby klatek, można skonfigurować CameraX tak, aby pomijał klatki na jeden z tych sposobów:

  • non-blocking (domyślny): w tym trybie wykonawca zawsze zapisuje najnowszy obraz w buforze obrazu (podobnie jak w kolejce o głębokości 1), podczas gdy aplikacja analizuje poprzedni obraz. Jeśli CameraX otrzyma nowy obraz, zanim aplikacja zakończy przetwarzanie, nowy obraz zostanie zapisany w tym samym buforze, zastępując poprzedni. Pamiętaj, że w tym scenariuszu ImageAnalysis.Builder.setImageQueueDepth() nie ma wpływu, a zawartość bufora jest zawsze zastępowana. Możesz włączyć ten tryb nieblokujący, wywołując setBackpressureStrategy() z STRATEGY_KEEP_ONLY_LATEST. Więcej informacji o konsekwencjach użycia wykonawcy znajdziesz w dokumentacji referencyjnej STRATEGY_KEEP_ONLY_LATEST.

  • blocking: w tym trybie wewnętrzny wykonawca może dodać wiele obrazów do wewnętrznej kolejki obrazów i zaczyna pomijać klatki dopiero wtedy, gdy kolejka jest pełna. Blokowanie dotyczy całego zakresu urządzenia z aparatem: jeśli urządzenie z aparatem ma wiele powiązanych przypadków użycia, wszystkie te przypadki użycia zostaną zablokowane, gdy CameraX będzie przetwarzać te obrazy. Na przykład gdy zarówno podgląd, jak i analiza obrazu są powiązane z urządzeniem aparatu, podgląd również zostanie zablokowany, gdy CameraX będzie przetwarzać obrazy. Tryb blokowania możesz włączyć, przekazując STRATEGY_BLOCK_PRODUCER do setBackpressureStrategy(). Możesz też skonfigurować głębokość kolejki obrazów za pomocą metody ImageAnalysis.Builder.setImageQueueDepth().

W przypadku analizatora o niskim czasie oczekiwania i wysokiej wydajności, gdy łączny czas analizy obrazu jest krótszy niż czas trwania klatki CameraX (np. 16 ms przy 60 kl./s), oba tryby działania zapewniają płynne działanie. Tryb blokowania może być nadal przydatny w niektórych sytuacjach, np. gdy występują bardzo krótkie zakłócenia systemu.

W przypadku analizatora o wysokiej wydajności i długim czasie oczekiwania tryb blokowania z dłuższą kolejką jest niezbędny do kompensacji czasu oczekiwania. Pamiętaj jednak, że aplikacja może nadal przetwarzać wszystkie klatki.

W przypadku analizatora o dużym opóźnieniu i czasochłonnym działaniu (analizator nie jest w stanie przetworzyć wszystkich klatek) lepszym wyborem może być tryb nieblokujący, ponieważ klatki muszą zostać odrzucone na potrzeby ścieżki analizy, ale inne równoczesne przypadki użycia mogą nadal widzieć wszystkie klatki.

Implementacja

Aby używać analizy obrazów w aplikacji, wykonaj te czynności:

Zaraz po powiązaniu CameraX wysyła obrazy do zarejestrowanego analizatora. Po zakończeniu analizy wywołaj ImageAnalysis.clearAnalyzer() lub odłącz przypadek użycia ImageAnalysis, aby zatrzymać analizę.

Tworzenie przypadku użycia ImageAnalysis

ImageAnalysis łączy analizator (odbiorcę obrazu) z CameraX, który jest producentem obrazu. Aplikacje mogą używać ImageAnalysis.Builder do tworzenia obiektu ImageAnalysis. Za pomocą ImageAnalysis.Builder aplikacja może skonfigurować te elementy:

Aplikacje mogą ustawiać rozdzielczość lub format obrazu, ale nie obie te wartości. Dokładna rozdzielczość wyjściowa zależy od rozmiaru (lub formatu obrazu) żądanego przez aplikację oraz możliwości sprzętowych i może się różnić od żądanego rozmiaru lub formatu. Informacje o algorytmie dopasowywania rozdzielczości znajdziesz w dokumentacji setTargetResolution().

Aplikacja może skonfigurować piksele obrazu wyjściowego tak, aby były w przestrzeni barw YUV (domyślnie) lub RGBA. Podczas ustawiania formatu wyjściowego RGBA CameraX wewnętrznie konwertuje obrazy z przestrzeni kolorów YUV na RGBA i pakuje bity obrazu do ByteBuffer pierwszej płaszczyzny ImageProxy (pozostałe 2 płaszczyzny nie są używane) w tej kolejności:

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

Podczas przeprowadzania skomplikowanej analizy obrazu, gdy urządzenie nie nadąża za liczbą klatek, możesz skonfigurować CameraX tak, aby pomijał klatki, korzystając ze strategii opisanych w sekcji Tryby działania w tym artykule.

Tworzenie analizatora

Aplikacje mogą tworzyć analizatory, implementując interfejs ImageAnalysis.Analyzer i zastępując analyze(ImageProxy image). W każdym analizatorze aplikacje otrzymują obiekt ImageProxy, który jest otoką dla Media.Image. Format pliku graficznego można sprawdzić za pomocą ImageProxy.getFormat(). Format jest jedną z tych wartości, które aplikacja udostępnia za pomocą ImageAnalysis.Builder:

  • ImageFormat.RGBA_8888, jeśli aplikacja prosi o OUTPUT_IMAGE_FORMAT_RGBA_8888.
  • ImageFormat.YUV_420_888, jeśli aplikacja prosi o OUTPUT_IMAGE_FORMAT_YUV_420_888.

Informacje o konfiguracjach przestrzeni barw i miejscach, z których można pobrać bajty pikseli, znajdziesz w sekcji Tworzenie przypadku użycia ImageAnalysis.

W analizatorze aplikacja powinna wykonać te czynności:

  1. Analizuj daną klatkę jak najszybciej, najlepiej w ramach limitu czasu liczby klatek (np. poniżej 32 ms w przypadku 30 kl./s). Jeśli aplikacja nie może przeanalizować klatki wystarczająco szybko, rozważ użycie jednego z obsługiwanych mechanizmów pomijania klatek.
  2. Zwolnij ImageProxy do CameraX, wywołując ImageProxy.close(). Pamiętaj, że nie należy wywoływać funkcji close (Media.Image.close()) opakowanego obiektu Media.Image.

Aplikacje mogą używać opakowanego obiektu Media.Image bezpośrednio w ImageProxy. Nie wywołuj metody Media.Image.close() na opakowanym obrazie, ponieważ spowoduje to uszkodzenie mechanizmu udostępniania obrazu w CameraX. Zamiast tego użyj metody ImageProxy.close(), aby zwolnić bazowy obiekt Media.Image do CameraX.

Konfigurowanie analizatora na potrzeby ImageAnalysis

Po utworzeniu analizatora użyj metody ImageAnalysis.setAnalyzer(), aby go zarejestrować i rozpocząć analizę. Po zakończeniu analizy użyj polecenia ImageAnalysis.clearAnalyzer(), aby usunąć zarejestrowany analizator.

Do analizy obrazu można skonfigurować tylko 1 aktywny analizator. Wywołanie ImageAnalysis.setAnalyzer() zastępuje zarejestrowany analizator, jeśli już istnieje. Aplikacje mogą ustawić nowy analizator w dowolnym momencie, przed powiązaniem przypadku użycia lub po nim.

Powiąż ImageAnalysis z cyklem życia

Zdecydowanie zalecamy powiązanie ImageAnalysis z istniejącym cyklem życia AndroidX za pomocą funkcji ProcessCameraProvider.bindToLifecycle(). Pamiętaj, że funkcja bindToLifecycle() zwraca wybrane urządzenie Camera, którego można użyć do dostosowania ustawień zaawansowanych, takich jak ekspozycja. Więcej informacji o sterowaniu obrazem z kamery znajdziesz w tym przewodniku.

Poniższy przykład łączy wszystkie elementy z poprzednich kroków, wiążąc przypadki użycia CameraX ImageAnalysis i Preview z właścicielem lifeCycle:

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)

Dodatkowe materiały

Więcej informacji o CameraX znajdziesz w tych materiałach.

Ćwiczenie w Codelabs

  • Pierwsze kroki z CameraX
  • Przykładowy kod

  • Przykładowe aplikacje CameraX