POC de ferramenta de anotação e treino estilo Roboflow para detecção de objetos com YOLO11. Backend FastAPI + auth (fastapi-users + SQLite), painel admin e frontend Vue 3 SPA.
- Backend: FastAPI (Python 3.12) + uvicorn
- ML: Ultralytics YOLO11 / YOLO26 (PyTorch CUDA 12.8)
- Auth: fastapi-users 13+ (cookie JWT
annauth, 7 dias) - DB: SQLite via SQLAlchemy async + aiosqlite
- Pacotes Python:
uv(lockfile emuv.lock) - Frontend: Vue 3 + Vite + Vue Router + Pinia + Tailwind (JavaScript)
# uv (gerencia Python e libs)
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
# Dependências Python
uv sync
# Dependências do frontend
cd frontend && npm install && cd ..cat > .env <<EOF
AUTH_SECRET=$(openssl rand -hex 32)
COOKIE_SECURE=false
IMAGES_DIR=motores
CLASSES=objeto
EXPECTED_BOXES_PER_IMAGE=1
DEFAULT_CONF=0.30
DEFAULT_EPOCHS=100
IMG_SIZE=640
VAL_SPLIT=0.2
EOFuv run python seed_admin.py admin@local.dev senha123cd frontend && npm run build && cd ..
uv run python app.py
# ou: uv run uvicorn app:app --reload --port 8888Abra http://localhost:8888/.
# Terminal 1: backend (API e auth)
uv run python app.py
# Terminal 2: Vite (UI em :5173 com proxy para :8888)
cd frontend && npm run devAnnotator/
├── app.py # FastAPI: API, middleware auth, SPA mount em /
├── config.py # Lê .env
├── db.py # SQLAlchemy async + tabelas User / AppClass + seed
├── users.py # fastapi-users (cookie JWT, hooks de signup/reset)
├── schemas.py # UserRead / UserCreate / UserUpdate
├── trainer.py # Pipeline YOLO (treino, predição, export, merge)
├── seed_admin.py # Cria o primeiro superuser
│
├── frontend/ # Vue 3 SPA
│ ├── package.json, vite.config.js, tailwind.config.js
│ ├── index.html
│ ├── dist/ # (gerado por `npm run build`, gitignored)
│ └── src/
│ ├── main.js, App.vue, router.js, api.js, style.css
│ ├── stores/ # auth.js, theme.js (Pinia)
│ ├── composables/ # useToast.js
│ ├── components/ # Brand, ThemeToggle, Modal, ToastHost
│ ├── pages/ # Login, Signup, Forgot, Reset, Admin, Annotator
│ └── annotator/ # componentes do anotador
│ ├── ImageList.vue, Toolbar.vue
│ ├── Canvas.vue # desenhar/mover/resize de bboxes
│ ├── AnnotationList.vue
│ ├── TrainingPanel.vue # polling de /api/train/status
│ ├── ShortcutsPanel.vue
│ └── modals/ # Settings / Export / Results / Models / Merge
│
├── motores/ # IMAGES_DIR padrão (gitignored)
├── dataset/ # dataset YOLO gerado (gitignored)
├── runs/ # saídas de treino (gitignored)
├── exports/ # modelos campeões exportados (gitignored)
├── annotations.json # estado das anotações (gitignored)
├── app.db # SQLite — usuários + classes (gitignored)
├── pyproject.toml, uv.lock
└── README.md
| Variável | Padrão | Descrição |
|---|---|---|
AUTH_SECRET |
CHANGE-ME-IN-ENV |
Segredo HS256 do JWT do cookie. Troque em produção. Rotacionar invalida todas as sessões. |
COOKIE_SECURE |
false |
Em produção (HTTPS) defina true. |
DATA_DIR |
raiz do projeto | Onde o app.db é criado. Útil em hosts com volume montado (ex.: /data no HF Space). |
IMAGES_DIR |
motores |
Pasta com imagens a anotar. |
CLASSES |
objeto |
Classes iniciais — só lidas no primeiro boot para semear a tabela app_classes. Depois a fonte da verdade é o DB (gerencie em /admin). |
EXPECTED_BOXES_PER_IMAGE |
1 |
Referência visual no anotador. |
DEFAULT_CONF |
0.30 |
Confiança padrão da predição. |
DEFAULT_EPOCHS |
100 |
Épocas padrão do treino. |
IMG_SIZE |
640 |
Tamanho da imagem no treino. |
VAL_SPLIT |
0.2 |
Split de validação. |
- Cookie:
annauth(HttpOnly, SameSite=Lax, JWT HS256, validade 7 dias). - Login:
POST /auth/login(formusername+password). Logout:POST /auth/logout. - Cadastro:
POST /auth/register. Reset:/auth/forgot-password+/auth/reset-password. Em modo POC o token de reset é impresso no log do servidor (vejausers.py). - Middleware
auth_gateemapp.py: APIs (/api/*,/users/*,/images/*,/runs/*) exigem cookie válido (401se não tiver). Qualquer outro caminho serve oindex.htmlda SPA — o cliente chama/api/mee redireciona para/loginse necessário.
| Papel | Pode |
|---|---|
| Usuário ativo | Anotar, salvar, navegar, ver runs, exportar labels, treinar (/api/train), rodar predição. |
| Superuser | Acima + /admin, CRUD de usuários e classes, rotas destrutivas (/api/reset, /api/models/runs, /api/merge, /api/models/export). |
POST /auth/login,POST /auth/logout,POST /auth/registerPOST /auth/forgot-password,POST /auth/reset-passwordPOST /auth/request-verify-token,POST /auth/verify
GET /users/me,PATCH /users/meGET /users/{id},PATCH /users/{id},DELETE /users/{id}(superuser)
GET /api/admin/usersGET /api/admin/classes,POST /api/admin/classes,DELETE /api/admin/classes/{id},POST /api/admin/classes/reorder
GET /api/config— classes + defaultsGET /api/settings— leitura do.env+ classes (read-only via UI)GET /api/me— sessão atualGET /api/images,GET /images/{filename}GET /api/annotations,POST /api/annotationsPOST /api/train,GET /api/train/status,GET /api/train/resultsPOST /api/predictGET /api/exports,GET /api/modelsPOST /api/export(labels YOLO)POST /api/models/export(admin),POST /api/merge(admin)POST /api/reset(admin),DELETE /api/models/runs(admin)GET /runs/{path}— gráficos/artefatos de treino
user(id UUID, email, hashed_password, is_active, is_superuser, is_verified)
app_classes(id, name UNIQUE, position)
Tabelas criadas no startup via lifespan → create_db_and_tables(). CLASSES do .env semeia app_classes apenas no primeiro boot.
- Tipografia: IBM Plex Sans (UI) + IBM Plex Mono (dados, badges, atalhos).
- Accent: âmbar (
#d97706light /#f59e0bdark). - Background com malha sutil de grade 32px, borders hairline.
- Toggle dark/light persistido em
localStorage.theme. Antes do paint, um script no<head>aplicadata-themeno<html>para evitar flash.
# Criar outro admin
uv run python seed_admin.py outro@local.dev senha
# Resetar tudo (apaga usuários e classes)
rm app.db && uv run python seed_admin.py admin@local.dev senha123
# Backend com auto-reload
uv run uvicorn app:app --host 0.0.0.0 --port 8888 --reload
# Build do frontend
cd frontend && npm run build
# Dev do frontend (hot reload)
cd frontend && npm run dev
# Limpar artefatos do Vite
rm -rf frontend/dist frontend/node_modulesCOOKIE_SECURE=falsepor padrão para funcionar emhttp://localhost. Em produção sirva via HTTPS e troque paratrue.POST /auth/registerestá aberto (padrão do fastapi-users). Para travar, remova oget_register_routeremapp.pye crie usuários só via/admin.AUTH_SECRET: rotacionar invalida todas as sessões existentes.- Em modo POC, links de reset de senha são impressos no log do servidor (veja
users.py). Para produção, pluguefastapi-mail+ Resend/Mailtrap.