Przewodnik po klasyfikacji obrazów w Androidzie

Zadanie MediaPipe Image Classifier umożliwia klasyfikację obrazów. Możesz użyć tego zadania, aby określić, co obraz reprezentuje w zbiorze kategorii zdefiniowanych w czasie trenowania. Te instrukcje pokazują, jak używać klasyfikatora obrazów w aplikacjach na Androida. Przykładowy kod opisany w tych instrukcjach jest dostępny na GitHub.

Aby zobaczyć, jak to zadanie działa, możesz obejrzeć wersję demonstracyjną w przeglądarce. Więcej informacji o możliwościach, modelach i opcjach konfiguracji w tym zadaniu znajdziesz w sekcji Omówienie.

Przykładowy kod

Przykładowy kod MediaPipe Tasks to prosta implementacja aplikacji Image Classifier na Androida. Ten przykład korzysta z aparatu w fizycznym urządzeniu z Androidem, aby stale klasyfikować obiekty. Może też używać obrazów i filmów z galerii urządzenia do statycznego klasyfikowania obiektów.

Możesz użyć tej aplikacji jako punktu wyjścia dla własnej aplikacji na Androida lub odwołać się do niej podczas modyfikowania istniejącej aplikacji. Przykładowy kod klasyfikatora obrazów jest przechowywany na stronie GitHub.

Pobieranie kodu

Poniższe instrukcje pokazują, 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 możesz skonfigurować w instancji git opcję rozproszonego procesu płatności, tak aby mieć tylko pliki przykładowej aplikacji Image Classifier:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_classification/android
    

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

Kluczowe elementy

Poniższe pliki zawierają kluczowy kod tej przykładowej aplikacji do klasyfikacji obrazów:

Konfiguracja

W tej sekcji znajdziesz najważniejsze czynności, które musisz wykonać, aby skonfigurować środowisko programistyczne i projekty kodu pod kątem użycia klasyfikatora obrazów. Ogólne informacje o konfigurowaniu środowiska programistycznego na potrzeby zadań MediaPipe, w tym o wymaganiach dotyczących wersji platformy, znajdziesz w przewodniku po konfiguracji na Androida.

Zależności

Klasyfikator obrazów używa biblioteki com.google.mediapipe:tasks-vision. Dodaj tę zależność do pliku build.gradle projektu programistycznego 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 Classifier wymaga wytrenowanego modelu, który jest zgodny z tym zadaniem. Więcej informacji o dostępnych wytrenowanych modelach na potrzeby klasyfikatora obrazów znajdziesz w omówieniu zadania w sekcji Modele.

Wybierz i pobierz model, a następnie 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. O tej metodzie dowiesz się w przykładzie kodu w następnej sekcji.

W przykładowym kodzie klasyfikatora obrazów model jest zdefiniowany w pliku ImageClassifierHelper.kt.

Tworzenie zadania

Do utworzenia zadania możesz użyć funkcji createFromOptions. Funkcja createFromOptions akceptuje opcje konfiguracji, w tym tryb działania, język nazw wyświetlanych, maksymalną liczbę wyników, próg ufności oraz listę dozwolonych lub odrzuconych kategorii. Więcej informacji o opcjach konfiguracji znajdziesz w artykule Omówienie konfiguracji.

Zadanie Klasyfikator obrazów obsługuje 3 typy danych wejściowych: obrazy, pliki wideo i strumieni wideo na żywo. Podczas tworzenia zadania musisz określić tryb działania odpowiadający typowi danych wejściowych. Wybierz kartę odpowiadającą typowi danych wejściowych, aby zobaczyć, jak utworzyć zadanie i uruchomić wnioskowanie.

Obraz

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.IMAGE)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

Wideo

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.VIDEO)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

Transmisja na żywo

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setMaxResults(5)
    .setResultListener((result, inputImage) -> {
         // Process the classification result here.
    })
    .setErrorListener((result, inputImage) -> {
         // Process the classification errors here.
    })
    .build()
imageClassifier = ImageClassifier.createFromOptions(context, options)
    

Przykładowa implementacja kodu klasyfikatora obrazów pozwala użytkownikowi przełączać się między trybami przetwarzania. Takie podejście zwiększa złożoność kodu tworzenia zadań i może nie być odpowiedni w Twoim przypadku. Ten kod znajdziesz w funkcji setupImageClassifier() w pliku ImageClassifierHelper.kt.

Opcje konfiguracji

To zadanie zawiera 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:

IMAGE: ten tryb używany do przesyłania pojedynczego obrazu.

WIDEO: tryb zdekodowanych klatek filmu.

TRANSMISJA NA ŻYWO: tryb transmisji danych wejściowych, np. z kamery. W tym trybie musisz wywołać funkcję resultListener, aby skonfigurować detektor do asynchronicznego otrzymywania wyników.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
displayNamesLocale Określa język etykiet, które mają być używane w przypadku 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 dodać zlokalizowane etykiety do metadanych modelu niestandardowego za pomocą interfejsu TensorFlow Lite Metadata Writer API. Kod języka en
maxResults Określa opcjonalną maksymalną liczbę zwracanych wyników klasyfikacji z najlepszymi wynikami. Jeśli wartość jest mniejsza niż 0, zwracane są wszystkie dostępne wyniki. Dowolne liczby dodatnie -1
scoreThreshold Ustawia próg wyniku prognozy, który zastępuje próg podany w metadanych modelu (jeśli występuje). Wyniki poniżej tej wartości zostały odrzucone. Dowolna liczba zmiennoprzecinkowa Nie ustawiono
categoryAllowlist Ustawia opcjonalną listę dozwolonych nazw kategorii. Jeśli pole nie jest puste, wyniki klasyfikacji, których nazwy kategorii nie znajdują się w tym zbiorze, zostaną odfiltrowane. Zduplikowane lub nieznane nazwy kategorii są ignorowane. Ta opcja wzajemnie się wyklucza z metodą categoryDenylist i jej użycie powoduje błąd. Dowolne ciągi Nie ustawiono
categoryDenylist Ustawia opcjonalną listę nazw kategorii, które nie są dozwolone. Jeśli pole nie jest puste, wyniki klasyfikacji, których nazwa kategorii znajduje się w tym zbiorze, zostaną odfiltrowane. Zduplikowane lub nieznane nazwy kategorii są ignorowane. Ta opcja wyklucza się z metodą categoryAllowlist i jej użycie powoduje błąd. Dowolne ciągi Nie ustawiono
resultListener Konfiguruje detektor wyników tak, aby asynchronicznie otrzymywał wyniki klasyfikacji, gdy klasyfikator obrazów jest w trybie transmisji na żywo. Tej opcji można używać tylko wtedy, gdy tryb działania jest ustawiony na LIVE_STREAM Nie dotyczy Nie ustawiono
errorListener Ustawia opcjonalny detektor błędów. Nie dotyczy Nie ustawiono

Przygotuj dane

Klasyfikator obrazów działa z obrazami, plikami wideo i transmisjami wideo na żywo. Zadanie obejmuje wstępne przetwarzanie danych wejściowych, w tym zmianę rozmiaru, rotację i normalizację wartości.

Musisz przekonwertować obraz lub ramkę wejściową na obiekt com.google.mediapipe.framework.image.MPImage, zanim przekażesz go do klasyfikatora obrazów.

Obraz

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

// Load an image on the user’s device as a Bitmap object using BitmapFactory.

// Convert an Android’s Bitmap object to a MediaPipe’s 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 video’s metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT value. You’ll need them
// to calculate the timestamp of each frame later.

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

// Convert the Android’s Bitmap object to a MediaPipe’s 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 CameraX’s ImageAnalysis to continuously receive frames 
// from the device’s camera. Configure it to output frames in RGBA_8888
// format to match with what is required by the model.

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

W przykładowym kodzie klasyfikatora obrazów przygotowanie danych odbywa się w pliku ImageClassifierHelper.kt.

Uruchamianie zadania

Aby aktywować wnioskowanie, możesz wywołać funkcję classify odpowiadającą Twojemu trybowi działającemu. Interfejs Image Classifier API zwraca możliwe kategorie obiektu w obrazie lub ramce wejściowej.

Obraz

ImageClassifierResult classifierResult = imageClassifier.classify(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.
ImageClassifierResult classifierResult =
    imageClassifier.classifyForVideo(image, frameTimestampMs);
    

Transmisja na żywo


// Run inference on the frame. The classifications results will be available 
// via the `resultListener` provided in the `ImageClassifierOptions` when 
// the image classifier was created.
imageClassifier.classifyAsync(image, frameTimestampMs);
    

Uwaga:

  • Jeśli pracujesz w trybie wideo lub w trybie transmisji na żywo, musisz też podać do zadania Klasyfikator obrazów sygnaturę czasową klatki wejściowej.
  • W trybie obrazu lub wideo zadanie Klasyfikator obrazów blokuje bieżący wątek, dopóki nie zakończy przetwarzania obrazu lub klatki wejściowej. Aby uniknąć blokowania interfejsu użytkownika, wykonaj przetwarzanie w wątku w tle.
  • W trybie transmisji na żywo zadanie klasyfikacji obrazów nie blokuje bieżącego wątku, ale zostaje natychmiast przywrócone. Wywołuje swój detektor wyników z wynikiem wykrywania za każdym razem, gdy zakończy przetwarzanie ramki wejściowej. Jeśli funkcja classifyAsync jest wywoływana, gdy zadanie Klasyfikator obrazów jest zajęte przetwarzaniem kolejnej klatki, zadanie zignoruje nową ramkę wejściową.

W przykładowym kodzie klasyfikatora obrazów funkcje classify są zdefiniowane w pliku ImageClassifierHelper.kt.

Obsługa i wyświetlanie wyników

Po uruchomieniu wnioskowania zadanie Klasyfikator obrazów zwraca obiekt ImageClassifierResult zawierający listę możliwych kategorii obiektów w obrazie lub ramce wejściowej.

Poniżej znajdziesz przykładowe dane wyjściowe tego zadania:

ImageClassifierResult:
 Classifications #0 (single classification head):
  head index: 0
  category #0:
   category name: "/m/01bwb9"
   display name: "Passer domesticus"
   score: 0.91406
   index: 671
  category #1:
   category name: "/m/01bwbt"
   display name: "Passer montanus"
   score: 0.00391
   index: 670

Ten wynik uzyskano po uruchomieniu Bird Classifier na:

W przykładowym kodzie klasyfikatora obrazów klasa ClassificationResultsAdapter w pliku ClassificationResultsAdapter.kt obsługuje wyniki:

fun updateResults(imageClassifierResult: ImageClassifierResult? = null) {
    categories = MutableList(adapterSize) { null }
    if (imageClassifierResult != null) {
        val sortedCategories = imageClassifierResult.classificationResult()
            .classifications()[0].categories().sortedBy { it.index() }
        val min = kotlin.math.min(sortedCategories.size, categories.size)
        for (i in 0 until min) {
            categories[i] = sortedCategories[i]
        }
    }
}