App Vertex AI Vision Queue Detection

1. Objetivos

Visão geral

Este codelab vai se concentrar na criação de um aplicativo da Vertex AI Vision completo para monitorar o tamanho da fila usando imagens de vídeo de varejo. Vamos usar os recursos integrados do modelo especializado pré-treinado Análise de ocupação para capturar o seguinte:

  • Conte o número de pessoas na fila.
  • Conte o número de pessoas atendidas no balcão.

O que você vai aprender

  • Como criar e implantar um aplicativo na Vertex AI Vision
  • Como configurar um stream RTSP usando um arquivo de vídeo e ingerir o stream na Vertex AI Vision usando vaictl de um notebook Jupyter.
  • Como usar o modelo de análise de ocupação e os diferentes recursos dele.
  • Como pesquisar vídeos no Media Warehouse da Vertex AI Vision.
  • Como conectar a saída ao BigQuery, escrever uma consulta SQL para extrair insights da saída JSON do modelo e usar a saída para rotular e anotar o vídeo original.

Custo:

O custo total da execução deste laboratório no Google Cloud é de aproximadamente US $2.

2. Antes de começar

Crie um projeto e ative as APIs:

  1. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto na nuvem do Google Cloud. Observação: se você não pretende manter os recursos criados neste procedimento, crie um projeto novo em vez de selecionar um que já existe. Depois de concluir essas etapas, é possível excluir o projeto. Para fazer isso, basta remover todos os recursos associados a ele. Acessar o seletor de projetos
  2. Verifique se o faturamento está ativado para seu projeto do Cloud. Saiba como verificar se o faturamento está ativado em um projeto.
  3. Ative as APIs Compute Engine, Vertex, Notebook e Vision AI. Ativar as APIs

Criar uma conta de serviço:

  1. No Console do Google Cloud, acesse a página Criar conta de serviço. Acesse "Criar conta de serviço"
  2. Selecione o projeto.
  3. No campo Nome da conta de serviço, insira um nome. O Console do Google Cloud preenche o campo ID da conta de serviço com base nesse nome. No campo Descrição da conta de serviço, insira uma descrição. Por exemplo, "Conta de serviço para início rápido".
  4. Clique em Criar e continuar.
  5. Para conceder acesso ao seu projeto, conceda os seguintes papéis à sua conta de serviço:
  • Vision AI > Editor do Vision AI
  • Compute Engine > Administrador da instância do Compute (Beta)
  • BigQuery > Administrador do BigQuery.

Na lista Selecionar papel, escolha um. Para papéis adicionais, clique em Adicionar outro papel e adicione cada papel adicional.

  1. Clique em Continuar.
  2. Clique em Concluído para terminar a criação da conta de serviço. Não feche a janela do navegador. Você vai usá-la na próxima etapa.

3. Configurar o Jupyter Notebook

Antes de criar um app no Occupancy Analytics, é necessário registrar um fluxo que pode ser usado depois pelo app.

Neste tutorial, você vai criar uma instância do notebook Jupyter que hospeda um vídeo e enviar os dados de streaming desse vídeo do notebook. Estamos usando o notebook Jupyter porque ele oferece flexibilidade para executar comandos do shell e executar código personalizado de pré/pós-processamento em um único lugar, o que é muito bom para experimentos rápidos. Vamos usar este notebook para:

  1. Executar o servidor rtsp como um processo em segundo plano
  2. Executar o comando vaictl como um processo em segundo plano
  3. Executar consultas e código de processamento para analisar a saída da análise de ocupação

Criar um notebook do Jupyter

A primeira etapa para enviar vídeos de uma instância do Jupyter Notebook é criar o notebook com a conta de serviço criada na etapa anterior.

  1. No console do Google Cloud, acesse a página da Vertex AI. Acessar o Vertex AI Workbench
  2. Clique em "Notebooks gerenciados pelo usuário".

65b7112822858dce.png

  1. Clique em Novo notebook > Tensorflow Enterprise 2.6 (com LTS) > Sem GPUs

dc156f20b14651d7.png

  1. Insira o nome do notebook do Jupyter. Para mais informações, consulte Convenção de nomenclatura de recursos.

b4dbc5fddc37e8d9.png

  1. Clique em OPÇÕES AVANÇADAS.
  2. Role a tela para baixo até Seções de permissões.
  3. Desmarque a opção Usar a conta de serviço padrão do Compute Engine.
  4. Adicione o e-mail da conta de serviço criado na etapa anterior. Clique em Criar.

ec0b9ef00f0ef470.png

  1. Quando a instância for criada, clique em ABRIR JUPYTERLAB.

4. Configurar um notebook para transmitir vídeo

Antes de criar um app no Occupancy Analytics, é necessário registrar um fluxo que pode ser usado depois pelo app.

Neste tutorial, vamos usar nossa instância do notebook Jupyter para hospedar um vídeo e enviar os dados de streaming do terminal do notebook.

Baixar a ferramenta de linha de comando vaictl

  1. Na instância do JupyterLab aberta, abra um Notebook no iniciador.

a6d182923ae4ada3.png

  1. Faça o download da ferramenta de linha de comando da Vertex AI Vision (vaictl), da ferramenta de linha de comando do servidor rtsp e da ferramenta open-cv usando o seguinte comando na célula do notebook:
!wget -q https://github.com/aler9/rtsp-simple-server/releases/download/v0.20.4/rtsp-simple-server_v0.20.4_linux_amd64.tar.gz
!wget -q https://github.com/google/visionai/releases/download/v0.0.4/visionai_0.0-4_amd64.deb
!tar -xf rtsp-simple-server_v0.20.4_linux_amd64.tar.gz
!pip install opencv-python --quiet
!sudo apt-get -qq remove -y visionai
!sudo apt-get -qq install -y ./visionai_0.0-4_amd64.deb
!sudo apt-get -qq install -y ffmpeg

5. Ingerir um arquivo de vídeo para streaming

Depois de configurar o ambiente do notebook com as ferramentas de linha de comando necessárias, copie um arquivo de vídeo de amostra e use vaictl para transmitir os dados de vídeo para o app de análise de ocupação.

Registrar um novo stream

  1. Clique na guia "Fluxos de cliques" no painel à esquerda da Vertex AI Vision.
  2. Clique no botão "Registrar" na parte de cima eba418e723916514.png
  3. Em "Nome do stream", insira queue-stream.
  4. Em "Região", escolha a mesma região selecionada durante a criação do notebook na etapa anterior.
  5. Clique em Registrar.

Copiar um vídeo de amostra para sua VM

  1. No notebook, copie um vídeo de amostra com o seguinte comando wget.
!wget -q https://github.com/vagrantism/interesting-datasets/raw/main/video/collective_activity/seq25_h264.mp4

Transmitir vídeo da VM e ingerir dados no stream

  1. Para enviar esse arquivo de vídeo local para o fluxo de entrada do app, use o seguinte comando na célula do notebook. Faça as seguintes substituições de variáveis:
  • PROJECT_ID: o ID do seu projeto do Google Cloud.
  • LOCATION: seu ID do local. Por exemplo, us-central1. Para mais informações, consulte Locais do Cloud.
  • LOCAL_FILE: o nome de um arquivo de vídeo local. Por exemplo, seq25_h264.mp4.
PROJECT_ID='<Your Google Cloud project ID>'
LOCATION='<Your stream location>'
LOCAL_FILE='seq25_h264.mp4'
STREAM_NAME='queue-stream'
  1. Inicie um rtsp-simple-server em que transmitimos o arquivo de vídeo com o protocolo rtsp.
import os
import time
import subprocess

subprocess.Popen(["nohup", "./rtsp-simple-server"], stdout=open('rtsp_out.log', 'a'), stderr=open('rtsp_err.log', 'a'), preexec_fn=os.setpgrp)
time.sleep(5)
  1. Usar a ferramenta de linha de comando ffmpeg para repetir o vídeo no fluxo rtsp
subprocess.Popen(["nohup", "ffmpeg", "-re", "-stream_loop", "-1", "-i", LOCAL_FILE, "-c", "copy", "-f", "rtsp", f"rtsp://localhost:8554/{LOCAL_FILE.split('.')[0]}"], stdout=open('ffmpeg_out.log', 'a'), stderr=open('ffmpeg_err.log', 'a'), preexec_fn=os.setpgrp)
time.sleep(5)
  1. Use a ferramenta de linha de comando vaictl para transmitir o vídeo do URI do servidor rtsp para nosso stream da Vertex AI Vision "queue-stream" criado na etapa anterior.
subprocess.Popen(["nohup", "vaictl", "-p", PROJECT_ID, "-l", LOCATION, "-c", "application-cluster-0", "--service-endpoint", "visionai.googleapis.com", "send", "rtsp", "to", "streams", "queue-stream", "--rtsp-uri", f"rtsp://localhost:8554/{LOCAL_FILE.split('.')[0]}"], stdout=open('vaictl_out.log', 'a'), stderr=open('vaictl_err.log', 'a'), preexec_fn=os.setpgrp)

Pode levar cerca de 100 segundos entre o início da operação de ingestão do vaictl e o aparecimento do vídeo no painel.

Depois que a ingestão de stream estiver disponível, você poderá ver o feed de vídeo na guia Streams do painel da Vertex AI Vision selecionando o stream de fila.

Acessar a guia "Streams"

1b7aac7d36552f29.png

6. Criar um aplicativo

A primeira etapa é criar um app que processe seus dados. Um app pode ser considerado um pipeline automatizado que conecta o seguinte:

  • Ingestão de dados: um feed de vídeo é ingerido em um stream.
  • Análise de dados: um modelo de IA(visão computacional) pode ser adicionado após a ingestão.
  • Armazenamento de dados: as duas versões do feed de vídeo (o stream original e o stream processado pelo modelo de IA) podem ser armazenadas em um warehouse de mídia.

No console do Google Cloud, um app é representado como um gráfico.

Criar um app vazio

Antes de preencher o gráfico de apps, é necessário criar um app vazio.

Crie um app no console do Google Cloud.

  1. Acesse o console do Google Cloud.
  2. Abra a guia Aplicativos do painel da Vertex AI Vision. Acesse a guia "Aplicativos"
  3. Clique no botão Criar. 21ecba7a23e9979e.png
  4. Insira queue-app como o nome do app e escolha sua região.
  5. Clique em Criar.

Adicionar nós do componente do app

Depois de criar o aplicativo vazio, adicione os três nós ao gráfico do app:

  1. Nó de ingestão: o recurso de stream que ingere dados enviados de um servidor de vídeo rtsp criado no notebook.
  2. Nó de processamento: o modelo de análise de ocupação que atua nos dados ingeridos.
  3. Nó de armazenamento: o warehouse de mídia que armazena vídeos processados e serve como um repositório de metadados. Os armazenamentos de metadados incluem informações de análise sobre dados de vídeo ingeridos e informações inferidas pelos modelos de IA.

Adicione nós de componentes ao app no console.

  1. Abra a guia Aplicativos do painel da Vertex AI Vision. Acesse a guia "Aplicativos"

Isso leva você à visualização gráfica do pipeline de processamento.

Adicionar um nó de ingestão de dados

  1. Para adicionar um nó de stream de entrada, selecione a opção Streams na seção Conectores do menu lateral.
  2. Na seção Origem do menu Stream que é aberto, selecione Adicionar streams.
  3. No menu Adicionar streams, escolha queue-stream.
  4. Para adicionar o stream ao gráfico de apps, clique em Adicionar streams.

Adicionar um nó de processamento de dados

  1. Para adicionar o nó do modelo de contagem de ocupação, selecione a opção análise de ocupação na seção Modelos especializados do menu lateral.
  2. Deixe as seleções padrão Pessoas. Desmarque Veículos se essa opção já estiver selecionada.

618b0c9dc671bae3.png

  1. Na seção "Opções avançadas", clique em Criar zonas/linhas ativas 5b2f31235603e05d.png.
  2. Desenhe as zonas ativas usando a ferramenta de polígono para contar pessoas nessa zona. Rotule a zona de acordo

50281a723650491f.png

  1. Clique na seta para voltar na parte de cima.

2bf0ff4d029d29eb.png

  1. Clique na caixa de seleção para adicionar configurações de tempo de permanência e detectar congestionamentos.

c067fa256ca5bb96.png

Adicionar um nó de armazenamento de dados

  1. Para adicionar o nó de destino (armazenamento) de saída, selecione a opção Warehouse da Vision AI na seção Conectores do menu lateral.
  2. Clique no conector Vertex AI Warehouse para abrir o menu e clique em Conectar warehouse.
  3. No menu Conectar warehouse, selecione Criar warehouse. Nomeie o warehouse como queue-warehouse e deixe a duração do TTL em 14 dias.
  4. Clique no botão Criar para adicionar o warehouse.

7. Conectar saída à tabela do BigQuery

Quando você adiciona um conector do BigQuery ao app Vertex AI Vision, todas as saídas do modelo do app conectado são ingeridas na tabela de destino.

Você pode criar sua própria tabela do BigQuery e especificá-la ao adicionar um conector do BigQuery ao app ou deixar que a plataforma de apps do Vertex AI Vision crie a tabela automaticamente.

Criação automática de tabelas

Se você permitir que a plataforma de apps da Vertex AI Vision crie a tabela automaticamente, especifique essa opção ao adicionar o nó do conector do BigQuery.

As seguintes condições de conjunto de dados e tabela se aplicam se você quiser usar a criação automática de tabelas:

  • Conjunto de dados: o nome do conjunto de dados criado automaticamente é visionai_dataset.
  • Tabela: o nome da tabela criada automaticamente é visionai_dataset.APPLICATION_ID.
  • Tratamento de erros:
  • Se a tabela com o mesmo nome no mesmo conjunto de dados existir, nenhuma criação automática será feita.
  1. Abra a guia Aplicativos do painel da Vertex AI Vision. Acesse a guia "Aplicativos"
  2. Selecione Ver app ao lado do nome do aplicativo na lista.
  3. Na página do criador de aplicativos, selecione BigQuery na seção Conectores.
  4. Deixe o campo Caminho do BigQuery vazio.

ee0b67d4ab2263d.png

  1. Em Armazenar metadados de:, selecione apenas Análise de ocupação e desmarque os fluxos.

O gráfico final do app vai ficar assim:

da0a1a049843572f.png

8. Implantar o app para uso

Depois de criar o app completo com todos os componentes necessários, a última etapa para usar o app é implantá-lo.

  1. Abra a guia Aplicativos do painel da Vertex AI Vision. Acesse a guia "Aplicativos"
  2. Selecione Ver app ao lado do app queue-app na lista.
  3. Na página Studio, clique no botão Implantar.
  4. Na caixa de diálogo de confirmação a seguir, clique em Implantar. A operação de implantação pode levar alguns minutos para ser concluída. Quando a implantação for concluída, marcas de seleção verdes vão aparecer ao lado dos nós. dc514d9b9f35099d.png

9. Pesquisar conteúdo de vídeo no data warehouse

Depois de ingerir dados de vídeo no app de processamento, você pode conferir os dados analisados e pesquisar com base nas informações de análise de ocupação.

  1. Abra a guia Armazéns do painel da Vertex AI Vision. Acesse a guia "Armazéns".
  2. Encontre o depósito queue-warehouse na lista e clique em Ver recursos.
  3. Na seção Contagem de pessoas, defina o valor Mínimo como 1 e o valor Máximo como 5.
  4. Para filtrar os dados de vídeo processados armazenados no Media Warehouse da Vertex AI Vision, clique em Pesquisar.

a0e5766262443d6c.png

Uma visualização dos dados de vídeo armazenados que correspondem aos critérios de pesquisa no console do Google Cloud.

10. Anotar e analisar a saída usando a tabela do BigQuery

  1. No notebook, inicialize as seguintes variáveis na célula.
DATASET_ID='vision_ai_dataset'
bq_table=f'{PROJECT_ID}.{DATASET_ID}.queue-app'
frame_buffer_size=10000
frame_buffer_error_milliseconds=5
dashboard_update_delay_seconds=3
rtsp_url='rtsp://localhost:8554/seq25_h264'
  1. Agora vamos capturar os frames do fluxo rtsp usando o seguinte código:
import cv2
import threading
from collections import OrderedDict
from datetime import datetime, timezone

frame_buffer = OrderedDict()
frame_buffer_lock = threading.Lock()

stream = cv2.VideoCapture(rtsp_url)
def read_frames(stream):
  global frames
  while True:
    ret, frame = stream.read()
    frame_ts = datetime.now(timezone.utc).timestamp() * 1000
    if ret:
      with frame_buffer_lock:
        while len(frame_buffer) >= frame_buffer_size:
          _ = frame_buffer.popitem(last=False)
        frame_buffer[frame_ts] = frame

frame_buffer_thread = threading.Thread(target=read_frames, args=(stream,))
frame_buffer_thread.start()
print('Waiting for stream initialization')
while not list(frame_buffer.keys()): pass
print('Stream Initialized')
  1. Extraia o carimbo de data/hora dos dados e as informações de anotação da tabela do BigQuery e crie um diretório para armazenar as imagens de frames capturadas:
from google.cloud import bigquery
import pandas as pd

client = bigquery.Client(project=PROJECT_ID)

query = f"""
SELECT MAX(ingestion_time) AS ts
FROM `{bq_table}`
"""

bq_max_ingest_ts_df = client.query(query).to_dataframe()
bq_max_ingest_epoch = str(int(bq_max_ingest_ts_df['ts'][0].timestamp()*1000000))
bq_max_ingest_ts = bq_max_ingest_ts_df['ts'][0]
print('Preparing to pull records with ingestion time >', bq_max_ingest_ts)
if not os.path.exists(bq_max_ingest_epoch):
   os.makedirs(bq_max_ingest_epoch)
print('Saving output frames to', bq_max_ingest_epoch)
  1. Anote os frames usando o seguinte código:
import json
import base64
import numpy as np
from IPython.display import Image, display, HTML, clear_output

im_width = stream.get(cv2.CAP_PROP_FRAME_WIDTH)
im_height = stream.get(cv2.CAP_PROP_FRAME_HEIGHT)

dashdelta = datetime.now()
framedata = {}
cntext = lambda x: {y['entity']['labelString']: y['count'] for y in x}
try:
  while True:
    try:
        annotations_df = client.query(f'''
          SELECT ingestion_time, annotation
          FROM `{bq_table}`
          WHERE ingestion_time > TIMESTAMP("{bq_max_ingest_ts}")
         ''').to_dataframe()
    except ValueError as e: 
        continue
    bq_max_ingest_ts = annotations_df['ingestion_time'].max()
    for _, row in annotations_df.iterrows():
      with frame_buffer_lock:
        frame_ts = np.asarray(list(frame_buffer.keys()))
        delta_ts = np.abs(frame_ts - (row['ingestion_time'].timestamp() * 1000))
        delta_tx_idx = delta_ts.argmin()
        closest_ts_delta = delta_ts[delta_tx_idx]
        closest_ts = frame_ts[delta_tx_idx]
        if closest_ts_delta > frame_buffer_error_milliseconds: continue
        image = frame_buffer[closest_ts]
      annotations = json.loads(row['annotation'])
      for box in annotations['identifiedBoxes']:
        image = cv2.rectangle(
          image,
          (
            int(box['normalizedBoundingBox']['xmin']*im_width),
            int(box['normalizedBoundingBox']['ymin']*im_height)
          ),
          (
            int((box['normalizedBoundingBox']['xmin'] + box['normalizedBoundingBox']['width'])*im_width),
            int((box['normalizedBoundingBox']['ymin'] + box['normalizedBoundingBox']['height'])*im_height)
          ),
          (255, 0, 0), 2
        )
      img_filename = f"{bq_max_ingest_epoch}/{row['ingestion_time'].timestamp() * 1000}.png"
      cv2.imwrite(img_filename, image)
      binimg = base64.b64encode(cv2.imencode('.jpg', image)[1]).decode()
      curr_framedata = {
        'path': img_filename,
        'timestamp_error': closest_ts_delta,
        'counts': {
          **{
            k['annotation']['displayName'] : cntext(k['counts'])
            for k in annotations['stats']["activeZoneCounts"]
          },
          'full-frame': cntext(annotations['stats']["fullFrameCount"])
        }
      }
      framedata[img_filename] = curr_framedata
      if (datetime.now() - dashdelta).total_seconds() > dashboard_update_delay_seconds:
        dashdelta = datetime.now()
        clear_output()
        display(HTML(f'''
          <h1>Queue Monitoring Application</h1>
          <p>Live Feed of the queue camera:</p>
          <p><img alt="" src="{img_filename}" style="float: left;"/></a></p>
          <table border="1" cellpadding="1" cellspacing="1" style="width: 500px;">
            <caption>Current Model Outputs</caption>
            <thead>
              <tr><th scope="row">Metric</th><th scope="col">Value</th></tr>
            </thead>
            <tbody>
              <tr><th scope="row">Serving Area People Count</th><td>{curr_framedata['counts']['serving-zone']['Person']}</td></tr>
              <tr><th scope="row">Queueing Area People Count</th><td>{curr_framedata['counts']['queue-zone']['Person']}</td></tr>
              <tr><th scope="row">Total Area People Count</th><td>{curr_framedata['counts']['full-frame']['Person']}</td></tr>
              <tr><th scope="row">Timestamp Error</th><td>{curr_framedata['timestamp_error']}</td></tr>
            </tbody>
          </table>
          <p>&nbsp;</p>
        '''))
except KeyboardInterrupt:
  print('Stopping Live Monitoring')

9426ffe2376f0a7d.png

  1. Interrompa a tarefa de anotação usando o botão Parar na barra de menus do notebook.

6c19cb00dcb28894.png

  1. É possível acessar frames individuais usando o seguinte código:
from IPython.html.widgets import Layout, interact, IntSlider
imgs = sorted(list(framedata.keys()))
def loadimg(frame):
    display(framedata[imgs[frame]])
    display(Image(open(framedata[imgs[frame]]['path'],'rb').read()))
interact(loadimg, frame=IntSlider(
    description='Frame #:',
    value=0,
    min=0, max=len(imgs)-1, step=1,
    layout=Layout(width='100%')))

78b63b546a4c883b.png

11. Parabéns

Parabéns, você concluiu o laboratório!

Limpeza

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto ou mantenha o projeto e exclua cada um dos recursos.

Excluir o projeto

Excluir recursos individuais

Recursos

https://cloud.google.com/vision-ai/docs/overview

https://cloud.google.com/vision-ai/docs/occupancy-count-tutorial

Licença

Pesquisa

Como você usou este tutorial?

Apenas leitura Leitura e exercícios

Este codelab foi útil?

Muito útil Útil Não é útil

Qual foi o nível de dificuldade para seguir este codelab?

Fácil Moderada Difícil