Guia de incorporação de imagens para Android

A tarefa MediaPipe Image Embedder permite converter dados de imagem em uma representação numérica para realizar tarefas de processamento de imagens relacionadas a ML, como comparar similaridade de duas imagens. Estas instruções mostram como usar o Incorporador de imagens com apps Android.

Para mais informações sobre recursos, modelos e opções de configuração, desta tarefa, consulte a Visão geral.

Exemplo de código

O código de exemplo do MediaPipe Tasks é uma implementação simples de um incorporador de imagem para Android. O exemplo usa a câmera de um dispositivo Android físico para incorporar imagens continuamente e também executar o incorporador em arquivos de imagem armazenados no dispositivo.

Você pode usar o app como ponto de partida para seu próprio app Android ou consultá-lo ao modificar um aplicativo existente. O exemplo de código do incorporador de imagens está hospedado GitHub.

Fazer o download do código

As instruções a seguir mostram como criar uma cópia local do 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. Opcionalmente, configure sua instância git para usar a finalização esparsa. Assim, você terá apenas os arquivos do app de exemplo do incorporador de imagens:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_embedder/android
    

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

Principais componentes

Os arquivos a seguir contêm o código crucial para este exemplo de incorporador de imagens aplicativo:

Configuração

Esta seção descreve as principais etapas para configurar seu ambiente de desenvolvimento e projetos de código para usar o incorporador de imagens. Para informações gerais sobre como configurar sua ambiente de desenvolvimento para usar tarefas do MediaPipe, incluindo a versão da plataforma consulte o Guia de configuração do Android.

Dependências

O incorporador de imagens usa a biblioteca com.google.mediapipe:tasks-vision. Adicionar dependência do 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 MediaPipe Image Embedder exige um modelo treinado que seja compatível com esse tarefa. Para mais informações sobre os modelos treinados disponíveis para o incorporador de imagens, consulte na seção de visão geral da tarefa Modelos.

Selecione e 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. Na código de exemplo, o modelo é definido na função setupImageEmbedder() na ImageEmbedderHelper.kt arquivo:

Use o método BaseOptions.Builder.setModelAssetPath() para especificar o caminho. usados pelo modelo. Esse método é mencionado no exemplo de código nos próximos nesta seção.

Criar a tarefa

Use a função createFromOptions para criar a tarefa. A A função createFromOptions aceita opções de configuração para definir o incorporador . Para mais informações sobre as opções de configuração, consulte Configuração Visão geral.

A tarefa Incorporador de imagens oferece suporte a três tipos de dados de entrada: imagens estáticas, arquivos de vídeo e transmissões de vídeo ao vivo. Você precisa especificar o modo de corrida correspondente o tipo de dados de entrada ao criar a tarefa. Escolha a guia correspondente ao seu tipo de dados de entrada para ver como criar a tarefa e executar a inferência.

Imagem

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.IMAGE)
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

Vídeo

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.VIDEO)
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

Transmissão ao vivo

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setResultListener((result, inputImage) -> {
         // Process the embedding result here.
    })
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

O exemplo de implementação de código permite que o usuário alterne entre o processamento dois modos. A abordagem torna o código de criação da tarefa mais complicado e pode não ser apropriados para seu caso de uso. Confira esse código função setupImageEmbedder() na ImageEmbedderHelper.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. Existem três modos:

IMAGEM: o modo para entradas de imagem única.

VÍDEO: o modo para frames decodificados de um vídeo.

LIVE_STREAM: o modo de transmissão ao vivo da entrada dados de uma câmera, por exemplo. Neste modo, resultListener deve ser chamado para configurar um listener e receber resultados de forma assíncrona.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
l2_normalize Define se o vetor de atributo retornado deve ser normalizado com a norma L2. Use essa opção somente se o modelo ainda não tiver um L2_NORMALIZATION Op. do TFLite. Na maioria dos casos, esse já é o caso e Assim, a normalização L2 é alcançada por meio da inferência do TFLite sem a necessidade para essa opção. Boolean False
quantize Se o embedding retornado deve ser quantizado em bytes por meio de com a quantização escalar. Os embeddings são implicitamente definidos como unidade-norma e portanto, qualquer dimensão terá um valor em [-1.0, 1.0]. Usar a opção l2_normalize, se esse não for o caso. Boolean False
resultListener Define o listener de resultados para receber os resultados de embedding. de forma assíncrona quando o incorporador de imagens está na transmissão ao vivo modo Só pode ser usado quando o modo de corrida está definido como LIVE_STREAM N/A Não definido
errorListener Define um listener de erro opcional. N/A Não definido

Preparar dados

O incorporador de imagens funciona com imagens, arquivos de vídeo e streaming de vídeo ao vivo. A tarefa lida com o pré-processamento de entrada de dados, incluindo redimensionamento, rotação e valor. normalização.

Você precisa converter a imagem ou frame de entrada com.google.mediapipe.framework.image.MPImage antes de transmiti-lo ao Incorporador de imagens.

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

Vídeo

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, a preparação dos dados é tratada na ImageEmbedderHelper.kt .

Executar a tarefa

Você pode chamar a função embed correspondente ao modo de corrida para acionar ou inferências. A API Image Embedder retorna os vetores de embedding para a entrada imagem ou frame.

Imagem

ImageEmbedderResult embedderResult = imageEmbedder.embed(image);
    

Vídeo

// 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.
ImageEmbedderResult embedderResult =
    imageEmbedder.embedForVideo(image, frameTimestampMs);
    

Transmissão ao vivo


// Run inference on the frame. The embedding results will be available
// via the `resultListener` provided in the `ImageEmbedderOptions` when
// the image embedder was created.
imageEmbedder.embedAsync(image, frameTimestampMs);
    

Observe o seguinte:

  • Ao executar nos modos de vídeo ou de transmissão ao vivo, você também precisa forneça o carimbo de data/hora do frame de entrada para a tarefa Incorporador de imagens.
  • Quando executada no modo de imagem ou vídeo, a tarefa Incorporador de imagens bloquear a linha de execução atual até que ela termine de processar a imagem de entrada ou frame. Para evitar o bloqueio da linha de execução atual, execute o processamento em um linha de execução em segundo plano.
  • Quando executada no modo de transmissão ao vivo, a tarefa "Incorporador de imagens" não bloqueia thread atual, mas retorna imediatamente. Ele vai invocar seu resultado com o resultado da detecção sempre que ele terminar de processar um erro. frame de entrada. Se a função embedAsync for chamada quando o incorporador de imagens estiver ocupada processando outro frame, a tarefa ignora o novo frame de entrada.

No código de exemplo, a função embed é definida no ImageEmbedderHelper.kt .

Gerenciar e exibir resultados

Ao executar a inferência, a tarefa do incorporador de imagens retorna um ImageEmbedderResult. objeto que contém uma lista de embeddings (ponto flutuante ou quantificada por escalar) para a imagem de entrada.

Confira abaixo um exemplo dos dados de saída desta tarefa:

ImageEmbedderResult:
  Embedding #0 (sole embedding head):
    float_embedding: {0.0, 0.0, ..., 0.0, 1.0, 0.0, 0.0, 2.0}
    head_index: 0

Esse resultado foi obtido pela incorporação da seguinte imagem:

É possível comparar a semelhança de dois embeddings usando o função ImageEmbedder.cosineSimilarity. Confira o código a seguir exemplo.

// Compute cosine similarity.
double similarity = ImageEmbedder.cosineSimilarity(
  result.embeddingResult().embeddings().get(0),
  otherResult.embeddingResult().embeddings().get(0));