Analiza obrazu

Przypadek użycia związany z analizą obrazu udostępnia aplikacji obraz dostępny dla procesora, na którym można przetwarzać obraz, wizualizować obraz lub wnioskować przez systemy uczące się. Aplikacja implementuje metodę analyze(), która jest uruchamiana w każdej klatce.

Aby dowiedzieć się, jak zintegrować pakiet Google ML Kit z aplikacją CameraX, przeczytaj artykuł o Analizatorze ML Kit.

Tryby pracy

Gdy potok analizy aplikacji nie spełnia wymagań aparatu CameraX w zakresie liczby klatek, można skonfigurować kamerę X w taki sposób, by pomijała klatki w jeden z tych sposobów:

  • nieblokujący (domyślny): w tym trybie wykonawcy zawsze zapisuje w pamięci podręcznej najnowszy obraz (podobnie do kolejki o głębokości 1), podczas gdy aplikacja analizuje poprzedni obraz. Jeśli CameraX otrzyma nowe zdjęcie przed zakończeniem przetwarzania przez aplikację, zostanie ono zapisane w tym samym buforze i zastąpi poprzednie. Pamiętaj, że w tym scenariuszu ImageAnalysis.Builder.setImageQueueDepth() nie ma żadnego wpływu, a zawartość bufora jest zawsze zastępowana. Aby włączyć ten tryb nieblokowania, wywołaj setBackpressureStrategy() za pomocą STRATEGY_KEEP_ONLY_LATEST. Więcej informacji o konsekwencjach dla wykonawcy znajdziesz w dokumentacji referencyjnej STRATEGY_KEEP_ONLY_LATEST.

  • blokowanie: w tym trybie wewnętrzny wykonawca może dodawać wiele obrazów do wewnętrznej kolejki obrazów i rozpoczyna pomijanie klatek tylko wtedy, gdy kolejka jest pełna. Blokada obejmuje cały zakres kamery: jeśli kamera ma wiele powiązanych przypadków użycia, wszystkie te przypadki użycia będą zablokowane podczas przetwarzania obrazów przez CameraX. Na przykład: jeśli z urządzeniem Aparat powiązany jest zarówno podgląd, jak i analiza obrazu, podgląd też będzie blokowany podczas przetwarzania zdjęć przez CameraX. Aby włączyć tryb blokowania, możesz przekazać adres STRATEGY_BLOCK_PRODUCER do setBackpressureStrategy(). Głębokość kolejki obrazów możesz też skonfigurować za pomocą funkcji ImageAnalysis.Builder.setImageQueueDepth().

Dzięki analizatorze obrazu o krótkim czasie oczekiwania i wysokiej wydajności, w którym łączny czas analizowania obrazu jest krótszy niż czas trwania klatki w Aparacie X (np. 16 ms przy 60 kl./s), oba tryby działają płynnie. Tryb blokowania może być przydatny w niektórych sytuacjach, np. w przypadku bardzo krótkich zakłóceń systemu.

Przy dużym opóźnieniach i wysokiej wydajności analizatorze tryb blokowania z dłuższą kolejką jest niezbędny w celu skompensowania opóźnień. Aplikacja może jednak nadal przetwarzać wszystkie ramki.

Ze względu na długi czas oczekiwania i czasochłonność analizatora (analizator nie jest w stanie przetworzyć wszystkich klatek) lepszym wyborem może być tryb nieblokujący, ponieważ klatki muszą być pomijane na potrzeby ścieżki analizy, ale w innych równoczesnych przypadkach użycia nadal będą widoczne wszystkie klatki.

Implementacja

Aby skorzystać z analizy obrazu w aplikacji, wykonaj te czynności:

Bezpośrednio po powiązaniu usługa CameraX wysyła zdjęcia do zarejestrowanego analizatora. Po zakończeniu analizy wywołaj ImageAnalysis.clearAnalyzer() lub usuń powiązanie przypadku użycia ImageAnalysis, aby zatrzymać analizę.

Przypadek użycia analizy obrazów

ImageAnalysis łączy analizator (konsumenta obrazów) z aparatem CameraX, który jest producentem obrazu. ImageAnalysis.Builder mogą służyć do budowania obiektu ImageAnalysis. Za pomocą ImageAnalysis.Builder aplikacja może konfigurować te elementy:

W aplikacjach można ustawić rozdzielczość lub format obrazu, ale nie oba te rodzaje naraz. Dokładna rozdzielczość wyjściowa zależy od żądanego rozmiaru (lub formatu obrazu) aplikacji oraz możliwości sprzętu. Może się ona różnić od żądanego rozmiaru lub współczynnika proporcji. Więcej informacji o algorytmie dopasowywania rozdzielczości znajdziesz w dokumentacji dotyczącej setTargetResolution()

Aplikacja może skonfigurować piksele obrazu wyjściowego tak, aby znajdowały się w przestrzeni kolorów YUV (domyślnej) lub RGBA. Podczas ustawiania formatu wyjściowego RGBA CameraX wewnętrznie konwertuje obrazy z YUV na przestrzeń kolorów RGBA i pakuje bity obrazu do ByteBuffer pierwszej platformy 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
...

Gdy przeprowadzasz skomplikowaną analizę obrazu, gdy urządzenie nie nadąża za liczbą klatek, możesz skonfigurować aparat CameraX tak, aby pomijał klatki zgodnie ze strategiami opisanymi w sekcji Tryby operacyjne w tym temacie.

Tworzenie analizatora

Aplikacje mogą tworzyć analizatory, implementując interfejs ImageAnalysis.Analyzer i zastępując interfejs analyze(ImageProxy image). W każdym analizatorze aplikacje otrzymują kod ImageProxy, który jest kodem Media.Image. Zapytanie o format obrazu można wysłać za pomocą ImageProxy.getFormat(). Jest to jedna z tych wartości, które aplikacja podaje w polu ImageAnalysis.Builder:

  • ImageFormat.RGBA_8888, jeśli aplikacja zażądała OUTPUT_IMAGE_FORMAT_RGBA_8888.
  • ImageFormat.YUV_420_888, jeśli aplikacja zażądała OUTPUT_IMAGE_FORMAT_YUV_420_888.

Informacje o konfigurowaniu przestrzeni kolorów i miejscu pobierania bajtów pikseli znajdziesz w tym artykule.

Wewnątrz analizatora aplikacja powinna wykonywać następujące zadania:

  1. Analizuj daną klatkę jak najszybciej, najlepiej w ramach podanego limitu czasu (np. mniej niż 32 ms w przypadku 30 kl./s). Jeśli aplikacja nie może wystarczająco szybko przeanalizować klatki, rozważ zastosowanie jednego z obsługiwanych mechanizmów pomijania klatek.
  2. Zwolnij przycisk ImageProxy, aby połączyć się z aparatem CameraX, wywołując metodę ImageProxy.close(). Pamiętaj, że nie należy wywoływać funkcji zamykania opakowanego elementu Media.Image (Media.Image.close()).

Aplikacje mogą bezpośrednio korzystać z zakodowanego pliku Media.Image w ImageProxy. Nie wywoływaj tylko polecenia Media.Image.close() na opakowanym obrazie, ponieważ spowodowałoby to uszkodzenie mechanizmu udostępniania zdjęć w aplikacji CameraX. Zamiast tego użyj ImageProxy.close(), aby przekazać bazowy element Media.Image do Aparatu X.

Konfigurowanie analizatora na potrzeby analizy obrazów

Po utworzeniu analizatora zarejestruj go za pomocą ImageAnalysis.setAnalyzer(), aby rozpocząć analizę. Po zakończeniu analizy użyj ImageAnalysis.clearAnalyzer(), aby usunąć zarejestrowany analizator.

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

Powiąż analizę obrazów z cyklem życia

Zdecydowanie zalecamy powiązanie ImageAnalysis z istniejącym cyklem życia AndroidaX za pomocą funkcji ProcessCameraProvider.bindToLifecycle(). Pamiętaj, że funkcja bindToLifecycle() zwraca wybrane urządzenie Camera, które może służyć do dostrajania zaawansowanych ustawień, takich jak ekspozycja i inne. Więcej informacji o kontrolowaniu wyjścia kamery znajdziesz w tym przewodniku.

W poniższym przykładzie połączono wszystkie czynności z poprzednich kroków i powiązaliśmy przypadki użycia Aparatu X ImageAnalysis i Preview z właścicielem 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);

Dodatkowe materiały

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

Ćwiczenia z programowania

  • Pierwsze kroki z Aparatem X
  • Przykładowy kod

  • Przykładowe aplikacje CameraX