Guia de segmentação de imagens para Android

A tarefa MediaPipe Image Segmenter permite dividir imagens em regiões com base em categorias predefinidas para aplicar efeitos visuais, como o desfoque do plano de fundo. Estas instruções mostram como usar o Segmentador de imagens com apps Android. 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 exemplo de código do MediaPipe Tasks contém duas implementações simples de um app Segmentador de imagem para Android:

Os exemplos usam a câmera de um dispositivo Android físico para realizar a segmentação de imagens em um feed de câmera em tempo real. Também é possível escolher imagens e vídeos da galeria do dispositivo. Você pode usar os apps como ponto de partida para seu próprio app Android ou se referir a eles ao modificar um app existente. O código de exemplo do Segmenter de imagem está hospedado no GitHub (em inglês).

As seções a seguir se referem ao app Segmentação de imagem com uma máscara de categoria.

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 do Segmenter de imagem:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_segmentation/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 exemplo de aplicativo de segmentação de imagens:

  • ImageSegmenterHelper.kt: inicializa a tarefa de segmento de imagem, processa o modelo e delega a seleção.
  • CameraFragment.kt: fornece a interface do usuário e o código de controle de uma câmera.
  • GalleryFragment.kt: fornece a interface do usuário e o código de controle para selecionar arquivos de imagem e vídeo.
  • OverlayView.kt: processa e formata os resultados da segmentação.

Configuração

Esta seção descreve as principais etapas para configurar seu ambiente de desenvolvimento e projetos de código para usar o Segmentador de imagem. 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 Segmentador de imagens 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 Segmenter de imagem do MediaPipe requer um modelo treinado compatível com ela. Para mais informações sobre os modelos treinados disponíveis para o Image Segmenter, 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. Esse método é mencionado no exemplo de código na próxima seção.

No código de exemplo do Segmentador de imagem, o modelo é definido na classe ImageSegmenterHelper.kt na função setupImageSegmenter().

Criar a tarefa

Use a função createFromOptions para criar a tarefa. A função createFromOptions aceita opções de configuração, incluindo tipos de saída de máscara. Para mais informações sobre a configuração da tarefa, consulte Opções de configuração.

A tarefa Segmentador de imagem é 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, especifique 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 essa tarefa.

Imagem

ImageSegmenterOptions options =
  ImageSegmenterOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.IMAGE)
    .setOutputCategoryMask(true)
    .setOutputConfidenceMasks(false)
    .build();
imagesegmenter = ImageSegmenter.createFromOptions(context, options);
    

Video

ImageSegmenterOptions options =
  ImageSegmenterOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.VIDEO)
    .setOutputCategoryMask(true)
    .setOutputConfidenceMasks(false)
    .build();
imagesegmenter = ImageSegmenter.createFromOptions(context, options);
    

Transmissão ao vivo

ImageSegmenterOptions options =
  ImageSegmenterOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setOutputCategoryMask(true)
    .setOutputConfidenceMasks(false)
    .setResultListener((result, inputImage) -> {
         // Process the segmentation result here.
    })
    .setErrorListener((result, inputImage) -> {
         // Process the segmentation errors here.
    })
    .build()
imagesegmenter = ImageSegmenter.createFromOptions(context, options)
    

Com a implementação de código de exemplo do Segmenter de imagem, o usuário pode alternar entre os 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 classe ImageSegmenterHelper da função setupImageSegmenter().

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
outputCategoryMask Se definido como True, a saída incluirá uma máscara de segmentação como uma imagem uint8, em que cada valor de pixel indica o valor da categoria vencedora. {True, False} False
outputConfidenceMasks Se definido como True, a saída incluirá uma máscara de segmentação como uma imagem de valor flutuante, em que cada valor flutuante representa o mapa de pontuação de confiança da categoria. {True, False} True
displayNamesLocale 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
resultListener Define o listener de resultados para receber os resultados da segmentação de forma assíncrona quando o segmento de imagem está no modo de transmissão ao vivo. Só pode ser usado quando o modo de corrida está definido como LIVE_STREAM N/A N/A
errorListener Define um listener de erro opcional. N/A Não definido

preparar dados

O Segmentador de imagens funciona com imagens, arquivos de vídeo e transmissões 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.

É necessário converter a imagem de entrada ou o frame em um objeto com.google.mediapipe.framework.image.MPImage antes de transmiti-lo ao segmentador de imagem.

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 value. You’ll need them
// 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()
Image mpImage = new MediaImageBuilder(mediaImage).build();
    

No código de exemplo do Segmentador de imagem, a preparação de dados é processada na classe ImageSegmenterHelper pela função segmentLiveStreamFrame().

Executar a tarefa

Você chama uma função segment diferente com base no modo de corrida que está usando. A função "Segmento de imagem" retorna as regiões de segmentos identificadas na imagem ou frame de entrada.

Imagem

ImageSegmenterResult segmenterResult = imagesegmenter.segment(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.
ImageSegmenterResult segmenterResult =
    imagesegmenter.segmentForVideo(image, frameTimestampMs);
    

Transmissão ao vivo

// Run inference on the frame. The segmentations results will be available via
// the `resultListener` provided in the `ImageSegmenterOptions` when the image
// segmenter was created.
imagesegmenter.segmentAsync(image, frameTimestampMs);
    

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 Segmentador de imagem.
  • Quando executada no modo de imagem ou vídeo, a tarefa Segmentador de imagem 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 interface 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 do Segmentador de imagem 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 segmentAsync for chamada quando a tarefa do Segmentador de imagem estiver ocupada processando outro frame, a tarefa ignorará o novo frame de entrada.

No código de exemplo do Segmentador de imagem, as funções segment são definidas no arquivo ImageSegmenterHelper.kt.

Gerenciar e mostrar resultados

Ao executar a inferência, a tarefa Segmentador de imagem retorna um objeto ImageSegmenterResult que contém os resultados da tarefa de segmentação. O conteúdo da saída depende do outputType definido quando você configurou a tarefa.

As seções abaixo mostram exemplos dos dados de saída dessa tarefa:

Confiança da categoria

As imagens abaixo mostram uma visualização da saída da tarefa para uma máscara de confiança de categoria. A saída da máscara de confiança contém valores flutuantes entre [0, 1].

Saída da máscara de confiança da categoria e da imagem original. Imagem de origem do conjunto de dados Pascal VOC 2012.

Valor da categoria

As imagens a seguir mostram uma visualização da saída da tarefa para uma máscara de valor de categoria. O intervalo da máscara de categoria é [0, 255], e cada valor de pixel representa o índice da categoria vencedora da saída do modelo. O índice de categorias vencedor tem a pontuação mais alta entre as categorias que o modelo consegue reconhecer.

Saída original da máscara de imagem e categoria. Imagem de origem do conjunto de dados Pascal VOC 2012.