Przewodnik wykrywania pozycji punktu orientacyjnego na Androidzie

Zadanie MediaPipe Pose Markuper umożliwia wykrywanie punktów orientacyjnych ludzkich ciał na zdjęciach lub film. Możesz użyć tego zadania, aby zidentyfikować kluczowe lokalizacje ciała, przeanalizować postawę i kategoryzować ruchy. W tym zadaniu używane są modele systemów uczących się, które z pojedynczymi obrazami lub filmami. Zadanie generuje na obrazie punkty orientacyjne w pozycji ciała i trójwymiarowych współrzędnych świata.

Przykładowy kod opisany w tych instrukcjach jest dostępny na GitHub Więcej informacji o funkcjach, modelach i opcjach konfiguracji zapoznaj się z Przeglądem.

Przykładowy kod

Przykładowy kod MediaPipe Tasks to prosta implementacja punktu orientacyjnego umiejscowienia na Androida. W tym przykładzie użyliśmy aparatu w fizycznym urządzeniu z Androidem, Wykrywaj pozy w ciągłym strumieniu wideo. Aplikacja może też wykrywać pozy w obrazów i filmów z galerii urządzenia.

Możesz użyć tej aplikacji jako punktu wyjścia dla własnej aplikacji na Androida lub odnieść się do niej podczas modyfikowania istniejącej aplikacji. Przykładowy kod punktu orientacyjnego położenia jest hostowany GitHub

Pobieranie kodu

Poniżej znajdziesz instrukcje tworzenia lokalnej kopii przykładu. 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ć instancję git tak, aby wykorzystywała rozproszony proces płatności, aby tylko pliki przykładowej aplikacji Pose Notebooker:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/pose_landmarker/android
    

Po utworzeniu lokalnej wersji przykładowego kodu możesz zaimportować projekt w Android Studio i uruchom aplikację. Odpowiednie instrukcje znajdziesz w Przewodniku po konfiguracji na urządzeniu z Androidem.

Kluczowe elementy

Te pliki zawierają kluczowy kod tego przykładowego punktu orientacyjnego położenia aplikacja:

  • PoseLandmarkerHelper.kt – inicjuje punkt orientacyjny ułożenia oraz obsługuje model i przekazuje do niego dostęp. wyboru.
  • CameraFragment.kt – obsługuje aparat urządzenia i przetwarza dane wejściowe obrazu i filmu.
  • GalleryFragment.kt – wchodzi w interakcję z parametrem OverlayView, aby wyświetlić obraz wyjściowy lub film.
  • OverlayView.kt – implementuje wyświetlanie obrazu wykrytego umiejscowienia.

Konfiguracja

W tej sekcji opisujemy najważniejsze czynności związane z konfigurowaniem środowiska programistycznego oraz w projektach kodu do użycia punktów orientacyjnych. Ogólne informacje na temat: skonfigurować środowisko programistyczne do korzystania z zadań MediaPipe, w tym wymagań wersji platformy znajdziesz w przewodniku konfiguracji dla na urządzeniu z Androidem.

Zależności

Zadanie wyznaczania pozycji korzysta z biblioteki com.google.mediapipe:tasks-vision. Dodaj tę zależność od pliku build.gradle aplikacji na Androida:

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

Model

Zadanie MediaPipe Pose Markuper wymaga wytrenowanego pakietu modeli zgodnego z w tym zadaniu. Aby uzyskać więcej informacji o dostępnych wytrenowanych modelach z punktu wyznaczania pozycji, zapoznaj się z omówieniem zadania sekcją Modele.

Wybierz i pobierz model oraz zapisz go w katalogu projektu:

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

Określ ścieżkę modelu w parametrze ModelAssetPath. W przykładowy kod, model jest zdefiniowany w PoseLandmarkerHelper.kt plik:

val modelName = "pose_landmarker_lite.task"
baseOptionsBuilder.setModelAssetPath(modelName)

Tworzenie zadania

Zadanie MediaPipe Pose Notebooker używa funkcji createFromOptions() do skonfigurowania zadanie. Funkcja createFromOptions() akceptuje wartości konfiguracji . Więcej informacji o opcjach konfiguracji znajdziesz w sekcji Konfiguracja .

Punkt orientacyjny pozycji obsługuje następujące typy danych wejściowych: zdjęcia, filmy pliki i transmisje wideo na żywo. Musisz określić tryb działania odpowiadający typowi danych wejściowych podczas tworzenia zadania. Wybierz kartę dla typu danych wejściowych, aby zobaczyć, jak utworzyć zadanie.

Obraz

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

val optionsBuilder = 
    poseLandmarker.poseLandmarkerOptions.builder()
        .setBaseOptions(baseOptionsBuilder.build())
        .setMinPoseDetectionConfidence(minPoseDetectionConfidence)
        .setMinTrackingConfidence(minPoseTrackingConfidence)
        .setMinPosePresenceConfidence(minposePresenceConfidence)
        .setNumPoses(maxNumPoses)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()
poseLandmarker = poseLandmarker.createFromOptions(context, options)
    

Wideo

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

val optionsBuilder = 
    poseLandmarker.poseLandmarkerOptions.builder()
        .setBaseOptions(baseOptionsBuilder.build())
        .setMinPoseDetectionConfidence(minPoseDetectionConfidence)
        .setMinTrackingConfidence(minPoseTrackingConfidence)
        .setMinPosePresenceConfidence(minposePresenceConfidence)
        .setNumPoses(maxNumPoses)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()
poseLandmarker = poseLandmarker.createFromOptions(context, options)
    

Transmisja na żywo

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

val optionsBuilder = 
    poseLandmarker.poseLandmarkerOptions.builder()
        .setBaseOptions(baseOptionsBuilder.build())
        .setMinPoseDetectionConfidence(minPoseDetectionConfidence)
        .setMinTrackingConfidence(minPoseTrackingConfidence)
        .setMinPosePresenceConfidence(minposePresenceConfidence)
        .setNumPoses(maxNumPoses)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.LIVE_STREAM)

val options = optionsBuilder.build()
poseLandmarker = poseLandmarker.createFromOptions(context, options)
    

Przykładowa implementacja kodu punktu orientacyjnego Pose Notebooker pozwala użytkownikowi przełączać się między i przetwarzania danych. Takie podejście sprawia, że kod tworzenia zadań jest bardziej skomplikowany, może nie być odpowiednia w Twoim przypadku użycia. Możesz zobaczyć ten kod w setupPoseLandmarker() w PoseLandmarkerHelper.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. Są trzy tryby:

IMAGE: tryb wprowadzania pojedynczego obrazu.

WIDEO: tryb zdekodowanych klatek filmu.

LIVE_STREAM: tryb transmisji danych wejściowych na przykład z kamery. W tym trybie detektor wyników musi mieć wartość wywołano, aby skonfigurować detektor i otrzymywać wyniki asynchronicznie.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numposes Maksymalna liczba pozycji, które może wykryć Punkt orientacyjny. Integer > 0 1
minPoseDetectionConfidence Minimalny wskaźnik ufności, z którego ustala się pozycję została uznana za udaną. Float [0.0,1.0] 0.5
minPosePresenceConfidence Minimalny wskaźnik ufności obecności pozycji podczas wykrywania punktów orientacyjnych. Float [0.0,1.0] 0.5
minTrackingConfidence Minimalny wynik ufności dla śledzenia pozycji można uznać za udany. Float [0.0,1.0] 0.5
outputSegmentationMasks Określa, czy punkt orientacyjny położenia wyświetla maskę segmentacji dla wykrytego w pozycji. Boolean False
resultListener Konfiguruje detektor wyników tak, aby otrzymywał wyniki z punktu orientacyjnego asynchronicznie wtedy, gdy Pose Notebooker jest w trybie transmisji na żywo. Tej opcji można używać tylko wtedy, gdy tryb działania jest ustawiony na LIVE_STREAM ResultListener N/A
errorListener Ustawia opcjonalny detektor błędów. ErrorListener N/A

Przygotuj dane

Pose Pointer obsługuje zdjęcia, pliki wideo i strumieni wideo na żywo. Zadanie obsługuje wstępne przetwarzanie danych wejściowych, w tym zmianę rozmiaru, obrót i wartość ich normalizację.

Ten kod pokazuje, jak przekazywać dane do przetworzenia. Te przykłady zawierają szczegółowe instrukcje postępowania z danymi z obrazów, plików wideo i transmisji na żywo strumienie wideo.

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 położenia punktu orientacyjnego przygotowywanie danych odbywa się w instrukcji PoseLandmarkerHelper.kt .

Uruchamianie zadania

W zależności od typu danych, z którymi pracujesz, skorzystaj z poseLandmarker.detect...(), która jest specyficzna dla tego typu danych. Używaj detect() – pojedyncze zdjęcia, detectForVideo() – klatki w plikach wideo, i detectAsync() w przypadku strumieni wideo. Jeśli wykonujesz wykrywanie na wideo, pamiętaj, aby uruchomić wykrywanie w oddzielnym wątku, aby uniknąć blokując wątek interpunkcji przez użytkownika.

Poniższe przykładowe fragmenty kodu pokazują proste przykłady sposobu uruchamiania punktu orientacyjnego położenia w różnych trybach danych:

Obraz

val result = poseLandmarker.detect(mpImage)
    

Wideo

val timestampMs = i * inferenceIntervalMs

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

Transmisja na żywo

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

poseLandmarker.detectAsync(mpImage, frameTime)
    

Pamiętaj:

  • Korzystając z trybu wideo lub transmisji na żywo, musisz podać: sygnatura czasowa klatki wejściowej do zadania Pose Pointer.
  • Podczas pracy w trybie obrazu lub wideo zadanie Pose Markuper blokuje w bieżącym wątku aż do zakończenia przetwarzania obrazu lub ramki wejściowej. Do unikaj blokowania interpunkcji przez użytkownika, wykonywanie przetwarzania w tle w wątku.
  • W trybie transmisji na żywo zadanie Pose Latitudeer powraca od razu i nie zablokuje bieżącego wątku. Wywoła wynik detektor z wynikiem wykrywania po każdym zakończeniu przetwarzania ramki wejściowej.

W przykładowym kodzie punktu orientacyjnego Pozycja: detect, detectForVideo i Funkcje detectAsync są zdefiniowane w sekcji PoseLandmarkerHelper.kt .

Obsługa i wyświetlanie wyników

Punkt orientacyjny pozycji zwraca obiekt poseLandmarkerResult przy każdym wykryciu wykrycia bieganie. Obiekt wynikowy zawiera współrzędne każdego punktu orientacyjnego pozycji.

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

PoseLandmarkerResult:
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : 0.129959
      visibility   : 0.9999997615814209
      presence     : 0.9999984502792358
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
      visibility   : 0.999909
      presence     : 0.999958
    ... (33 landmarks per pose)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
      visibility   : 0.9999997615814209
      presence     : 0.9999984502792358
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
      visibility   : 0.999976
      presence     : 0.999998
    ... (33 world landmarks per pose)
  SegmentationMasks:
    ... (pictured below)

Dane wyjściowe zawierają zarówno znormalizowane współrzędne (Landmarks), jak i świat (WorldLandmarks) każdego punktu orientacyjnego.

Dane wyjściowe zawierają następujące znormalizowane współrzędne (Landmarks):

  • x i y: współrzędne punktu orientacyjnego znormalizowane między 0,0 a 1,0 przez szerokość (x) i wysokość (y) obrazu.

  • z: głębokość punktu orientacyjnego, której głębia znajduje się w połowie bioder pochodzeniu danych. Im mniejsza wartość, tym punkt orientacyjny jest bliżej kamery. magnituda wielkości z używa mniej więcej takiej samej skali jak funkcja x.

  • visibility: prawdopodobieństwo, że punkt orientacyjny jest widoczny na zdjęciu.

Dane wyjściowe zawierają następujące współrzędne świata (WorldLandmarks):

  • x, y i z: rzeczywiste, trójwymiarowe współrzędne w metrach, punktu środkowego bioder jako punktu początkowego.

  • visibility: prawdopodobieństwo, że punkt orientacyjny jest widoczny na zdjęciu.

Ten obraz przedstawia wizualizację danych wyjściowych zadania:

Opcjonalna maska segmentacji odpowiada prawdopodobieństwu przynależności każdego piksela wykrytej osobie. Na tej ilustracji widać maskę segmentacji wynik zadania:

Przykładowy kod elementu orientacyjnego pozycji pokazuje, jak wyświetlić zwrócone wyniki zobaczysz OverlayView klasy, by dowiedzieć się więcej.