Title: 8. Demonstração de Deteção de Objetos AR + IA
Course: AI Projects for Students (PT) > Section: IA COM AR/VR

8. Demonstração de Deteção de Objetos AR + IA

8.1 Visão Geral do Projeto

Esta é uma aplicação de Realidade Aumentada WebXR para o Meta Quest 3 que combina:

- AR passthrough — a sala real é visível através das câmaras dos auscultadores
- Interação hit-test — um cursor virtual que se encaixa em superfícies do mundo real
- Deteção de objetos por IA — TensorFlow.js reconhece objetos no feed da câmara e exibe etiquetas no espaço 3D
- Compreensão de Cena — A deteção de planos WebXR visualiza superfícies detetadas (chão, paredes, mesa)

URL em direto: http://92.113.18.92/

Em fila indiana:

index.html

8.2 Arquitetura do Projeto

📄 index.html (tudo-em-um)

│

├── HTML → canvas + botão UI + elemento de vídeo oculto

├── CSS → fundo transparente, interface sobreposta

└── JavaScript

├── Three.js → motor de renderização 3D (cena, câmara, materiais)

├── WebXR API → sessão AR, hit-test, deteção de plano

└── TF.js  → Inferência de IA (modelo coco-ssd)

Porquê Three.js pura (sem A-Frame)?

Durante o desenvolvimento, descobriu-se que o A-Frame tem o seu próprio loop interno de renderização que entra em conflito com uma sessão WebXR explícita de ar imersiva. O A-Frame inicia uma  sessão de immersive VR (opaca, sem passthrough) em vez de immersive-ar. Mudar para Three.js puro deu-nos controlo direto tanto sobre o tipo de sessão como sobre o loop de renderização.

8.3 Stack de Tecnologia

Tecnologia | Versão | Função
Three.js | 0.157.0 | Renderização 3D, geometria, materiais
WebXR Device API | Nativo do navegador | Sessão AR, teste de acerto, deteção de avião
TensorFlow.js | 4.15.0 | Motor de inferência ML no navegador
COCO-SSD | 2.2.3 | Modelo de deteção de objetos pré-treinado
getUserMedia API | Nativo do navegador | Stream de câmara para análise TF.js

8.4 Fluxo de Aplicação

Carregamento da página

→ modelo TF.js (coco-ssd) carrega de forma assíncrona (~6MB)

→ Verificar: navigator.xr.isSessionSupported('immersive-ar')

→ botão "Enter AR" torna-se ativo

O utilizador clica em "Enter AR"

→ Pedido: navigator.xr.requestSession('immersive-ar', {...})

→ Quest ativa o Passthrough (câmara visível através de auscultadores)

→ Simultaneamente: getUserMedia() → stream de câmara para TF.js

→ renderer.setAnimationLoop() inicia (ciclo de renderização XR)

Cada frame (~72fps no Quest 3)

o   scene.background = null (garante transparência)

o   Teste de acerto → atualiza a posição do cursor

o   A deteção de planos → atualiza a visualização da superfície

o   A cada 2 segundos → runDetection() → inferência de IA

o   renderer.render(cena, câmara)

8.5 Explicação detalhada do código

1. HTML Estrutura (linhas 1–78)

Um elemento oculto <vídeo> que recebe o fluxo getUserMedia(). TensorFlow.js usa-o como entrada para inferência — nunca é apresentado ao utilizador, apenas analisado.

Uma camada de interface sobreposta a flutuar acima da Three.js tela. Eventos de ponteiro: nenhum no contentor mas eventos de ponteiro: todos apenas no botão — para que a sobreposição não bloqueie interações 3D.

2. Three.js Inicialização (linhas 84–109)

alpha: true cria uma tela WebGL com um canal alfa transparente — isto é um pré-requisito para passthrough. xr.enabled = true ativa a integração integrada com WebXR da Three.js.

A câmara é adicionada à cena. Isto é importante porque os objetos ligados à câmara (entidades filhas) precisam de estar na hierarquia da cena.
A geometria do anel é rodada -90° no eixo X, ficando horizontalmente plana nas superfícies detetadas. Sem esta rotação, ficaria na vertical.

3. makeLabel() — Texto Sprite (linhas 113–138)

TRÊS. Sprite é um objeto que está sempre virado para a câmara (outdoors) — ideal para rótulos no espaço 3D. É desenhado numa tela HTML, convertido numa CanvasTexture e aplicado a um SpriteMaterial.

depthTest: false garante que a etiqueta é sempre visível mesmo que esteja geometricamente "atrás" de outro objeto 3D.

4. detecçãoTo3D() —  Projeção 2D → 3D (linhas 140–163)

Este é o núcleo matemático da integração IA-AR.

Exemplo: Se a IA detetar uma cadeira no terço esquerdo do vídeo, nx ≈ -0,17. Isto converte-se num ângulo negativo (à esquerda do eixo do olhar). A etiqueta é colocada 1,8m à frente da câmara nessa direção.

5. makeBox3D() e bbox2dSize3D() — Caixas Delimitadoras 3D (linhas 165–182)

EdgesGeometry + LineSegments renderiza apenas as arestas de uma caixa — o efeito de uma borda fina em wireframe sem superfície preenchida.
A  fórmula totalW é a projeção de perspetiva padrão: à distância d, a largura visível depende do FOV. A partir disto, deduzimos quantos metros correspondem aos píxeis da caixa delimitadora.

6. runDetection() — Pipeline de Inferência de IA (linhas 205–247)

cocoModel.detect(tfVideo) aceita diretamente um elemento <vídeo> — TF.js lê internamente dados de píxeis do quadro de vídeo atual.

Formato Pred.bbox : [X, Y, Largura, Altura] em pixels.

Cada deteção cria um par JS (sprite, caixa) que dura 4 segundos.

7. Sessão WebXR — Detalhes Chave (linhas 274–285)

Porquê a realidade aumentada imersiva e não a realidade virtual imersiva?

- immersive-VR = fundo preto opaco (experiência de capacete VR)
- immersive-ar = câmara passthrough + conteúdo virtual sobreposto

o espaço de referência do piso local fixa o sistema de coordenadas ao piso da sala — y=0 está ao nível do piso.

O teste de acerto e  a deteção de aviões são opcionais porque não são suportados em todos os dispositivos e versões do navegador — declará-los como opcionais impede que a sessão falhe se não estiverem disponíveis

8. Loop de Renderização (linhas 335–395)

Porque setAnimationLoop e não requestAnimationFrame?

O loop de renderização Three.js XR deve estar integrado com o callback de frames WebXR. renderer.setAnimationLoop() liga-se automaticamente à sessão XR quando renderer.xr.enabled = true. A alternativa (chamar manualmente session.requestAnimationFrame()) requer chamar o renderer.render() manualmente, mas corre o risco de perder as matrizes corretas de visualização XR que Three.js se aplicam internamente.

scene.background = null deve ser chamado em cada frame porque Three.js pode redefinir este valor internamente durante certas operações.

9. Deteção de Planos (linhas 355–378)

plane.planeSpace é um XRSpace que rastreia a superfície física. frame.getPose() devolve a sua pose no espaço de referência escolhido.

O mapa DetectedPlanes (Map<XRPlane, TRÊS. Mesh>) impede a criação de malhas duplicadas para a mesma superfície entre frames.

8.6 Limitações Conhecidas

Limitação | Razão
As etiquetas de IA não estão alinhadas perfeitamente com o objeto | As câmaras passthrough do Quest e o getUserMedia entregam diferentes streams com diferentes FOV e calibração ótica
A deteção de planos requer que a Configuração da Sala de Missões seja concluída | O headset deve ter escaneado a sala anteriormente
O getUserMedia pode ser recusado em alguns dispositivos | Depende das permissões do navegador
A inferência de IA não é em tempo real (executa-se a cada 2 segundos) | O Coco-SSD é um modelo relativamente pesado; Alternativas mais leves (SSD MobileNet) dariam maior débito

8.7 Código-fonte

https://github.com/bciric1/ARVR

http://vr.aiforvet.eu/