Detectar objetos em imagens no Android usando um modelo treinado pelo AutoML

Depois de treinar seu modelo com o AutoML Vision Edge, você poderá usá-lo no seu app para detectar objetos nas imagens.

Há duas maneiras de integrar modelos treinados usando o AutoML Vision Edge: é possível agrupar o modelo colocando-o na pasta de recursos do app ou fazer o download dele dinamicamente usando o Firebase.

Opções de empacotamento de modelos
Incluído no seu app
  • O modelo faz parte do APK do seu app
  • O modelo estará disponível imediatamente, mesmo quando o dispositivo Android estiver off-line
  • Não é necessário ter um projeto do Firebase
Hospedado com o Firebase
  • A hospedagem do modelo é feita quando ele é enviado para o Firebase Machine Learning
  • Reduz o tamanho do APK
  • O download do modelo é feito sob demanda
  • Enviar atualizações do modelo sem republicar o app
  • Teste A/B fácil com a Configuração remota do Firebase
  • Requer um projeto do Firebase

Antes de começar

  1. Se você quiser fazer o download de um modelo, adicione o Firebase ao seu projeto do Android, caso ainda não tenha feito isso. Essa etapa não é necessária para empacotar o modelo.

  2. Adicione as dependências da biblioteca de tarefas do TensorFlow Lite ao arquivo Gradle no nível do app do seu módulo, que geralmente é app/build.gradle:

    Para agrupar um modelo e seu app:

    dependencies {
      // ...
      // Object detection with a bundled Auto ML model
      implementation 'org.tensorflow:tensorflow-lite-task-vision:0.0.0-nightly-SNAPSHOT'
    }
    

    Para fazer o download dinâmico de um modelo do Firebase, adicione também a dependência do Firebase ML:

    dependencies {
      // ...
      // Object detection with an Auto ML model deployed to Firebase
      implementation platform('com.google.firebase:firebase-bom:26.1.1')
      implementation 'com.google.firebase:firebase-ml-model-interpreter'
    
      implementation 'org.tensorflow:tensorflow-lite-task-vision:0.0.0-nightly'
    }
    

1. Carregar o modelo

Configurar uma fonte de modelo local

Para agrupar o modelo e o aplicativo, siga estas etapas:

  1. Extraia o modelo do arquivo zip que você salvou do Console do Google Cloud.
  2. Inclua o modelo no pacote de apps:
    1. Se você não tiver uma pasta de recursos no projeto, crie uma clicando com o botão direito do mouse na pasta app/ e, em seguida, em Novo > Pasta > Pasta de recursos.
    2. Copie o arquivo de modelo tflite com metadados incorporados para a pasta de recursos.
  3. Adicione o seguinte ao arquivo build.gradle do app para garantir que o Gradle não compacte o arquivo de modelo ao criar o app:

    android {
        // ...
        aaptOptions {
            noCompress "tflite"
        }
    }
    

    O arquivo de modelo será incluído no pacote de app e estará disponível como um recurso bruto.

Configurar uma fonte de modelos hospedada no Firebase

Para usar o modelo hospedado remotamente, crie um objeto RemoteModel, especificando o nome que você atribuiu ao modelo quando o publicou:

Java

// Specify the name you assigned when you deployed the model.
FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();

Kotlin

// Specify the name you assigned when you deployed the model.
val remoteModel =
    FirebaseCustomRemoteModel.Builder("your_model_name").build()

Em seguida, inicie a tarefa de download do modelo, especificando as condições sob as quais você quer permitir o download. Se o modelo não estiver no dispositivo ou se uma versão mais recente do modelo estiver disponível, a tarefa fará o download do modelo de forma assíncrona do Firebase:

Java

DownloadConditions downloadConditions = new DownloadConditions.Builder()
        .requireWifi()
        .build();
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(@NonNull Task<Void> task) {
                // Success.
            }
        });

Kotlin

val downloadConditions = DownloadConditions.Builder()
    .requireWifi()
    .build()
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
    .addOnSuccessListener {
        // Success.
    }

Muitos apps iniciam a tarefa de download no código de inicialização, mas você pode fazer isso a qualquer momento antes de precisar usar o modelo.

Criar um detector de objetos do seu modelo

Depois de configurar as origens do modelo, crie um objeto ObjectDetector a partir de uma delas.

Se você tiver somente um modelo agrupado localmente, basta criar um detector de objetos a partir do arquivo de modelo e configurar o limite de pontuação de confiança que quer exigir. Consulte Avaliar seu modelo:

Java

// Initialization
ObjectDetectorOptions options = ObjectDetectorOptions.builder()
    .setScoreThreshold(0)  // Evaluate your model in the Google Cloud Console
                           // to determine an appropriate value.
    .build();
ObjectDetector objectDetector = ObjectDetector.createFromFileAndOptions(context, modelFile, options);

Kotlin

// Initialization
val options = ObjectDetectorOptions.builder()
    .setScoreThreshold(0)  // Evaluate your model in the Google Cloud Console
                           // to determine an appropriate value.
    .build()
val objectDetector = ObjectDetector.createFromFileAndOptions(context, modelFile, options)

Se você tiver um modelo hospedado remotamente, será necessário verificar se foi feito o download dele antes de executá-lo. É possível verificar o status da tarefa de download do modelo usando o método isModelDownloaded() do gerenciador de modelos.

Embora isso só precise ser confirmado antes da execução do detector de objetos, se você tiver um modelo hospedado remotamente e um modelo agrupado localmente, talvez faça sentido realizar essa verificação ao instanciar o detector de objetos: crie um detector de objetos do modelo remoto, se ele tiver sido transferido por download. Caso contrário, crie a partir do modelo local.

Java

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
            }
        });

Kotlin

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener { success ->

        }

Se você tiver apenas um modelo hospedado remotamente, desative o recurso relacionado ao modelo (por exemplo, ocultando ou esmaecendo parte da IU) até confirmar que o download do modelo foi concluído. Para fazer isso, anexe um listener ao método download() do gerenciador de modelos:

Quando você souber que o modelo foi salvo, crie um detector de objetos a partir do arquivo de modelo:

Java

FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnCompleteListener(new OnCompleteListener<File>() {
            @Override
            public void onComplete(@NonNull Task<File> task) {
                File modelFile = task.getResult();
                if (modelFile != null) {
                    ObjectDetectorOptions options = ObjectDetectorOptions.builder()
                            .setScoreThreshold(0)
                            .build();
                    objectDetector = ObjectDetector.createFromFileAndOptions(
                            getApplicationContext(), modelFile.getPath(), options);
                }
            }
        });

Kotlin

FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnSuccessListener { modelFile ->
            val options = ObjectDetectorOptions.builder()
                    .setScoreThreshold(0f)
                    .build()
            objectDetector = ObjectDetector.createFromFileAndOptions(
                    applicationContext, modelFile.path, options)
        }

2. Preparar a imagem de entrada

Em seguida, para cada imagem que você quer rotular, crie um objeto TensorImage a partir da sua imagem. É possível criar um objeto TensorImage a partir de um Bitmap usando o método fromBitmap:

Java

TensorImage image = TensorImage.fromBitmap(bitmap);

Kotlin

val image = TensorImage.fromBitmap(bitmap)

Se os dados de imagem não estiverem em um Bitmap, será possível carregar uma matriz de pixels conforme mostrado nos documentos do TensorFlow Lite.

3. Executar o detector de objetos

Para rotular objetos em uma imagem, transmita o objeto TensorImage para o método detect() do ObjectDetector.

Java

List<Detection> results = objectDetector.detect(image);

Kotlin

val results = objectDetector.detect(image)

4. Ver informações sobre os objetos rotulados

Se a operação de detecção de objetos for bem-sucedida, ela retornará uma lista de objetos Detection. Cada objeto Detection representa algo que foi detectado na imagem. É possível receber a caixa delimitadora de cada objeto e os rótulos associados.

Exemplo:

Java

for (Detection result : results) {
    RectF bounds = result.getBoundingBox();
    List<Category> labels = result.getCategories();
}

Kotlin

for (result in results) {
    val bounds = result.getBoundingBox()
    val labels = result.getCategories()
}

Dicas para melhorar o desempenho em tempo real

Caso você queira rotular imagens em um aplicativo em tempo real, siga estas diretrizes para ter as melhores taxas de frames:

  • Limite as chamadas para o rotulador de imagens. Se um novo quadro de vídeo ficar disponível enquanto o rotulador de imagens estiver em execução, elimine o frame. Consulte a classe VisionProcessorBase no app de amostra do guia de início rápido para conferir um exemplo.
  • Se você estiver usando a saída do rotulador de imagens para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada quadro de entrada. Consulte as classes CameraSourcePreview e GraphicOverlay no app de amostra do guia de início rápido para conferir um exemplo.
  • Se você usar a API Camera2, capture imagens no formato ImageFormat.YUV_420_888.

    Se você usar a API Camera mais antiga, capture imagens no formato ImageFormat.NV21.