Przewodnik wykrywania pozycji punktu orientacyjnego na Androidzie

Zadanie MediaPipe Pose pointer umożliwia wykrywanie na zdjęciach lub filmach punktów orientacyjnych ludzkich ciał. Możesz używać tego zadania do identyfikowania kluczowych lokalizacji ciała, analizowania postawy i kategoryzowania ruchów. To zadanie wykorzystuje modele systemów uczących się, które działają z pojedynczymi obrazami lub filmami. Zadanie wyprowadza punkty orientacyjne ustalania pozycji ciała we współrzędnych zdjęcia i w trójwymiarowych współrzędnych świata.

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 to prosta implementacja aplikacji Pose Kraina na Androida. W tym przykładzie używamy aparatu na fizycznym urządzeniu z Androidem, aby wykrywać pozycje w ciągłym strumieniu wideo. Może też wykrywać pozy na zdjęciach i filmach z galerii urządzenia.

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 Pose pointer 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 Pose pointer:
    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 do Android Studio i uruchomić aplikację. Instrukcje znajdziesz w przewodniku konfiguracji na Androida.

Kluczowe elementy

Te pliki zawierają kluczowy kod tej przykładowej aplikacji z elementami charakterystycznymi dla pozy:

  • PoseLandmarkerHelper.kt – inicjuje punkt orientacyjny pozycji i obsługuje model oraz wybór przedstawicieli.
  • CameraFragment.kt – obsługuje aparat urządzenia oraz przetwarza wejściowe dane obrazu i filmu.
  • GalleryFragment.kt – wchodzi w interakcję z elementem OverlayView, by wyświetlić obraz lub film wyjściowy.
  • OverlayView.kt – implementuje ekran w przypadku wykrytych pozycji.

Konfiguracja

W tej sekcji opisujemy najważniejsze czynności, jakie należy wykonać podczas konfigurowania środowiska programistycznego i projektów kodowania, tak aby można było używać aplikacji Pose pointer. 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 Tworzenie punktu orientacyjnego 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 Pose pointer wymaga wytrenowanego pakietu modeli, który jest z nim zgodny. Więcej informacji o dostępnych wytrenowanych modelach dla funkcji Punkt orientacyjny pozycji znajdziesz w opisie zadania 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 PoseLandmarkerHelper.kt:

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

Tworzenie zadania

Zadanie MediaPipe Pose w sam raz do skonfigurowania tego zadania używa funkcji createFromOptions(). Funkcja createFromOptions() akceptuje wartości opcji konfiguracji. Więcej informacji o opcjach konfiguracji znajdziesz w artykule Opcje konfiguracji.

Narzędzie do oznaczania pozycji obsługuje następujące typy danych wejściowych: obrazy, pliki wideo i strumienie wideo na żywo. Podczas tworzenia zadania musisz określić tryb działania odpowiadającym typowi danych wejściowych. Wybierz kartę z typem 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)
    

Implementacja kodu przykładowej Pose Kraina umożliwia użytkownikowi przełączanie się między trybami 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 setupPoseLandmarker() w pliku PoseLandmarkerHelper.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
numposes Maksymalna liczba pozycji, które może wykryć Punkt orientacyjny pozycji. Integer > 0 1
minPoseDetectionConfidence Minimalny poziom ufności, że wykrywanie pozycji zostanie uznane za udane. Float [0.0,1.0] 0.5
minPosePresenceConfidence Minimalny wskaźnik ufności dotyczący obecności pozycji podczas wykrywania punktu orientacyjnego pozycji. Float [0.0,1.0] 0.5
minTrackingConfidence Minimalny wskaźnik ufności pozwalający na uznanie śledzenia pozycji za udane. Float [0.0,1.0] 0.5
outputSegmentationMasks Określa, czy Kreator elementów budowy pozycji generuje maskę podziału na segmenty dla wykrytej pozycji. Boolean False
resultListener Ustawia detektor wyników tak, aby asynchronicznie otrzymywał wyniki dotyczące punktów orientacyjnych, gdy kreator pozycji jest w trybie transmisji na żywo. Tego ustawienia można używać tylko wtedy, gdy tryb biegowy jest ustawiony na LIVE_STREAM ResultListener N/A
errorListener Ustawia opcjonalny detektor błędów. ErrorListener N/A

Przygotuj dane

Narzędzie do oznaczania pozycji obsługuje obrazy, pliki wideo i 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 na temat 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 Pose pointer przygotowanie danych odbywa się w pliku PoseLandmarkerHelper.kt.

Uruchamianie zadania

W zależności od typu danych, z którymi pracujesz, użyj metody poseLandmarker.detect...() odpowiedniej dla tego typu danych. Użyj detect() w przypadku pojedynczych obrazów, detectForVideo() – klatek w plikach wideo, a detectAsync() w przypadku strumieni wideo. Gdy wykrywasz treści w strumieniu wideo, pamiętaj o uruchomieniu wykrywania w osobnym wątku, aby uniknąć zablokowania takiego wątku.

Poniższe przykłady kodu pokazują proste przykłady uruchamiania punktu orientacyjnego Pose 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)
    

Uwaga:

  • Gdy korzystasz z trybu wideo lub transmisji na żywo, musisz w zadaniu „punkt orientacyjny pozycji” podać sygnaturę czasową klatki wejściowej.
  • W trybie graficznym lub wideo zadanie Punkt orientacyjny do pozycjonowania blokuje bieżący wątek do momentu zakończenia przetwarzania obrazu lub klatki wejściowej. Aby uniknąć blokowania interwencji użytkownika, przetwarzaj dane w wątku w tle.
  • W trybie transmisji na żywo zadanie Punkt orientacyjny pozycji wraca natychmiast i nie blokuje bieżącego wątku. Wywoła on detektor wyników z wynikiem wykrywania za każdym razem, gdy zakończy przetwarzanie ramki wejściowej.

W przykładowym kodzie Pose Symbol Oznacz funkcje detect, detectForVideo i detectAsync są zdefiniowane w pliku PoseLandmarkerHelper.kt.

Obsługa i wyświetlanie wyników

Punkt orientacyjny pozycji zwraca obiekt poseLandmarkerResult przy każdym uruchomieniu wykrywania. Wynikowy obiekt zawiera współrzędne każdego punktu orientacyjnego umiejscowienia.

Poniżej znajdziesz przykład danych wyjściowych z 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)

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

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

  • x i y: współrzędne punktu orientacyjnego znormalizowane od 0,0 do 1,0 według szerokości obrazu (x) i wysokości (y).

  • z: głębokość punktu orientacyjnego, której początek znajduje się w połowie bioder. Im mniejsza wartość, tym bliżej kamery znajduje się punkt orientacyjny. Wielkość „z” jest mniej więcej taka sama jak w przypadku właściwości x.

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

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

  • x, y i z: rzeczywiste trójwymiarowe współrzędne w metrach, gdzie punktem początkowym jest środek bioder.

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

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

Opcjonalna maska podziału na segmenty reprezentuje prawdopodobieństwo, że każdy piksel należy do wykrytej osoby. Ten obraz przedstawia maskę podziału danych wyjściowych zadania:

Przykładowy kod Znacznika elementu położenia pokazuje, jak wyświetlać wyniki zwrócone z zadania. Więcej informacji znajdziesz w klasie OverlayView.