Przewodnik dotyczący wykrywania punktów orientacyjnych dłoni na Androidzie

Zadanie MediaPipe Hand Pointer umożliwia wykrywanie punktów orientacyjnych dłoni na zdjęciu. Te instrukcje pokazują, jak używać Podręcznego punktu orientacyjnego z aplikacjami na Androida. Przykładowy kod opisany w tych instrukcjach jest dostępny na GitHub.

Więcej informacji o możliwościach, modelach i opcjach konfiguracji tego zadania znajdziesz w sekcji Omówienie.

Przykładowy kod

Przykładowy kod MediaPipe Tasks jest prostą implementacją aplikacji wskaźniki rejonu dłoni na Androida. W tym przykładzie korzystamy z aparatu w fizycznym urządzeniu z Androidem, aby stale wykrywać punkty orientacyjne dłoni, a także wykorzystywać obrazy i filmy z galerii urządzenia do statycznego wykrywania punktów orientacyjnych.

Możesz użyć 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 obszaru orientacyjnego jest hostowany na GitHub.

Pobieranie kodu

Z instrukcji poniżej 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 tak, aby używała rozproszonego procesu płatności, aby mieć tylko pliki dla przykładowej aplikacji Notatnik ręki:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/hand_landmarker/android
    

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

Kluczowe elementy

Te pliki zawierają najważniejszy kod tej przykładowej aplikacji do wykrywania punktów orientacyjnych:

  • HandLandmarkerHelper.kt – inicjuje detektor punktu orientacyjnego z dłonią i obsługuje model oraz wybór przedstawicieli.
  • MainActivity.kt – implementuje aplikację, umożliwiając wywołanie metody HandLandmarkerHelper.

Konfiguracja

W tej sekcji opisujemy najważniejsze czynności, jakie należy wykonać, aby skonfigurować środowisko programistyczne i projekty kodu tak, aby używały tego narzędzia. Ogólne informacje o konfigurowaniu środowiska programistycznego na potrzeby zadań MediaPipe, w tym o wymaganiach dotyczących wersji platformy, znajdziesz w przewodniku konfiguracji na Androida.

Zależności

Zadanie Zakreślacz ręki korzysta z biblioteki com.google.mediapipe:tasks-vision. Dodaj tę zależność do pliku build.gradle aplikacji na Androida:

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

Model

Zadanie MediaPipe Hand Pointer wymaga wytrenowanego pakietu modeli, który jest zgodny z tym zadaniem. Więcej informacji o dostępnych wytrenowanych modelach dla Wskaźnika ręcznego znajdziesz w omówieniu zadań w sekcji „Modele”.

Wybierz i pobierz model, a następnie zapisz go w katalogu projektu:

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

Podaj ścieżkę modelu w parametrze ModelAssetPath. W przykładowym kodzie model jest zdefiniowany w pliku HandLandmarkerHelper.kt:

baseOptionBuilder.setModelAssetPath(MP_HAND_LANDMARKER_TASK)

Tworzenie zadania

Zadanie tworzenia punktu orientacyjnego MediaPipe ręcznie konfiguruje to zadanie za pomocą funkcji createFromOptions(). Funkcja createFromOptions() akceptuje wartości opcji konfiguracji. Więcej informacji o opcjach konfiguracji znajdziesz w artykule Opcje konfiguracji.

To narzędzie obsługuje 3 typy danych wejściowych: obrazy, pliki wideo i transmisje 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

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

Wideo

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

Transmisja na żywo

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

Implementacja przykładowego kodu Hand Pointer umożliwia użytkownikowi przełączanie trybów przetwarzania. Takie podejście zwiększa złożoność kodu tworzenia zadania i może nie być odpowiednie w Twoim przypadku. Ten kod znajdziesz w funkcji setupHandLandmarker() w pliku HandLandmarkerHelper.kt.

Opcje konfiguracji

To zadanie ma te opcje konfiguracji aplikacji na Androida:

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

IMAGE: tryb wprowadzania pojedynczych obrazów.

WIDEO: tryb dekodowanych klatek filmu.

TRANSMISJA NA ŻYWO: tryb transmisji danych wejściowych na żywo, np. z kamery. W tym trybie należy wywołać metodę resultListener, aby skonfigurować odbiornik, który będzie odbierał wyniki asynchronicznie.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands Maksymalna liczba rąk wykrytych przez detektor punktów orientacyjnych dłoni. Any integer > 0 1
minHandDetectionConfidence Minimalny stopień pewności, że wykrywanie dłoni zostanie uznane za udane w modelu wykrywania dłoni. 0.0 - 1.0 0.5
minHandPresenceConfidence Minimalny wskaźnik ufności dla wyniku obecności ręki w modelu wykrywania punktów orientacyjnych ręki. W trybie wideo i w trybie transmisji na żywo, jeśli wskaźnik ufności obecności ręki dla modelu punktu orientacyjnego dłoni jest poniżej tego progu, komponent wskazuje rękę ręcznie uruchamia model wykrywania dłoni. W przeciwnym razie do wykrywania punktów orientacyjnych potrzebny jest uproszczony algorytm śledzenia dłoni. 0.0 - 1.0 0.5
minTrackingConfidence Minimalny wynik pewności, że śledzenie dłoni zostanie uznane za udane. Jest to próg interfejsu użytkownika w ramce ograniczającej między rękami w bieżącej a ostatniej klatce. W trybie wideo i trybie strumienia Wskaźnika ręcznego, jeśli śledzenie nie powiedzie się, wskazujesz rękę ręcznie. W przeciwnym razie pomijane jest wykrywanie rąk. 0.0 - 1.0 0.5
resultListener Konfiguruje detektor wyników, aby asynchronicznie otrzymywać wyniki wykrywania, gdy punkt orientacyjny z ręczną ręką działa w trybie transmisji na żywo. Ma zastosowanie tylko wtedy, gdy tryb działania jest ustawiony na LIVE_STREAM Nie dotyczy Nie dotyczy
errorListener Ustawia opcjonalny detektor błędów. Nie dotyczy Nie dotyczy

Przygotuj dane

Aplikacja Hand Pointer obsługuje zdjęcia, pliki wideo oraz transmisje wideo na żywo. To zadanie obsługuje wstępne przetwarzanie danych wejściowych, w tym zmianę rozmiaru, rotację i normalizację wartości.

Poniższy kod pokazuje, jak przekazywać dane do przetwarzania. Obejmują one szczegółowe informacje o sposobie postępowania z danymi z obrazów, plików wideo i strumieni wideo na żywo.

Obraz

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

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(image).build()
    

Wideo

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

val argb8888Frame =
    if (frame.config == Bitmap.Config.ARGB_8888) frame
    else frame.copy(Bitmap.Config.ARGB_8888, false)

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(argb8888Frame).build()
    

Transmisja na żywo

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

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(rotatedBitmap).build()
    

W przykładowym kodzie Ręczne narzędzie przygotowanie danych odbywa się w pliku HandLandmarkerHelper.kt.

Uruchamianie zadania

W zależności od typu danych, z którymi pracujesz, użyj metody HandLandmarker.detect...() odpowiedniej dla tego typu danych. Użyj detect() w przypadku pojedynczych obrazów, detectForVideo() – klatek w plikach wideo i detectAsync() w przypadku strumieni wideo. Podczas wykrywania strumienia wideo pamiętaj o uruchamianiu wykrywania w osobnym wątku, aby uniknąć zablokowania wątku interfejsu.

Poniższe przykłady kodu pokazują proste przykłady uruchamiania punktu orientacyjnego ręcznego w różnych trybach danych:

Obraz

val result = handLandmarker?.detect(mpImage)
    

Wideo

val timestampMs = i * inferenceIntervalMs

handLandmarker?.detectForVideo(mpImage, timestampMs)
    ?.let { detectionResult ->
        resultList.add(detectionResult)
    }
    

Transmisja na żywo

val mpImage = BitmapImageBuilder(rotatedBitmap).build()
val frameTime = SystemClock.uptimeMillis()

handLandmarker?.detectAsync(mpImage, frameTime)
    

Uwaga:

  • W trybie wideo lub w trybie transmisji na żywo musisz też podać znacznik czasu ramki wejściowej w zadaniu Zakreślacz ręki.
  • W trybie graficznym lub wideo zadanie Wskażnik dłoni będzie blokować bieżący wątek do momentu zakończenia przetwarzania obrazu wejściowego lub klatki. Aby uniknąć zablokowania interfejsu, przetwarzanie odbywa się w wątku w tle.
  • W trybie transmisji na żywo zadanie Oznaczenie ręki nie blokuje bieżącego wątku, ale wraca natychmiast. Wywołuje on detektor wyników z wynikiem wykrywania za każdym razem, gdy zakończy przetwarzanie ramki wejściowej. Jeśli funkcja wykrywania zostanie wywołana, gdy zadanie Mapa witryny jest zajęte przetwarzaniem innej klatki, zignoruje nową ramkę wejściową.

W przykładowym kodzie funkcji Zielone punkty orientacyjne funkcje detect, detectForVideo i detectAsync są zdefiniowane w pliku HandLandmarkerHelper.kt.

Obsługa i wyświetlanie wyników

Narzędzie do tworzenia punktów orientacyjnych ręcznie generuje obiekt wynikowy wskaźniku ręcznego dla każdego uruchomienia wykrywania. Otrzymany obiekt zawiera punkty orientacyjne dłoni we współrzędnych zdjęcia, punkty orientacyjne we współrzędnych świata i ręczność(lewa/prawa ręka) wykrytych dłoni.

Poniżej znajdziesz przykład danych wyjściowych z tego zadania:

Dane wyjściowe HandLandmarkerResult zawierają 3 komponenty. Każdy komponent jest tablicą, w której każdy element zawiera te wyniki dotyczące pojedynczej wykrytej dłoni:

  • Ręka dominująca

    Ręka wskazuje, czy wykryte ręce są lewą czy prawą ręką.

  • Punkty orientacyjne

    Jest 21 punktów orientacyjnych wskazujących dłonie, a każdy z nich składa się ze współrzędnych x, y i z. Współrzędne x i y są normalizowane do wartości [0,0, 1,0] odpowiednio do szerokości i wysokości obrazu. Współrzędna z reprezentuje głębokość punktu orientacyjnego, przy czym głębokość na nadgarstku jest punktem początkowym. Im mniejsza wartość, tym zbliża się punkt orientacyjny do aparatu. Siła działania z jest mniej więcej zbliżona do skali x.

  • Punkty orientacyjne na świecie

    We współrzędnych świata są również przedstawione 21 punktów orientacyjnych. Każdy punkt orientacyjny składa się z elementów x, y i z, które reprezentują rzeczywiste współrzędne 3D w metrach z punktem początkowym w środku geometrycznym dłoni.

HandLandmarkerResult:
  Handedness:
    Categories #0:
      index        : 0
      score        : 0.98396
      categoryName : Left
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : -3.41E-7
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
    ... (21 landmarks for a hand)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
    ... (21 world landmarks for a hand)

Poniższy obraz przedstawia wizualizację wyników zadania:

Przykładowy kod modułu ręki wskazuje, jak wyświetlać wyniki zwrócone z zadania. Więcej informacji znajdziesz w klasie OverlayView.