Guia de classificação de imagens para Android

A tarefa do MediaPipe Image Classifier permite que você faça a classificação de imagens. É possível usar essa tarefa para identificar o que uma imagem representa entre um conjunto de categorias definidas no tempo de treinamento. Estas instruções mostram como usar o Classificador de imagem com apps Android. O exemplo de código descrito nestas instruções está disponível no GitHub.

Para saber como essa tarefa funciona, confira a 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 de classificação de imagem para Android. O exemplo usa a câmera de um dispositivo Android físico para classificar objetos continuamente, além de usar imagens e vídeos da galeria para classificar objetos estaticamente.

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

Fazer o download do código

As instruções a seguir mostram como criar uma cópia local do exemplo de código 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 a finalização esparsa. Assim, você terá apenas os arquivos do app de exemplo do Image Classifier:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_classification/android
    

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

Principais componentes

Os arquivos a seguir contêm o código crucial para esse aplicativo de exemplo de classificação de imagens:

Configuração

Nesta seção, descrevemos as principais etapas para configurar seu ambiente de desenvolvimento e projetos de código para usar o classificador de imagem. Para 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 Image Classificador usa a biblioteca com.google.mediapipe:tasks-vision. Adicione essa dependência ao arquivo build.gradle do seu 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 classificador de imagem do MediaPipe requer um modelo treinado que seja compatível com essa tarefa. Para mais informações sobre modelos treinados disponíveis para o classificador de imagem, consulte a visão geral da tarefa seção Modelos.

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. Falaremos sobre esse método no exemplo de código na próxima seção.

No código de exemplo do classificador de imagem, o modelo é definido no arquivo ImageClassifierHelper.kt.

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 e uma lista de permissões ou bloqueio de categorias. Para mais informações sobre as opções de configuração, consulte Visão geral da configuração.

A tarefa Classificador de imagem é compatível com 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, especifique o modo de execução correspondente ao tipo de dados de entrada. Escolha a guia correspondente ao tipo de dados de entrada para saber como criar a tarefa e executar a inferência.

Imagem

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.IMAGE)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

Vídeo

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.VIDEO)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

Transmissão ao vivo

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setMaxResults(5)
    .setResultListener((result, inputImage) -> {
         // Process the classification result here.
    })
    .setErrorListener((result, inputImage) -> {
         // Process the classification errors here.
    })
    .build()
imageClassifier = ImageClassifier.createFromOptions(context, options)
    

A implementação de código de exemplo do Image Classifier permite que o usuário alterne 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. Veja esse código na função setupImageClassifier() do arquivo ImageClassifierHelper.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:

IMAGEM: o modo para entradas de imagem única.

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

LIVE_STREAM: o modo de uma transmissão ao vivo de dados de entrada, como de uma câmera. Nesse modo, resultListener precisa ser chamado para configurar um listener para receber resultados de forma assíncrona.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
displayNamesLocale Define o idioma dos rótulos a serem usados nos nomes de exibição fornecidos nos metadados do modelo da tarefa, se disponíveis. 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 da classificação com maior pontuação a serem retornados. Se < 0, todos os resultados disponíveis serão retornados. Qualquer número positivo -1
scoreThreshold Define o limite de pontuação de previsão que substitui o fornecido nos metadados do modelo (se houver). Resultados abaixo desse valor são rejeitados. Qualquer flutuação Não definido
categoryAllowlist Define a lista opcional de nomes de categorias permitidos. Se não estiver vazio, os resultados da classificação com nome de categoria que não estiver nesse conjunto serão filtrados. Nomes de categorias duplicados ou desconhecidos são ignorados. Essa opção é mutuamente exclusiva com categoryDenylist e usar os dois resulta em 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 da classificação com um nome de categoria nesse conjunto serão filtrados. Nomes de categorias duplicados ou desconhecidos são ignorados. Essa opção é mutuamente exclusiva com categoryAllowlist e usar os dois resulta em erro. Qualquer string Não definido
resultListener Define o listener de resultado para receber os resultados da classificação de forma assíncrona quando o classificador de imagem estiver no modo de transmissão ao vivo. 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 os dados

O Image Classificador funciona com imagens, arquivos de vídeo e streaming de vídeo ao vivo. Ela processa o pré-processamento de entrada de dados, incluindo redimensionamento, rotação e normalização de valor.

É necessário converter a imagem de entrada ou o frame em um objeto com.google.mediapipe.framework.image.MPImage antes de transmiti-lo ao classificador 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();
    

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 do Image Classifier, a preparação de dados é processada no arquivo ImageClassifierHelper.kt.

Executar a tarefa

É possível chamar a função classify correspondente ao modo de execução para acionar inferências. A API Image Classifier retorna as categorias possíveis para o objeto na imagem de entrada ou no frame.

Imagem

ImageClassifierResult classifierResult = imageClassifier.classify(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.
ImageClassifierResult classifierResult =
    imageClassifier.classifyForVideo(image, frameTimestampMs);
    

Transmissão ao vivo


// Run inference on the frame. The classifications results will be available 
// via the `resultListener` provided in the `ImageClassifierOptions` when 
// the image classifier was created.
imageClassifier.classifyAsync(image, frameTimestampMs);
    

Observe o seguinte:

  • Ao executar no modo de vídeo ou de transmissão ao vivo, também é necessário fornecer o carimbo de data/hora do frame de entrada para a tarefa do Classificador de imagem.
  • Quando executada no modo de imagem ou vídeo, a tarefa do Classificador 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 classificador de imagem não bloqueia a linha de execução atual, mas retorna imediatamente. Ele invocará o listener de resultados com o resultado da detecção sempre que terminar de processar um frame de entrada. Se a função classifyAsync for chamada quando a tarefa do Classificador de imagem estiver ocupada processando outro frame, a tarefa ignora o novo frame de entrada.

No código de exemplo do classificador de imagem, as funções classify são definidas no arquivo ImageClassifierHelper.kt.

Gerenciar e exibir resultados

Ao executar a inferência, a tarefa do classificador de imagem retorna um objeto ImageClassifierResult que contém a lista de categorias possíveis para os objetos na imagem de entrada ou no frame.

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

ImageClassifierResult:
 Classifications #0 (single classification head):
  head index: 0
  category #0:
   category name: "/m/01bwb9"
   display name: "Passer domesticus"
   score: 0.91406
   index: 671
  category #1:
   category name: "/m/01bwbt"
   display name: "Passer montanus"
   score: 0.00391
   index: 670

Esse resultado foi obtido executando o Bird Classifier em:

No código de exemplo do classificador de imagem, a classe ClassificationResultsAdapter no arquivo ClassificationResultsAdapter.kt processa os resultados:

fun updateResults(imageClassifierResult: ImageClassifierResult? = null) {
    categories = MutableList(adapterSize) { null }
    if (imageClassifierResult != null) {
        val sortedCategories = imageClassifierResult.classificationResult()
            .classifications()[0].categories().sortedBy { it.index() }
        val min = kotlin.math.min(sortedCategories.size, categories.size)
        for (i in 0 until min) {
            categories[i] = sortedCategories[i]
        }
    }
}