Guia de detecção de objetos para Android

A tarefa Detector de objetos permite detectar a presença e o local de várias classes de objetos. Por exemplo, um detector de objetos pode localizar cães em uma imagem. Estas instruções mostram como usar a tarefa do detector de objetos no Android. O exemplo de código descrito nestas instruções está disponível no GitHub. Confira essa tarefa em ação nesta demonstração na Web. 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 do Object Detector para Android. O exemplo usa a câmera de um dispositivo Android físico para detectar objetos continuamente, podendo também usar imagens e vídeos da galeria do dispositivo para detectar objetos estaticamente.

Use o app como ponto de partida para seu próprio app Android ou consulte-o ao modificar um app atual. O código de exemplo do Detector de objetos 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 do git para usar o checkout esparso, para que você tenha apenas os arquivos do app de exemplo do Detector de objetos:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/object_detection/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 o aplicativo de exemplo do Detector de objetos:

Configuração

Nesta seção, descrevemos as principais etapas para configurar seu ambiente de desenvolvimento e projetos de código para usar o Object Detector. 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

O detector de objetos usa a biblioteca com.google.mediapipe:tasks-vision. Adicione essa dependência ao arquivo build.gradle do projeto de desenvolvimento de apps Android. Importe as dependências necessárias com o seguinte código:

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

Modelo

A tarefa do Detector de objetos do MediaPipe requer um modelo treinado compatível com ela. Para mais informações sobre os modelos treinados disponíveis para o Object Detector, consulte a seção Modelos de visão geral da tarefa.

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

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

Use o método BaseOptions.Builder.setModelAssetPath() para especificar o caminho usado pelo modelo. Confira um exemplo de código na próxima seção.

Criar a tarefa

Use a função createFromOptions para criar a tarefa. A função createFromOptions aceita opções de configuração, incluindo o modo de execução, a localidade dos nomes de exibição, o número máximo de resultados, o limite de confiança, a lista de permissões da categoria e a lista de bloqueio. Se nenhuma opção de configuração for especificada, o valor padrão será usado. Para mais informações sobre as opções de configuração, consulte Visão geral da configuração.

A tarefa Detector de objetos oferece suporte a três 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 correspondente ao tipo de dados de entrada para conferir como criar a tarefa e executar a inferência.

Imagem

ObjectDetectorOptions options =
  ObjectDetectorOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath(‘model.tflite’).build())
    .setRunningMode(RunningMode.IMAGE)
    .setMaxResults(5)
    .build();
objectDetector = ObjectDetector.createFromOptions(context, options);
    

Video

ObjectDetectorOptions options =
  ObjectDetectorOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath(‘model.tflite’).build())
    .setRunningMode(RunningMode.VIDEO)
    .setMaxResults(5)
    .build();
objectDetector = ObjectDetector.createFromOptions(context, options);
    

Transmissão ao vivo

ObjectDetectorOptions options =
  ObjectDetectorOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath(‘model.tflite’).build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setMaxResults(5)
    .setResultListener((result, inputImage) -> {
      // Process the detection result here.
    })
    .setErrorListener((result, inputImage) -> {
      // Process the classification errors here.
    })
   .build();
objectDetector = ObjectDetector.createFromOptions(context, options);
    

A implementação de código de exemplo do Detector de objetos 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 ver esse código na função setupObjectDetector() da classe ObjectDetectorHelper.

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
displayNamesLocales Define o idioma dos rótulos a ser usado para nomes de exibição fornecidos nos metadados do modelo da tarefa, se disponível. O padrão é en para inglês. É possível adicionar rótulos localizados aos metadados de um modelo personalizado usando a API TensorFlow Lite Metadata Writer Código da localidade en
maxResults Define o número máximo opcional de resultados de detecção com a melhor pontuação a ser retornado. Qualquer número positivo -1 (todos os resultados são retornados)
scoreThreshold Define o limite de pontuação de previsão que substitui o fornecido nos metadados do modelo (se houver). Os resultados abaixo desse valor são rejeitados. Qualquer ponto flutuante Não definido
categoryAllowlist Define a lista opcional de nomes de categorias permitidas. Se não estiver vazio, os resultados da detecção com o nome de categoria que não estiver nesse conjunto serão filtrados. Nomes de categoria duplicados ou desconhecidos são ignorados. Essa opção é mutuamente exclusiva com categoryDenylist, e o uso de ambos resulta em um erro. Qualquer string Não definido
categoryDenylist Define a lista opcional de nomes de categorias que não são permitidos. Se não estiver vazio, os resultados de detecção com o nome de categoria nesse conjunto serão filtrados. Nomes de categoria duplicados ou desconhecidos são ignorados. Essa opção é mutuamente exclusiva com categoryAllowlist, e o uso de ambos resulta em um erro. Qualquer string Não definido
resultListener Define o listener de resultados para receber os resultados de detecção de forma assíncrona quando o detector de objetos estiver no modo de transmissão ao vivo. Você só pode usar essa opção quando definir runningMode como LIVE_STREAM. Não relevante Não definido

preparar dados

É preciso converter a imagem ou o frame de entrada em um objeto com.google.mediapipe.framework.image.MPImage antes de transmiti-lo ao detector de objetos.

Os exemplos a seguir explicam e mostram como preparar os dados para o processamento de cada um dos tipos de dados disponíveis:

Imagem

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

Video

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 values. Use these values
// 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();
    

Transmissão ao vivo

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()
MPImage mpImage = new MediaImageBuilder(mediaImage).build();
    

No código de exemplo do detector de objetos, a preparação de dados é processada na classe ObjectDetectorHelper dentro das funções detectImage(), detectVideoFile() e detectLivestreamFrame().

Executar a tarefa

Dependendo do tipo de dados com que você está trabalhando, use o método ObjectDetector.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 detecções em uma linha de execução separada para evitar o bloqueio da linha de execução da interface do usuário.

Nos exemplos de código a seguir, mostramos exemplos simples de como executar o Detector de objetos nesses diferentes modos de dados:

Imagem

ObjectDetectorResult detectionResult = objectDetector.detect(image);
    

Video

// 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.
ObjectDetectorResult detectionResult =
    objectDetector.detectForVideo(image, frameTimestampMs);
    

Transmissão ao vivo

// Run inference on the frame. The detection results will be available
// via the `resultListener` provided in the `ObjectDetectorOptions` when
// the object detector was created.
objectDetector.detectAsync(image, frameTimestampMs);
    

O exemplo de código do Detector de objetos mostra as implementações de cada um desses modos em mais detalhes: detect(), detectVideoFile() e detectAsync(). O código de exemplo permite que o usuário alterne entre os modos de processamento, o que pode não ser necessário para seu caso de uso.

Observe o seguinte:

  • Ao executar no modo de vídeo ou de transmissão ao vivo, você também precisa fornecer o carimbo de data/hora do frame de entrada para a tarefa do Detector de objetos.
  • Quando executada no modo de imagem ou vídeo, a tarefa do Detector de objetos bloqueia a linha de execução atual até que ela termine de processar a imagem ou o frame de entrada. Para evitar o bloqueio da linha de execução atual, execute o processamento em uma linha de execução em segundo plano.
  • Quando executada no modo de transmissão ao vivo, a tarefa Detector de objetos não bloqueia a linha de execução atual, mas retorna imediatamente. Ele vai invocar o listener de resultados com o resultado da detecção sempre que terminar de processar um frame de entrada. Se a função de detecção for chamada quando a tarefa do Detector de objetos estiver ocupada processando outro frame, o novo quadro de entrada será ignorado.

Gerenciar e mostrar resultados

Ao executar a inferência, a tarefa do detector de objetos retorna um objeto ObjectDetectorResult que descreve os objetos encontrados na imagem de entrada.

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

ObjectDetectorResult:
 Detection #0:
  Box: (x: 355, y: 133, w: 190, h: 206)
  Categories:
   index       : 17
   score       : 0.73828
   class name  : dog
 Detection #1:
  Box: (x: 103, y: 15, w: 138, h: 369)
  Categories:
   index       : 17
   score       : 0.73047
   class name  : dog

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

O código de exemplo do Detector de objetos demonstra como exibir os resultados da detecção retornados da tarefa. Consulte a classe OverlayView para mais detalhes.