App Vertex AI Vision Queue Detection

1. Objetivos

Visão geral

Este codelab vai se concentrar na criação de um aplicativo completo da Vertex AI Vision 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 as seguintes informações:

  • Conte o número de pessoas na fila.
  • Conte o número de pessoas que estão sendo 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 transferir o stream para o Vertex AI Vision usando o vaictl em um notebook do 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 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 o Compute Engine, a API Vertex, a API Notebook e a API Vision AI. Ativar as APIs

Crie 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 o início rápido.
  4. Clique em Criar e continuar.
  5. Para conceder acesso ao projeto, conceda os seguintes papéis à conta de serviço:
  • Vision AI > Editor da Vision AI
  • Compute Engine > Administrador da instância do Compute (Beta)
  • BigQuery > Administrador do BigQuery .

Na lista Selecionar um 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 Ocupação por Analytics, você precisa registrar um stream que possa ser usado posteriormente pelo app.

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

  1. Executar o servidor rtsp como um processo em segundo plano
  2. Executar o comando vaictl como processo em segundo plano
  3. Executar consultas e processar código 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 de notebook do Jupyter é criar o notebook com nossa conta de serviço criada na etapa anterior.

  1. No console, acesse a página da Vertex AI. Acesse 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. Digite o nome do notebook do Jupyter. Saiba mais em Convenção de nomenclatura de recursos.

b4dbc5fddc37e8d9.png

  1. Clique em OPÇÕES AVANÇADAS
  2. Role para baixo até Seções 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 criada na etapa anterior. Clique em Criar.

ec0b9ef00f0ef470.png

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

4. Configurar um notebook para transmitir vídeos

Antes de criar um app no Ocupação por Analytics, você precisa registrar um stream que possa ser usado posteriormente pelo app.

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

Fazer o download da ferramenta de linha de comando vaictl

  1. Na instância aberta do JupyterLab, abra um Notebook na tela de início.

a6d182923ae4ada3.png

  1. Faça o download da ferramenta de linha de comando Vertex AI Vision (vaictl), a ferramenta de linha de comando do servidor rtsp e a 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 exemplo e use o vaictl para transmitir os dados de vídeo para o app de análise de ocupação.

Registrar uma nova transmissão

  1. Clique na guia "Streams" no painel esquerdo da Vertex AI Vision.
  2. Clique no botão "Registrar" na parte de cima eba418e723916514.png
  3. No 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 exemplo para a VM

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

Transmitir vídeos do VM e ingerir dados no stream

  1. Para enviar esse arquivo de vídeo local para o fluxo de entrada do app, use o comando a seguir na célula do notebook. Faça as seguintes substituições de variáveis:
  • PROJECT_ID: é o ID do projeto do Google Cloud.
  • LOCATION: o ID do seu local. Por exemplo, us-central1. Para mais informações, consulte Locais na nuvem.
  • 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 o arquivo de vídeo é transmitido por streaming 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. Use 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 fazer streaming do vídeo do URI do servidor rtsp para o stream "queue-stream" da Vertex AI Vision 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 vídeo que aparece no painel.

Depois que a transferência de streaming estiver disponível, selecione o fluxo de fila na guia Streams do painel da Vertex AI Vision para acessar o feed de vídeo.

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 é processado 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 do app, é 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 streaming que recebe 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 repositório de mídia que armazena vídeos processados e serve como repositório de metadados. Os repositórios 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 em gráfico 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 Add streams, escolha queue-stream.
  4. Para adicionar o stream ao gráfico do app, clique em Adicionar streams.

Adicionar um nó de processamento de dados

  1. Para adicionar o nó de 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 a opção Veículos se ela 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 Polígono para contar as pessoas nessa zona. Rotular a zona corretamente

50281a723650491f.png

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

2bf0ff4d029d29eb.png

  1. Adicione configurações para o tempo de permanência para detectar congestionamentos clicando na caixa de seleção.

c067fa256ca5bb96.png

Adicionar um nó de armazenamento de dados

  1. Para adicionar o nó de destino de saída (armazenamento), selecione a opção VIsion AI Warehouse 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 novo 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 a saída à tabela do BigQuery

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

Você pode criar sua própria tabela do BigQuery e especificar essa tabela ao adicionar um conector do BigQuery ao app ou permitir que a plataforma do app 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, poderá especificar essa opção ao adicionar o nó do conector do BigQuery.

As condições de conjunto de dados e tabela a seguir 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 vai acontecer.
  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 em branco.

ee0b67d4ab2263d.png

  1. Em Metadados da loja de:selecione somente 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 seu 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 aplicativo ao lado do aplicativo queue-app na lista.
  3. Na página Studio, clique no botão Deploy.
  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. Depois que 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 em vídeo no armazenamento

Depois de ingerir dados de vídeo no app de processamento, é possível visualizar os dados de vídeo analisados e pesquisá-los com base nas informações de análise de ocupação.

  1. Abra a guia Warehouses do painel da Vertex AI Vision. Acesse a guia "Armazenamentos"
  2. Encontre o depósito "queue-warehouse" na lista e clique em View assets.
  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 fotogramas 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. Use o seguinte código para anotar os frames:
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 Stop na barra de menus do notebook.

6c19cb00dcb28894.png

  1. É possível revisitar 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 Razoavelmente útil Não foi útil

Qual foi a facilidade de seguir este codelab?

Fácil Moderado Difícil