Criar um app personalizado da Web para detecção de objetos com o MediaPipe

1. Antes de começar

Com o MediaPipe Solutions, é possível aplicar soluções de machine learning (ML) nos apps. Ele oferece um framework usado para configurar pipelines de processamento pré-criados que entregam saídas imediatas, interativas e úteis para os usuários. É possível personalizar essas soluções com o Model Maker para alterar os modelos padrões.

A detecção de objetos é uma das muitas tarefas do ML Vision que o MediaPipe Solutions oferece. O MediaPipe Tasks está disponível para Android, Python e na Web.

Neste codelab, você vai adicionar detecção de objetos a um app da Web para detectar cães em imagens e vídeos ao vivo de webcams.

O que você vai aprender

  • Como incorporar uma tarefa de detecção de objetos a um app da Web com o MediaPipe Tasks.

O que você vai criar

  • Um app da Web que detecta se há cães. Também é possível personalizar um modelo para detectar uma classe de objetos que você definir. Para isso, use o MediaPipe Model Maker.

O que é necessário

  • Uma conta do CodePen.
  • Um dispositivo com um navegador da Web.
  • Conhecimento básico de JavaScript, CSS e HTML.

2. Preparativos

Este codelab executa o código no CodePen,​ um ambiente de desenvolvimento social para escrever código no navegador e verificar os resultados enquanto você cria.

Para configurar, siga estas etapas:

  1. Na sua conta do CodePen, acesse este link do CodePen. Use esse código como ponto de partida para criar seu próprio detector de objetos.
  2. Na parte de baixo do CodePen, no menu de navegação, clique em Fork para fazer uma cópia do código inicial.

O local do botão "Fork" no menu de navegação no CodePen

  1. Na guia JS, clique na f079bd83ad4547c9.png seta de expansão e selecione Maximize JavaScript editor. Você vai editar apenas o material na guia JS neste codelab, então não é necessário analisar as guias HTML ou CSS.

Revisar o app inicial

  1. No painel anterior, há duas imagens de cães e uma opção para executar sua webcam. O modelo que você vai usar neste tutorial foi treinado com os três cães exibidos nas duas imagens.

Uma prévia do app da Web do código inicial

  1. Na guia JS, há vários comentários ao longo do código. Por exemplo, você encontra este comentário na linha 15:
// Import the required package.

Esses comentários indicam que você precisa inserir snippets de código.

3. Importar o pacote de visão de tarefas do MediaPipe e adicionar as variáveis exigidas

  1. Na guia JS, importe o pacote MediaPipe tasks-vision:
// Import the required package.
​​import { ObjectDetector, FilesetResolver, Detection } from "https://cdn.skypack.dev/@mediapipe/tasks-vision@latest";

Esse código usa a rede de fornecimento de conteúdo (CDN, na sigla em inglês), do Skypack para importar o pacote. Para mais informações sobre como usar o Skypack com o CodePen, consulte Skypack + CodePen.

Nos seus projetos, adicione Node.js com npm ou o gerenciador de pacotes, ou o CDN que você preferir. Para mais informações sobre o pacote obrigatório que você precisa instalar, consulte pacotes JavaScript.

  1. Declare variáveis para o detector de objetos e o modo de execução:
// Create required variables.
let objectDetector: ObjectDetector;
let runningMode = "IMAGE";

A variável runningMode é uma string definida como um valor "IMAGE" ao detectar objetos em imagens ou um valor "VIDEO" ao detectar objetos em vídeos.

4. Inicializar o detector de objetos

  • Para inicializar o detector de objetos, adicione o código a seguir após o comentário relevante na guia JS:
// Initialize the object detector.
async function initializeObjectDetector() {
  const visionFilesetResolver = await FilesetResolver.forVisionTasks(
    "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
  );
  objectDetector = await ObjectDetector.createFromOptions(visionFilesetResolver, {
    baseOptions: {
      modelAssetPath: "https://storage.googleapis.com/mediapipe-assets/dogs.tflite"
    },
    scoreThreshold: 0.3,
    runningMode: runningMode
  });
}
initializeObjectDetector();

O método FilesetResolver.forVisionTasks() especifica o local do binário WebAssembly (Wasm) para a tarefa.

O método ObjectDetector.createFromOptions() instancia o detector de objetos. Forneça um caminho para o modelo usado para detecção. Nesse caso, o modelo de detecção de cães é hospedado no Cloud Storage.

A propriedade scoreThreshold é definida como um valor 0.3. Isso significa que o modelo retorna resultados de qualquer objeto detectado com um nível de confiança de, no mínimo, 30%. Ajuste esse limite de acordo com seu app.

A propriedade runningMode é definida na inicialização do objeto ObjectDetector. Você pode alterar essa e outras opções depois.

5. Executar previsões em imagens

  • Para executar previsões em imagens, acesse a função handleClick() e adicione o código a seguir ao corpo da função:
// Verify object detector is initialized and choose the correct running mode.
if (!objectDetector) {
    alert("Object Detector still loading. Please try again");
    return;
  }

  if (runningMode === "VIDEO") {
    runningMode = "IMAGE";
    await objectDetector.setOptions({ runningMode: runningMode });
  }

Esse código determina se o detector de objetos é inicializado e garante que o modo em execução esteja definido para imagens.

Detectar objetos

  • Para detectar objetos em imagens, adicione o código a seguir no corpo da função handleClick():
// Run object detection.
  const detections = objectDetector.detect(event.target);

O snippet de código a seguir inclui um exemplo dos dados de saída desta tarefa:

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

Processar e exibir previsões

  1. No final do corpo da função handleClick(), chame a função displayImageDetections():
// Call the displayImageDetections() function.
displayImageDetections(detections, event.target);
  1. No corpo da função displayImageDetections(), adicione o código a seguir para exibir os resultados da detecção de objetos:
// Display object detection results.

  const ratio = resultElement.height / resultElement.naturalHeight;

  for (const detection of detections) {
    // Description text
    const p = document.createElement("p");
    p.setAttribute("class", "info");
    p.innerText =
      detection.categories[0].categoryName +
      " - with " +
      Math.round(parseFloat(detection.categories[0].score) * 100) +
      "% confidence.";
    // Positioned at the top-left of the bounding box.
    // Height is that of the text.
    // Width subtracts text padding in CSS so that it fits perfectly.
    p.style =
      "left: " +
      detection.boundingBox.originX * ratio +
      "px;" +
      "top: " +
      detection.boundingBox.originY * ratio +
      "px; " +
      "width: " +
      (detection.boundingBox.width * ratio - 10) +
      "px;";
    const highlighter = document.createElement("div");
    highlighter.setAttribute("class", "highlighter");
    highlighter.style =
      "left: " +
      detection.boundingBox.originX * ratio +
      "px;" +
      "top: " +
      detection.boundingBox.originY * ratio +
      "px;" +
      "width: " +
      detection.boundingBox.width * ratio +
      "px;" +
      "height: " +
      detection.boundingBox.height * ratio +
      "px;";

    resultElement.parentNode.appendChild(highlighter);
    resultElement.parentNode.appendChild(p);
  }

Essa função exibe caixas delimitadoras sobre os objetos detectados nas imagens. Ela remove os destaques anteriores e, feito isso, cria e exibe tags <p> para destacar cada objeto detectado.

Testar o app

Ao fazer alterações no código no CodePen, o painel de prévia atualiza automaticamente após salvar. Provavelmente o app já atualizou se o salvamento automático estiver ativado, mas é uma boa ideia atualizar de novo.

Para testar o app, siga estas etapas:

  1. No painel de prévia, clique em cada imagem para ver as previsões. Uma caixa delimitadora mostra o nome do cão com o nível de confiança do modelo.
  2. Se não houver uma caixa delimitadora, abra o Chrome DevTools e procure erros no painel Console, ou revise as etapas anteriores para garantir que nada foi esquecido.

Uma prévia do app da Web com caixas delimitadoras sobre os cães detectados nas imagens

6. Executar previsões em um vídeo ao vivo de webcam

Detectar objetos

  • Para detectar objetos em um vídeo ao vivo de webcam, acesse a função predictWebcam() e adicione o código a seguir no corpo da função:
// Run video object detection.
  // If image mode is initialized, create a classifier with video runningMode.
  if (runningMode === "IMAGE") {
    runningMode = "VIDEO";
    await objectDetector.setOptions({ runningMode: runningMode });
  }
  let startTimeMs = performance.now();

  // Detect objects with the detectForVideo() method.
  const detections = await objectDetector.detectForVideo(video, startTimeMs);

  displayVideoDetections(detections);

A detecção de objetos em vídeos usa os mesmos métodos, não importa se é executada a inferência em dados de streaming ou um vídeo completo. O método detectForVideo() é parecido com o método detect() usado para fotos, mas ele inclui um parâmetro adicional para o carimbo de data/hora associado ao frame atual. A função realiza detecção em tempo real, então você transmite o tempo atual como o carimbo de data/hora.

Processar e exibir previsões

  • Para processar e exibir os resultados da detecção, acesse a função displayVideoDetections() e adicione o código a seguir ao corpo da função:
//  Display video object detection results.
  for (let child of children) {
    liveView.removeChild(child);
  }
  children.splice(0);

  // Iterate through predictions and draw them to the live view.
  for (const detection of result.detections) {
    const p = document.createElement("p");
    p.innerText =
      detection.categories[0].categoryName +
      " - with " +
      Math.round(parseFloat(detection.categories[0].score) * 100) +
      "% confidence.";
    p.style =
      "left: " +
      (video.offsetWidth -
        detection.boundingBox.width -
        detection.boundingBox.originX) +
      "px;" +
      "top: " +
      detection.boundingBox.originY +
      "px; " +
      "width: " +
      (detection.boundingBox.width - 10) +
      "px;";

    const highlighter = document.createElement("div");
    highlighter.setAttribute("class", "highlighter");
    highlighter.style =
      "left: " +
      (video.offsetWidth -
        detection.boundingBox.width -
        detection.boundingBox.originX) +
      "px;" +
      "top: " +
      detection.boundingBox.originY +
      "px;" +
      "width: " +
      (detection.boundingBox.width - 10) +
      "px;" +
      "height: " +
      detection.boundingBox.height +
      "px;";

    liveView.appendChild(highlighter);
    liveView.appendChild(p);

    // Store drawn objects in memory so that they're queued to delete at next call.
    children.push(highlighter);
    children.push(p);
  }

Ele remove os destaques anteriores e, feito isso, cria e exibe tags <p> para destacar cada objeto detectado.

Testar o app

Para testar a detecção de objetos em tempo real, tenha uma imagem de um dos cães em cada modelo treinado.

Para testar o app, siga estas etapas:

  1. Faça o download de fotos de cães no seu smartphone.
  2. No painel de prévia, clique em Ativar webcam.
  3. Caso seu navegador apresente uma caixa de diálogo pedindo acesso à webcam, conceda a permissão.
  4. Segure o smartphone com a imagem do cão na frente da webcam. Uma caixa delimitadora mostra o nome do cão e o nível de confiança do modelo.
  5. Se não houver uma caixa delimitadora, abra o Chrome DevTools e procure erros no painel Console, ou revise as etapas anteriores para garantir que nada foi esquecido.

Uma caixa delimitadora sobre uma imagem de um cão exibida em uma webcam ao vivo

7. Parabéns

Parabéns! Você criou um app da Web que detecta objetos em imagens. Para saber mais, consulte uma versão completa do app no CodePen.

Saiba mais