App Vertex AI Vision Queue Detection

1. Objetivos

Visão geral

O foco deste codelab é a criação de um aplicativo da Vertex AI Vision de ponta a ponta para monitorar o tamanho da fila usando filmagens 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 que são servidas 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 do Jupyter.
  • Como usar o modelo de análise de ocupação e seus diferentes recursos.
  • Como pesquisar vídeos no armazenamento de mídia do 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 fazer anotações no 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 quiser manter os recursos criados usando este procedimento, crie um projeto em vez de selecionar um atual. 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. Acessar "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, uma conta de serviço para o guia de início rápido.
  4. Clique em Criar e continuar.
  5. Para fornecer acesso ao projeto, conceda os seguintes papéis à conta de serviço:
  • Vision AI > Editor da Vision AI
  • Compute Engine > Administrador de instâncias do Compute (Beta)
  • BigQuery > Administrador do BigQuery .

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

  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 notebook do Jupyter

Antes de criar um app no Occupancy Analytics, é preciso registrar um fluxo que poderá ser usado posteriormente pelo app.

Neste tutorial, você vai criar uma instância de notebook do Jupyter que hospeda um vídeo e enviar esses dados de streaming de 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. Usaremos esse 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

Crie 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. Insira o nome do notebook do Jupyter. Para mais informações, consulte a 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. Depois disso, clique em ABRIR O JUPYTERLAB.

4. Configure um notebook para fazer streaming de vídeo

Antes de criar um app no Occupancy Analytics, é preciso registrar um fluxo que poderá 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 amostra e use o vaictl para transmitir os dados do 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 fluxo, digite ‘queue-stream'.
  4. Na 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 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

Faça streaming de vídeo da VM e processe dados no seu stream

  1. Para enviar esse arquivo de vídeo local ao stream 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 projeto do Google Cloud.
  • LOCATION: o ID do seu 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 servidor rtsp-simple-server em que fazemos o streaming do 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. 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 ingestão de streaming estiver disponível, selecione o stream de stream da fila para ver o feed de vídeo na guia Streams do painel da Vertex AI Vision.

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:

  • 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.

Criar 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. Acessar a guia "Aplicativos"
  3. Clique no botão Criar. 21ecba7a23e9979e.png
  4. Digite queue-app como o nome do app e escolha sua região.
  5. Clique em Criar.

Adicionar nós dos componentes 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 sobre dados ingeridos.
  3. Nó de armazenamento: o warehouse de mídia que armazena vídeos processados e funciona como um repositório de metadados. Os armazenamentos de metadados incluem informações analíticas sobre dados de vídeo ingeridos e informações inferidas pelos modelos de IA.

Adicione nós de componentes ao seu app no console.

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

Isso leva você à visualização do 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, selecione Adicionar streams.
  3. No menu Adicionar 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ó 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. Mantenha as seleções padrão, Pessoas. Desmarque a opção Veículos se 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 naquela zona. Rotule a zona adequadamente

50281a723650491f.png

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

2bf0ff4d029d29eb.png

  1. Adicione configurações de tempo de permanência para detectar congestionamento 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 do Vertex AI Warehouse para abrir o menu e em Conectar warehouse.
  3. No menu Conectar warehouse, selecione Criar novo warehouse. Nomeie o armazenamento como queue-warehouse e defina a duração do TTL como 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.

É possível criar sua própria tabela do BigQuery e especificá-la ao adicionar um conector do BigQuery ao app ou permitir que a plataforma de apps Vertex AI Vision crie a tabela automaticamente.

Criação automática de tabelas

Se você permitir que a plataforma de apps Vertex AI Vision crie a tabela automaticamente, especifique 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 houver uma tabela com o mesmo nome no mesmo conjunto de dados, não será feita nenhuma criação automática.
  1. Abra a guia Aplicativos do painel da Vertex AI Vision. Acessar a guia "Aplicativos"
  2. Selecione Ver app ao lado do nome do seu app 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. Acessar a guia "Aplicativos"
  2. Selecione Ver aplicativo ao lado do aplicativo queue-app na lista.
  3. Na página do 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 vários 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. Acessar a guia Warehouses
  2. Encontre o depósito em fila na lista e clique em Ver recursos.
  3. Na seção Contagem de pessoas, defina o valor Mín. como 1 e o valor Máx. como 5.
  4. Para filtrar 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 os resultados com 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 stream 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 e as informações de anotação dos dados 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 este 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. Pare a tarefa de anotação usando o botão Parar 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?

Somente leitura Ler e concluir os exercícios

Este codelab foi útil?

Muito útil Razoavelmente útil Não é útil

Foi fácil seguir este codelab?

Fácil Moderado Difícil