Przewodnik po segmentacji obrazów na Androidzie

Zadanie MediaPipe Image Segmenter umożliwia dzielenie obrazów na regiony na podstawie zdefiniowanych wstępnie kategorii, aby stosować efekty wizualne, takie jak rozmycie tła. Z tych instrukcji dowiesz się, jak używać narzędzia do dzielenia obrazu w przypadku aplikacji na Androida. Przykład kodu opisany w tych instrukcjach jest dostępny na GitHub. Więcej informacji o możliwościach, modelach i opcjach konfiguracji związanych z tym zadaniem znajdziesz w sekcji Omówienie.

Przykładowy kod

Przykładowy kod MediaPipe Tasks zawiera 2 proste implementacje aplikacji Image Segmenter na Androida:

Przykłady wykorzystują kamerę na fizycznym urządzeniu z Androidem do segmentacji obrazu na podstawie strumienia z kamery na żywo. Możesz też wybrać obrazy i filmy z galerii urządzenia. Możesz użyć tych aplikacji jako punktu wyjścia do utworzenia własnej aplikacji na Androida lub skorzystać z nich podczas modyfikowania istniejącej aplikacji. Przykładowy kod Image Segmenter jest hostowany na GitHub.

Poniższe sekcje odnoszą się do aplikacji Image Segmenter z maską kategorii.

Pobieranie kodu

Z tych instrukcji dowiesz się, jak utworzyć lokalną kopię przykładowego kodu za pomocą narzędzia wiersza poleceń git.

Aby pobrać przykładowy kod:

  1. Sklonuj repozytorium Git za pomocą tego polecenia:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. Opcjonalnie skonfiguruj instancję git do korzystania z rzadkiego sprawdzania, aby mieć tylko pliki przykładowej aplikacji Image Segmenter:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_segmentation/android
    

Po utworzeniu lokalnej wersji przykładowego kodu możesz zaimportować projekt do Android Studio i uruchomić aplikację. Instrukcje znajdziesz w przewodniku konfiguracji Androida.

Kluczowe komponenty

Te pliki zawierają kluczowy kod przykładowej aplikacji do segmentacji obrazu:

  • ImageSegmenterHelper.kt – inicjuje zadanie segmentacji obrazu i obsługuje model oraz wybór delegata.
  • CameraFragment.kt – zawiera interfejs użytkownika i kod sterujący kamerą.
  • GalleryFragment.kt – zawiera interfejs użytkownika i kod sterujący do wybierania plików z obrazami i filmami.
  • OverlayView.kt – obsługuje i formatuje wyniki podziału na segmenty.

Konfiguracja

Ta sekcja zawiera opis kluczowych kroków konfiguracji środowiska programistycznego i projektów kodu, które umożliwiają korzystanie z narzędzia Image Segmenter. Ogólne informacje o konfigurowaniu środowiska programistycznego do korzystania z zadań MediaPipe, w tym wymagania dotyczące wersji platformy, znajdziesz w przewodniku konfiguracji na Androida.

Zależności

Narzędzie do podziału obrazu korzysta z biblioteki com.google.mediapipe:tasks-vision. Dodaj tę zależność do pliku build.gradle w projekcie tworzenia aplikacji na Androida. Zaimportuj wymagane zależności za pomocą tego kodu:

dependencies {
    ...
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

Model

Zadanie MediaPipe Image Segmenter wymaga wytrenowanego modelu zgodnego z tym zadaniem. Więcej informacji o dostępnych wytrenowanych modelach usługi Image Segmenter znajdziesz w sekcji Modele w omówieniu zadania.

Wybierz i pobierz model, a potem zapisz go w katalogu projektu:

<dev-project-root>/src/main/assets

Użyj metody BaseOptions.Builder.setModelAssetPath(), aby określić ścieżkę używaną przez model. Ta metoda jest wywoływana w przykładowym kodzie w następnej sekcji.

W przykładowym kodzie segmentacji obrazu model jest zdefiniowany w klasie ImageSegmenterHelper.kt w funkcji setupImageSegmenter().

Tworzenie zadania

Do utworzenia zadania możesz użyć funkcji createFromOptions. Funkcja createFromOptions akceptuje opcje konfiguracji, w tym typy danych wyjściowych maski. Więcej informacji o konfigurowaniu zadań znajdziesz w artykule Opcje konfiguracji.

Zadanie segmentacji obrazu obsługuje te typy danych wejściowych: obrazy, pliki wideo i strumienie wideo na żywo. Podczas tworzenia zadania musisz określić tryb wykonywania odpowiadający typowi danych wejściowych. Aby dowiedzieć się, jak utworzyć to zadanie, wybierz kartę odpowiadającą wybranemu typowi danych wejściowych.

Obraz

ImageSegmenterOptions options =
  ImageSegmenterOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.IMAGE)
    .setOutputCategoryMask(true)
    .setOutputConfidenceMasks(false)
    .build();
imagesegmenter = ImageSegmenter.createFromOptions(context, options);
    

Wideo

ImageSegmenterOptions options =
  ImageSegmenterOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.VIDEO)
    .setOutputCategoryMask(true)
    .setOutputConfidenceMasks(false)
    .build();
imagesegmenter = ImageSegmenter.createFromOptions(context, options);
    

Transmisja na żywo

ImageSegmenterOptions options =
  ImageSegmenterOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setOutputCategoryMask(true)
    .setOutputConfidenceMasks(false)
    .setResultListener((result, inputImage) -> {
         // Process the segmentation result here.
    })
    .setErrorListener((result, inputImage) -> {
         // Process the segmentation errors here.
    })
    .build()
imagesegmenter = ImageSegmenter.createFromOptions(context, options)
    

Implementacja przykładowego kodu segmentacji obrazu umożliwia użytkownikowi przełączanie się między trybami przetwarzania. Takie podejście skomplikuje kod tworzący zadanie i może nie być odpowiednie w Twoim przypadku. Ten kod możesz zobaczyć w klasie ImageSegmenterHelper w funkcji setupImageSegmenter().

Opcje konfiguracji

W tym zadaniu dostępne są te opcje konfiguracji aplikacji na Androida:

Nazwa opcji Opis Zakres wartości Wartość domyślna
runningMode Ustawia tryb działania zadania. Dostępne są 3 tryby:

OBRAZ: tryb dla pojedynczych obrazów wejściowych.

FILM: tryb dekodowanych klatek filmu.

LIVE_STREAM: tryb transmisji na żywo danych wejściowych, takich jak dane z kamery. W tym trybie należy wywołać metodę resultListener, aby skonfigurować odbiornik, który będzie asynchronicznie odbierał wyniki.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
outputCategoryMask Jeśli ustawisz tę opcję na True, dane wyjściowe będą zawierać maskę segmentacji w postaci obrazu uint8, w którym każda wartość piksela wskazuje zwycięską kategorię. {True, False} False
outputConfidenceMasks Jeśli ustawisz tę opcję na True, dane wyjściowe będą zawierać maskę segmentacji w postaci obrazu z wartością zmiennoprzecinkową, gdzie każda wartość zmiennoprzecinkowa reprezentuje mapę współczynnika zaufania danej kategorii. {True, False} True
displayNamesLocale Określa język etykiet, których należy używać do wyświetlanych nazw podanych w metadanych modelu zadania (jeśli są dostępne). Wartość domyślna to en w przypadku języka angielskiego. Możesz dodawać zlokalizowane etykiety do metadanych modelu niestandardowego, korzystając z interfejsu TensorFlow Lite Metadata Writer API. Kod języka en
resultListener Ustawia odbiornik wyników tak, aby asynchronicznie otrzymywał wyniki podziału na segmenty, gdy segmentator obrazu jest w trybie LIVE_STREAM. Można go używać tylko wtedy, gdy tryb działania ma wartość LIVE_STREAM. Nie dotyczy Nie dotyczy
errorListener Ustawia opcjonalny odbiornik błędów. Nie dotyczy Nie ustawiono

Przygotuj dane

Narzędzie do dzielenia obrazu działa z obrazami, plikami wideo i transmisjami na żywo. Zadanie to obsługuje wstępną obróbkę danych wejściowych, w tym zmianę rozmiaru, obrót i normalizację wartości.

Przed przekazaniem do segmentatora obrazu obraz wejściowy lub jego ramka musi zostać przekonwertowana na obiekt com.google.mediapipe.framework.image.MPImage.

Obraz

import com.google.mediapipe.framework.image.BitmapImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Load an image on the users device as a Bitmap object using BitmapFactory.

// Convert an Androids Bitmap object to a MediaPipes Image object.
Image mpImage = new BitmapImageBuilder(bitmap).build();
    

Wideo

import com.google.mediapipe.framework.image.BitmapImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Load a video file on the user's device using MediaMetadataRetriever

// From the videos metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT value. Youll need them
// to calculate the timestamp of each frame later.

// Loop through the video and load each frame as a Bitmap object.

// Convert the Androids Bitmap object to a MediaPipes Image object.
Image mpImage = new BitmapImageBuilder(frame).build();
    

Transmisja na żywo

import com.google.mediapipe.framework.image.MediaImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Create a CameraXs ImageAnalysis to continuously receive frames
// from the devices camera. Configure it to output frames in RGBA_8888
// format to match with what is required by the model.

// For each Androids ImageProxy object received from the ImageAnalysis,
// extract the encapsulated Androids Image object and convert it to
// a MediaPipes Image object.
android.media.Image mediaImage = imageProxy.getImage()
Image mpImage = new MediaImageBuilder(mediaImage).build();
    

W przykładowym kodzie Image Segmenter przygotowanie danych jest obsługiwane w klasie ImageSegmenterHelper za pomocą funkcji segmentLiveStreamFrame().

Uruchamianie zadania

W zależności od trybu działania wywoływana jest inna funkcja segment. Funkcja ImageSegmenter zwraca zidentyfikowane obszary segmentu w wejściowym obrazie lub klatce.

Obraz

ImageSegmenterResult segmenterResult = imagesegmenter.segment(image);
    

Wideo

// Calculate the timestamp in milliseconds of the current frame.
long frame_timestamp_ms = 1000 * video_duration * frame_index / frame_count;

// Run inference on the frame.
ImageSegmenterResult segmenterResult =
    imagesegmenter.segmentForVideo(image, frameTimestampMs);
    

Transmisja na żywo

// Run inference on the frame. The segmentations results will be available via
// the `resultListener` provided in the `ImageSegmenterOptions` when the image
// segmenter was created.
imagesegmenter.segmentAsync(image, frameTimestampMs);
    

Pamiętaj:

  • W trybie wideo lub strumienia na żywo musisz też podać zadaniu segmentacji obrazu sygnaturę czasową ramki wejściowej.
  • W trybie obrazu lub wideo zadanie segmentacji obrazu blokuje bieżący wątek, dopóki nie przetworzy wejściowego obrazu lub klatki. Aby uniknąć blokowania interfejsu użytkownika, przeprowadź przetwarzanie w wątku w tle.
  • W trybie transmisji na żywo zadanie ImageSegmenter nie blokuje bieżącego wątku, ale zwraca wynik natychmiast. Za każdym razem, gdy zakończy przetwarzanie ramki wejściowej, wywoła swojego słuchacza z wynikiem wykrywania. Jeśli funkcja segmentAsync zostanie wywołana, gdy zadanie segmentacji obrazu będzie przetwarzać inną klatkę, zadanie zignoruje nową klatkę wejściową.

W przykładowym kodzie Image Segmenter funkcje segment są zdefiniowane w pliku ImageSegmenterHelper.kt.

Obsługa i wyświetlanie wyników

Po przeprowadzeniu wnioskowania zadanie segmentacji obrazu zwraca obiekt ImageSegmenterResult, który zawiera wyniki zadania segmentacji. Treść danych wyjściowych zależy od wartości outputType ustawionej podczas konfigurowania zadania.

W następnych sekcjach znajdziesz przykłady danych wyjściowych z tego zadania:

Poziom ufności kategorii

Na poniższych obrazach widać wizualizację danych wyjściowych zadania dotyczącego maski ufności kategorii. Wyjście maski ufności zawiera wartości zmiennoprzecinkowe z zakresu [0, 1].

2 dziewczyny jeżdżą na koniu, a trzecia idzie obok Maska obrazu, która wyznacza kształt dziewczynek i konia z poprzedniego zdjęcia. Po lewej stronie konturu obrazu jest zarejestrowana, ale po prawej stronie nie

Wyjście maski oryginalnego obrazu i pewności kategorii. Obraz źródłowy z danych Pascal VOC 2012.

Wartość kategorii

Na poniższych obrazach widać wizualizację danych wyjściowych zadania w przypadku maski wartości kategorii. Zakres maski kategorii to [0, 255], a każda wartość piksela reprezentuje zwycięski indeks kategorii w wyniku modelu. Wybrany indeks kategorii ma najwyższą wartość spośród kategorii rozpoznawanych przez model.

2 dziewczyny jeżdżą na koniu, a trzecia idzie obok Maska obrazu, która wyznacza kształt dziewczynek i konia z poprzedniego obrazu. Kształty wszystkich trzech dziewczynek i konia są zasłonięte

Wyjście oryginalnej maski obrazu i kategorii. Obraz źródłowy z danych Pascal VOC 2012.