Files
jhonny-editor/code/engine/integracoes/visual/apple_vision.py
T
João Henrique b9bf3b2863 feat: criado repositório jhonny-editor no Gitea
criado repositório jhonny-editor no Gitea
adicionado script admin/deploy.command com commit automático
atualizado admin/DEV-NOTES.md com template limpo

Resumo:
- 48 arquivos alterados
- 26 novos
- 19 modificados
- 3 removidos

 22 files changed, 658 insertions(+), 568 deletions(-)

Arquivos:
  - AGENTS.md
  - admin/DEV-NOTES.md
  - admin/OpenCut.command
  - admin/commit.command
  - admin/deploy.command
  - admin/update.command
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - code/engine/ARQUITETURA.md
  - code/engine/arquitetura/README.md
  - code/engine/gerar_relatorio_timeline.py
  - code/engine/integracoes/apple_speech/apple_speech_transcriber.swift
  - code/engine/integracoes/apple_speech/provider_de_transcricao_apple.py
  - code/engine/integracoes/midia/__init__.py
  - code/engine/integracoes/whisper/provider_de_transcricao_local.py
  - code/engine/scanner/__init__.py
  - code/engine/scanner/coordenacao/__init__.py
  - code/engine/scanner/descoberta/__init__.py
  - code/engine/scanner/modelos.py
  - code/engine/scanner/transcricao_da_timeline.py
  - code/src/tools/discovery.ts
  - :memory:.ses
  - admin/Jhonny.command
  - admin/inativos/OpenCut.command
  - admin/inativos/update.command
  - code/docs/glossario-analise-emocional.md
  - code/docs/levantamento-apple-vision-e-apple-intelligence.md
  - code/docs/levantamento-ferramentas-analise-visual-local.md
  - code/engine/arquitetura/biblioteca-inteligente-de-videos.md
  - code/engine/executar_scanner.py
  - code/engine/integracoes/huggingface/
  - code/engine/integracoes/midia/extracao_de_metadados.py
  - code/engine/integracoes/visual/
  - code/engine/requirements-visual.txt
  - code/engine/scanner/configuracao_visual.py
  - code/engine/scanner/descoberta/descoberta_de_arquivos.py
  - code/engine/scanner/metadados.py
  - code/engine/scanner/relatorio_visual.py
  - code/engine/scanner/retakes/
  - code/engine/scanner/visual.py
  - code/engine/testes/test_analise_visual_local.py
  - code/engine/testes/test_arquivos_e_metadados.py
  - code/engine/testes/test_provider_de_transcricao_apple.py
  - code/relatorios/analise-brools/
  - code/relatorios/analise-visual/
  - code/relatorios/audio/arquivos/
  - code/relatorios/transcricao-timeline.md
2026-09-08 16:13:27 -04:00

127 lines
6.6 KiB
Python

"""Adapter Python para o runner Swift que concentra Vision e Apple Intelligence."""
import json
import os
from pathlib import Path
import subprocess
import tempfile
from typing import Any, Callable
from ...scanner.modelos import EvidenciaVisual
from .contratos import AnalisadorDeFrame, AnalisadorDeSequenciaDeQuadros
from .modelos import QuadroDeVideo
RECURSOS_PADRAO = (
"faces", "qualidade_facial", "pessoas", "pose", "maos", "ocr", "categorias",
"estetica", "codigos", "horizonte", "retangulos", "contornos", "segmentacao_de_pessoas",
)
class ExecutorDoRunnerApple:
"""Compila o runner Swift quando necessário e o executa em processo isolado."""
def __init__(self, fonte: str | Path | None = None, compilador: str = "swiftc",
diretorio_de_build: str | Path | None = None) -> None:
self.fonte = Path(fonte) if fonte else Path(__file__).with_name("apple_vision_runner.swift")
self.compilador = compilador
self.diretorio_de_build = Path(diretorio_de_build) if diretorio_de_build else (
Path(tempfile.gettempdir()) / "jhonny-apple-vision")
def executar(self, carga: dict[str, Any], timeout: float) -> dict[str, Any]:
executavel = self._garantir_compilado()
processo = subprocess.run([str(executavel)], input=json.dumps(carga), capture_output=True,
text=True, timeout=timeout)
if processo.returncode != 0:
detalhe = processo.stderr.strip() or "runner Apple terminou sem diagnóstico"
raise RuntimeError(f"Runner Apple Vision falhou: {detalhe}")
try:
return json.loads(processo.stdout)
except json.JSONDecodeError as exc:
raise RuntimeError(f"Runner Apple Vision devolveu JSON inválido: {processo.stdout!r}") from exc
def _garantir_compilado(self) -> Path:
if not self.fonte.is_file():
raise FileNotFoundError(f"Fonte do runner Apple não encontrada: {self.fonte}")
self.diretorio_de_build.mkdir(parents=True, exist_ok=True)
executavel = self.diretorio_de_build / "apple-vision-runner"
if not executavel.exists() or executavel.stat().st_mtime < self.fonte.stat().st_mtime:
ambiente = os.environ | {"CLANG_MODULE_CACHE_PATH": str(self.diretorio_de_build / "module-cache")}
processo = subprocess.run([self.compilador, "-parse-as-library", str(self.fonte), "-o", str(executavel)],
capture_output=True, text=True, timeout=120, env=ambiente)
if processo.returncode != 0:
raise RuntimeError(f"Não foi possível compilar runner Apple Vision: {processo.stderr.strip()}")
return executavel
class AnalisadorAppleVisionNativo(AnalisadorDeFrame):
"""Módulo profundo: pede fatos visuais nativos e opcionalmente interpretação local.
A interface recebe somente um `QuadroDeVideo`; Vision, Foundation Models,
Swift, compilação e erros nativos permanecem atrás deste adapter.
"""
nome = "apple_vision"
def __init__(self, recursos: tuple[str, ...] = RECURSOS_PADRAO,
interpretar_com_apple_intelligence: bool = True,
executor: ExecutorDoRunnerApple | None = None,
timeout_em_segundos: float = 90.0) -> None:
self.recursos = recursos
self.interpretar_com_apple_intelligence = interpretar_com_apple_intelligence
self.executor = executor or ExecutorDoRunnerApple()
self.timeout_em_segundos = timeout_em_segundos
def analisar(self, quadro: QuadroDeVideo) -> list[EvidenciaVisual]:
if quadro.caminho is None:
raise ValueError("Apple Vision requer que o frame tenha caminho persistido em disco.")
dados = self.executor.executar({"frame": str(quadro.caminho), "recursos": list(self.recursos),
"resumir": self.interpretar_com_apple_intelligence},
self.timeout_em_segundos)
evidencias = [self._converter(item, quadro) for item in dados.get("evidencias", [])]
avisos = dados.get("avisos", [])
if avisos:
evidencias.append(EvidenciaVisual("diagnostico_apple_vision", quadro.timestamp, quadro.timestamp,
{"avisos": avisos, "recursos_solicitados": list(self.recursos)}, provider=self.nome))
return evidencias
def _converter(self, item: dict[str, Any], quadro: QuadroDeVideo) -> EvidenciaVisual:
return EvidenciaVisual(item["tipo"], quadro.timestamp, quadro.timestamp, item["valor"],
confianca=item.get("confianca"), provider=self.nome,
modelo=item.get("modelo"))
class AnalisadorSequenciaAppleVisionNativo(AnalisadorDeSequenciaDeQuadros):
"""Compara frames com feature prints nativos sem expor o protocolo Swift."""
nome = "apple_vision_sequencia"
def __init__(self, executor: ExecutorDoRunnerApple | None = None,
timeout_em_segundos: float = 90.0) -> None:
self.executor = executor or ExecutorDoRunnerApple()
self.timeout_em_segundos = timeout_em_segundos
def analisar(self, quadros: list[QuadroDeVideo]) -> list[EvidenciaVisual]:
if len(quadros) < 2:
return []
if any(quadro.caminho is None for quadro in quadros):
raise ValueError("Apple Vision de sequência requer frames persistidos em disco.")
dados = self.executor.executar({"frames": [str(quadro.caminho) for quadro in quadros],
"recursos": [], "resumir": False}, self.timeout_em_segundos)
evidencias: list[EvidenciaVisual] = []
for item in dados.get("evidencias", []):
valor = dict(item["valor"])
inicio = int(valor.pop("frame_inicial", 0))
fim = int(valor.pop("frame_final", inicio + 1))
if inicio < 0 or fim >= len(quadros) or fim < inicio:
raise RuntimeError("Runner Apple Vision devolveu índices temporais inválidos.")
evidencias.append(EvidenciaVisual(item["tipo"], quadros[inicio].timestamp,
quadros[fim].timestamp, valor,
confianca=item.get("confianca"), provider=self.nome,
modelo=item.get("modelo")))
avisos = dados.get("avisos", [])
if avisos:
evidencias.append(EvidenciaVisual("diagnostico_apple_vision", quadros[0].timestamp,
quadros[-1].timestamp, {"avisos": avisos}, provider=self.nome))
return evidencias