Guia de detecção de pontos de referência de poses para Android

A tarefa "MediaPipe Pose extraser" permite detectar pontos de referência de corpos humanos em uma imagem ou um vídeo. Você pode usar essa tarefa para identificar as principais localizações do corpo, analisar a postura e categorizar movimentos. Essa tarefa usa modelos de machine learning (ML) que funcionam com imagens ou vídeos únicos. A tarefa gera pontos de referência de poses corporais em coordenadas de imagem e em coordenadas mundiais tridimensionais.

O exemplo de código descrito nestas instruções está disponível no GitHub. Para mais informações sobre os recursos, modelos e opções de configuração dessa tarefa, consulte a Visão geral.

Exemplo de código

O código de exemplo do MediaPipe Tasks é uma implementação simples de um app "Pose stageer" para Android. No exemplo, a câmera de um dispositivo Android físico é usada para detectar poses em um stream de vídeo contínuo. O app também pode detectar poses em imagens e vídeos da galeria de dispositivos.

Você pode usar o app como ponto de partida para seu próprio app Android ou se referir a ele ao modificar um app já existente. O código de exemplo "Pose Stadiumer" está hospedado no GitHub (em inglês).

Fazer o download do código

As instruções a seguir mostram como criar uma cópia local do código de exemplo usando a ferramenta de linha de comando git.

Para fazer o download do código de exemplo:

  1. Clone o repositório git usando o seguinte comando:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. Como opção, configure sua instância git para usar a finalização da compra esparsa para que você tenha apenas os arquivos do app de exemplo Pose extras:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/pose_landmarker/android
    

Depois de criar uma versão local do código de exemplo, você poderá importar o projeto para o Android Studio e executar o app. Para conferir instruções, consulte o Guia de configuração para Android.

Principais componentes

Os arquivos a seguir contêm o código essencial para este aplicativo de exemplo de pontos de referência:

  • PoseLandmarkerHelper.kt: inicializa o ponto de referência de poses, processa o modelo e delega a seleção.
  • CameraFragment.kt: gerencia a câmera do dispositivo e processa os dados de entrada de imagem e vídeo.
  • GalleryFragment.kt: interage com OverlayView para mostrar a imagem ou o vídeo de saída.
  • OverlayView.kt: implementa a exibição das poses detectadas.

Configuração

Nesta seção, descrevemos as principais etapas para configurar seu ambiente de desenvolvimento e projetos de código para usar o Pose extras. Para ter informações gerais sobre como configurar seu ambiente de desenvolvimento para usar tarefas do MediaPipe, incluindo requisitos de versão da plataforma, consulte o Guia de configuração para Android.

Dependências

A tarefa "Posse de ponto de referência" usa a biblioteca com.google.mediapipe:tasks-vision. Adicione esta dependência ao arquivo build.gradle do seu app Android:

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

Modelo

A tarefa "MediaPipe Pose extras" requer um pacote de modelos treinados e compatível com ela. Para mais informações sobre os modelos treinados disponíveis para o "Pose extras", consulte a visão geral da tarefa Seção de modelos.

Selecione, faça o download do modelo e armazene-o no diretório do projeto:

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

Especifique o caminho do modelo no parâmetro ModelAssetPath. No código de exemplo, o modelo é definido no arquivo PoseLandmarkerHelper.kt:

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

Criar a tarefa

A tarefa MediaPipe Pose extras usa a função createFromOptions() para configurar a tarefa. A função createFromOptions() aceita valores para as opções de configuração. Para mais informações sobre as opções de configuração, consulte Opções de configuração.

O ponto de referência de postura é compatível com os seguintes tipos de dados de entrada: imagens estáticas, arquivos de vídeo e streams de vídeo ao vivo. Ao criar a tarefa, é necessário especificar o modo de execução correspondente ao tipo de dados de entrada. Escolha a guia do tipo de dados de entrada para saber como criar a tarefa.

Imagem

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)
    

Video

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)
    

Transmissão ao vivo

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)
    

A implementação de código de exemplo "Pose Highlighter" permite que o usuário alterne entre modos de processamento. A abordagem torna o código de criação da tarefa mais complicado e pode não ser apropriada para seu caso de uso. É possível conferir esse código na função setupPoseLandmarker() no arquivo PoseLandmarkerHelper.kt.

Opções de configuração

Esta tarefa tem as seguintes opções de configuração para apps Android:

Nome da opção Descrição Intervalo de valor Valor padrão
runningMode Define o modo de execução da tarefa. Há três modos:

IMAGE: o modo para entradas de imagem única.

VIDEO: o modo para frames decodificados de um vídeo.

LIVE_STREAM: é o modo para uma transmissão ao vivo de dados de entrada, como de uma câmera. Nesse modo, o resultListener precisa ser chamado para configurar um listener para receber resultados de forma assíncrona.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numposes O número máximo de poses que podem ser detectadas pelo ponto de referência da postura. Integer > 0 1
minPoseDetectionConfidence A pontuação de confiança mínima para que a detecção de poses seja considerada bem-sucedida. Float [0.0,1.0] 0.5
minPosePresenceConfidence A pontuação de confiança mínima da pontuação de presença de poses na detecção de pontos de referência de poses. Float [0.0,1.0] 0.5
minTrackingConfidence A pontuação de confiança mínima para que o rastreamento de poses seja considerado bem-sucedido. Float [0.0,1.0] 0.5
outputSegmentationMasks Define se o "Pose extras" gera uma máscara de segmentação para a pose detectada. Boolean False
resultListener Define o listener de resultado para receber os resultados do ponto de referência de forma assíncrona quando o elemento "Pose extras" está no modo de transmissão ao vivo. Só pode ser usado quando o modo de corrida está definido como LIVE_STREAM ResultListener N/A
errorListener Define um listener de erro opcional. ErrorListener N/A

preparar dados

O Pose Pointser funciona com imagens, arquivos de vídeo e streams de vídeo ao vivo. A tarefa lida com o pré-processamento da entrada de dados, incluindo redimensionamento, rotação e normalização de valores.

O código a seguir demonstra como transferir dados para processamento. Esses exemplos incluem detalhes sobre como lidar com dados de imagens, arquivos de vídeo e streams de vídeo ao vivo.

Imagem

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()
    

Video

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()
    

Transmissão ao vivo

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()
    

No código de exemplo do "Pose extras", a preparação de dados é processada no arquivo PoseLandmarkerHelper.kt.

Executar a tarefa

Dependendo do tipo de dados com que você está trabalhando, use o método poseLandmarker.detect...() específico para esse tipo de dados. Use detect() para imagens individuais, detectForVideo() para frames em arquivos de vídeo e detectAsync() para streams de vídeo. Ao realizar detecções em um stream de vídeo, execute-as em uma linha de execução separada para evitar o bloqueio dela.

Os exemplos de código a seguir mostram exemplos simples de como executar o "Pose stageer" nesses modos de dados diferentes:

Imagem

val result = poseLandmarker.detect(mpImage)
    

Video

val timestampMs = i * inferenceIntervalMs

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

Transmissão ao vivo

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

poseLandmarker.detectAsync(mpImage, frameTime)
    

Observe o seguinte:

  • Ao executar no modo de vídeo ou de transmissão ao vivo, é necessário fornecer o carimbo de data/hora do frame de entrada para a tarefa "Posse de ponto de referência".
  • Quando executada no modo de imagem ou vídeo, a tarefa "Pose extras" bloqueia a linha de execução atual até terminar de processar a imagem ou o frame de entrada. Para evitar o bloqueio da interposição do usuário, execute o processamento em uma linha de execução em segundo plano.
  • Quando executada no modo de transmissão ao vivo, a tarefa "Posse de ponto de referência" retorna imediatamente e não bloqueia a linha de execução atual. Ele vai invocar o listener de resultados com o resultado da detecção sempre que terminar de processar um frame de entrada.

No código de exemplo "Pose vCenter", as funções detect, detectForVideo e detectAsync são definidas no arquivo PoseLandmarkerHelper.kt.

Gerenciar e mostrar resultados

O ponto de referência de postura retorna um objeto poseLandmarkerResult para cada execução de detecção. O objeto de resultado contém coordenadas para cada ponto de referência de pose.

Veja a seguir um exemplo dos dados de saída dessa tarefa:

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)

A saída contém coordenadas normalizadas (Landmarks) e coordenadas mundiais (WorldLandmarks) para cada ponto de referência.

A saída contém as seguintes coordenadas normalizadas (Landmarks):

  • x e y: coordenadas de ponto de referência normalizadas entre 0,0 e 1,0 pela largura da imagem (x) e altura (y).

  • z: a profundidade do ponto de referência, com origem no ponto médio dos quadris. Quanto menor o valor, mais próximo o ponto de referência estará da câmera. A magnitude de z usa aproximadamente a mesma escala que x.

  • visibility: a probabilidade do ponto de referência ser visível na imagem.

A saída contém as seguintes coordenadas mundiais (WorldLandmarks):

  • x, y e z: coordenadas tridimensionais reais em metros, com o ponto médio dos quadris como origem.

  • visibility: a probabilidade do ponto de referência ser visível na imagem.

A imagem abaixo mostra uma visualização da saída da tarefa:

A máscara de segmentação opcional representa a probabilidade de cada pixel pertencer a uma pessoa detectada. A imagem a seguir é uma máscara de segmentação da saída da tarefa:

O código de exemplo "Pose Stadium" demonstra como mostrar os resultados retornados da tarefa. Consulte a classe OverlayView para saber mais.