Metadata-Version: 2.4
Name: reconstruct3d
Version: 0.1.2
Summary: Pipeline offline de reconstrucción 3D desde video monocular: Structure-from-Motion incremental + Bundle Adjustment + densificación MVS + mallado CGAL, con front-ends de features intercambiables (SIFT, ORB, SuperPoint+LightGlue).
Project-URL: Homepage, https://github.com/FernandoUs/Template-SLAM
Project-URL: Repository, https://github.com/FernandoUs/Template-SLAM
Project-URL: Issues, https://github.com/FernandoUs/Template-SLAM/issues
Author-email: BlackMonkcr <aaron.navarro@utec.edu.pe>
License: MIT
License-File: LICENSE
Keywords: 3d-reconstruction,bundle-adjustment,cgal,computer-vision,mvs,opencv,photogrammetry,point-cloud,sfm,structure-from-motion
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Requires-Python: >=3.10
Requires-Dist: numpy>=1.26
Requires-Dist: opencv-contrib-python>=4.8
Requires-Dist: pandas>=2.0
Requires-Dist: scipy>=1.11
Requires-Dist: tqdm>=4.66
Provides-Extra: api
Requires-Dist: fastapi>=0.110; extra == 'api'
Requires-Dist: httpx>=0.27; extra == 'api'
Requires-Dist: python-multipart>=0.0.9; extra == 'api'
Requires-Dist: uvicorn[standard]>=0.27; extra == 'api'
Provides-Extra: spglue
Requires-Dist: lightglue; extra == 'spglue'
Requires-Dist: torch>=2.2; extra == 'spglue'
Requires-Dist: torchvision>=0.17; extra == 'spglue'
Description-Content-Type: text/markdown

# Pipeline de Reconstrucción 3D (SfM offline + Densificación MVS)

Pipeline **offline** que reconstruye una escena 3D a partir de un **video
monocular**: estima la trayectoria de la cámara mediante Structure-from-Motion
(SfM) incremental, refina con Bundle Adjustment y genera una nube de puntos
—primero rala (*sparse*) y luego densa (*dense*)—.

> **Nota sobre el nombre:** aunque el repo se llama "Template-SLAM", esto es SfM
> offline + densificación MVS, **no SLAM en tiempo real**. No hay loop-closure ni
> mapeo online. Ver [CLAUDE.md](CLAUDE.md) para el detalle técnico.

## Características

- **3 front-ends de features intercambiables:**
  - `sift` (default) — SIFT + BFMatcher (L2). Solo CPU, sin dependencias pesadas.
  - `orb` — ORB + BFMatcher (Hamming). Más rápido, menos preciso.
  - `spglue` — SuperPoint + **LightGlue** (deep learning, CPU/GPU). Opcional,
    requiere `torch`.
- **Bundle Adjustment** local (por ventana, durante el tracking) y global.
- **Densificación MVS-lite** por stereo de dos vistas (StereoSGBM) con fusión
  multi-vista.
- **Mallado con CGAL** ([cgal_mesh/](cgal_mesh/)) — reconstruye una malla
  triangulada desde la nube densa (Advancing Front o Poisson).
- **Visor POV interactivo** que proyecta los puntos 3D sobre el video original.
- **Orquestador único** ([pipeline.py](pipeline.py)) — todo el pipeline en un
  solo comando.
- **Calibración desde video** ([calibrate.py](calibrate.py)) — estima `K` de tu
  dispositivo a partir de un video de un tablero de ajedrez y crea/actualiza
  `camera.json`.
- **Paralelización** (`--jobs`) en extracción, matching y densificación.
- **Reconstrucción por chunks** ([chunked.py](chunked.py)) — para videos largos:
  parte el video en fragmentos solapados, los reconstruye en paralelo y fusiona
  las nubes alineándolas por el solape (similaridad Sim(3)).

## Requisitos

- **[uv](https://docs.astral.sh/uv/)** para gestionar el entorno y dependencias.
- Python ≥ 3.10 (uv lo instala automáticamente según `.python-version`).
- **Solo para el mallado** (`mesh`): dependencias nativas de CGAL (no las instala
  uv). En macOS: `brew install cgal cmake eigen boost gmp mpfr`. En Debian/Ubuntu:
  `sudo apt install libcgal-dev cmake libeigen3-dev`. El resto del pipeline
  funciona sin ellas.

## Instalación

```bash
# Dependencias base (front-ends sift / orb):
uv sync

# Opcional: front-end spglue (SuperPoint + LightGlue, instala torch):
uv sync --extra spglue
```

`uv` crea el entorno virtual, **instala el paquete `reconstruct3d`** (editable) y
resuelve todo desde `pyproject.toml`. No hace falta activar nada: usa
`uv run <comando>`. Queda disponible el CLI instalado `reconstruct3d` (equivalente
al shim `python pipeline.py`).

## Uso como librería (API Python)

El pipeline se puede usar desde código con la clase `Pipeline`:

```python
from reconstruct3d import Pipeline

pipe = Pipeline("outputs/run1", frontend="sift", camera="camera.json", jobs=0)
pipe.extract("video.mp4", k_skip=15, m_window=4)
pipe.init()
pipe.track()
pipe.bundle_adjust()
pipe.dense("video.mp4")
pipe.mesh(method="afront")
print(pipe.artifacts())   # {'extract': '.../sfm_data.pkl', 'dense': '.../dense_cloud.ply', ...}
```

O todo de una vez, eligiendo etapas y recibiendo progreso por callback:

```python
from reconstruct3d import Pipeline

def on_event(ev):                      # ev = {stage, status, message, ...}
    print(ev["stage"], ev["status"])

pipe = Pipeline("outputs/run1", on_event=on_event)
pipe.run("video.mp4",
         stages=["extract", "init", "track", "dense"],
         config={"extract": {"k_skip": 15, "m_window": 4}})
```

Atajo: `reconstruct3d.run_all(video, out_dir, stages=..., config=...)`.

## Uso rápido (CLI)

Coloca tu video en `data/` y ejecuta el pipeline completo:

```bash
uv run reconstruct3d all data/mi_video.mp4      # o: uv run python pipeline.py all ...
```

Esto encadena: **extract → init → track → ba → dense → mesh**. Los artefactos
quedan en `outputs/sift/` (o `outputs/<frontend>/`). El mallado se omite
automáticamente si CGAL no está instalado (o con `--no-mesh`).

Con otro front-end, o saltando etapas:

```bash
uv run python pipeline.py all data/mi_video.mp4 --frontend spglue
uv run python pipeline.py all data/mi_video.mp4 --no-ba --no-dense   # solo sparse
uv run python pipeline.py all data/mi_video.mp4 --view               # abre el visor al final
```

## Cámara / calibración (por dispositivo)

La matriz intrínseca `K` **depende del dispositivo con el que grabaste**, así que
es configurable. Copia [camera.example.json](camera.example.json), ajústalo a tu
cámara y pásalo en `extract` (o en `all`):

```bash
uv run python pipeline.py all data/mi_video.mp4 --camera camera.json
```

La config se **persiste** en `sfm_data.pkl` y `map_state.npy` y se **propaga
automáticamente** a todas las etapas — solo la indicas una vez. Sin `--camera`,
se usan los valores por defecto (iPhone @ 540×960).

Formato del JSON (atajo `fx/fy/cx/cy`, o matriz `K` completa):

```json
{
  "fx": 801.25, "fy": 801.25, "cx": 188.67, "cy": 390.22,
  "width": 540, "height": 960,
  "dist_coeffs": [0, 0, 0, 0, 0]
}
```

`width`/`height` es la resolución a la que `K` está calibrada: el video se
redimensiona a ese tamaño antes de extraer features. Si das `fx/fy/cx/cy`, deben
corresponder a esa resolución.

### Calibrar K automáticamente desde un video

Si no conoces `K`, grábala: graba un video moviendo un **tablero de ajedrez**
frente a la cámara (cubriendo zonas y ángulos) y calibra:

```bash
# Genera/actualiza camera.json (tablero de 9x6 esquinas internas):
uv run python pipeline.py calibrate data/calib.mp4 --board 9x6 --square 0.025
# Revisión manual de cada vista (ventana OpenCV):
uv run python pipeline.py calibrate data/calib.mp4 --interactive
```

También puedes calibrar **en el mismo comando** de reconstrucción, o dejar que el
pipeline lo haga solo:

```bash
# Calibra y luego reconstruye, en un paso:
uv run python pipeline.py all data/video.mp4 --calibrate data/calib.mp4

# Auto-detección: si no pasas --camera, el pipeline usa ./camera.json si existe,
# o busca un video con 'calib' en el nombre (en data/calib/, data/ o .) y calibra.
uv run python pipeline.py all data/video.mp4
```

Calibrar fija `proc_size` a la resolución nativa del video de calibración, así que
el punto principal queda centrado y `K` siempre es coherente con la resolución.

## Paralelización (`--jobs`)

La extracción de features, el matching por pares y la densificación MVS están
paralelizados con hilos. Controla los hilos con `--jobs` (`0`=auto=nº de CPUs,
`1`=secuencial):

```bash
uv run python pipeline.py extract data/video.mp4 --jobs 8
uv run python pipeline.py dense data/video.mp4 --jobs 8
uv run python pipeline.py all data/video.mp4 --jobs 8
```

Notas: los detectores de OpenCV no son thread-safe compartidos, así que cada hilo
usa su propia instancia (resultados **idénticos** al modo secuencial). Con
`--frontend spglue` el paralelismo se fuerza a 1 hilo (torch ya paraleliza
internamente y su modelo no es thread-safe).

## Videos largos: reconstrucción por chunks

Para videos largos, reconstruir todo de una vez es lento y acumula deriva. El modo
`chunked` parte el video en fragmentos **solapados**, reconstruye cada uno de forma
**independiente y en paralelo** (procesos), y fusiona las sub-nubes alineándolas
por los frames del solape (similaridad Sim(3) vía Umeyama sobre los centros de
cámara compartidos):

```bash
uv run python pipeline.py chunked data/video.mp4 \
    --chunk 80 --overlap 20 --chunk-jobs 4
```

- `--chunk` / `--overlap`: tamaño y solape del fragmento, en frames muestreados.
  El solape debe ser suficiente para alinear (≥4 frames; por defecto 20).
- `--chunk-jobs`: cuántos chunks se reconstruyen en paralelo (procesos).
- `--inner-jobs`: hilos de extracción dentro de cada chunk (default 1, para no
  saturar al correr varios chunks a la vez).

Salida en `outputs/<frontend>/`: `merged_cloud.ply` (nube global) y
`merged_state.npy` (poses + puntos en el marco global, copiado también como
`map_state.npy` para poder lanzar `dense`/`view` sobre el resultado fusionado).

## Mallado de la nube (CGAL)

Convierte la nube densa en una **malla triangulada** con CGAL. Requiere las
dependencias nativas (ver *Requisitos*); el binario C++ se **compila solo** la
primera vez que se ejecuta.

```bash
uv run python pipeline.py mesh                                 # usa outputs/sift/dense_cloud.ply
uv run python pipeline.py mesh outputs/sift/dense_cloud.ply    # ruta explícita a la nube
uv run python pipeline.py mesh ruta/a/nube.ply --method poisson --smooth 24
```

Puedes indicar la nube de entrada como **argumento posicional** (o con `--input`);
si no pasas `--out`, el `mesh.ply` se escribe en la carpeta de esa nube.

Dos métodos:

- **`afront`** (Advancing Front, default) — **interpola** los puntos de entrada,
  conserva el color y respeta bordes abiertos. Ideal para superficies vistas de un
  lado (fachadas). Fiel a la nube.
- **`poisson`** (Poisson screened) — superficie **suave y cerrada**, tolera mejor
  el ruido pero puede "inflar" zonas abiertas. Estima normales y transfiere el
  color por vecino más cercano.

### Mejorar la calidad (la nube tiene ruido)

Advancing Front **interpola** los puntos, así que el ruido se vuelve picos
("grumoso"). Para una malla más limpia hay tres palancas, aplicadas por defecto y
ajustables:

- **Limpiar la nube antes de mallar**: `--outlier-pct P` (elimina % de outliers),
  `--simplify CELL` (rejilla; `0`=auto~2×spacing, coarsea→suaviza y acelera;
  `<0`=sin simplificar para máximo detalle), `--smooth N` (suavizado jet de los
  puntos con N vecinos).
- **Post-procesar la malla** (activo por defecto): `--mesh-smooth ITERS` (suavizado
  tangencial que respeta bordes, default 2) y `--min-component FRAC` (elimina
  componentes con menos de `FRAC×caras`, p.ej. islas de ruido flotantes,
  default 0.002).
- **Cambiar de método**: `--method poisson` produce una superficie **suave y
  cerrada** que tolera mucho mejor el ruido (a costa de "inflar" bordes abiertos).

```bash
# Más limpio (afront + suavizado fuerte + quitar islas):
uv run python pipeline.py mesh --mesh-smooth 5 --min-component 0.01 --smooth 12
# Superficie suave (Poisson):
uv run python pipeline.py mesh --method poisson --smooth 12
# Máximo detalle (sin simplificar ni suavizar):
uv run python pipeline.py mesh --simplify -1 --mesh-smooth 0 --min-component 0
```

La salida es `mesh.ply`, abrible en MeshLab/CloudCompare o cualquier visor de PLY.

## Uso por etapas

Las etapas comparten el mismo directorio de salida. Útil para iterar sobre una
etapa sin recalcular las anteriores:

| Etapa | Comando | Produce |
|---|---|---|
| 1. Extracción | `uv run python pipeline.py extract data/v.mp4` | `sfm_data.pkl` |
| 2. Inicialización | `uv run python pipeline.py init` | `map_state.npy`, `init_cloud.ply` |
| 3. Tracking (sparse) | `uv run python pipeline.py track` | `tracked_cloud.ply` |
| 4. Bundle Adjustment | `uv run python pipeline.py ba` | `map_state.npy` (refinado) |
| 5. Densificación | `uv run python pipeline.py dense data/v.mp4` | `dense_cloud.ply` |
| 6. Mallado (CGAL) | `uv run python pipeline.py mesh` | `mesh.ply` |
| 7. Visor | `uv run python pipeline.py view data/v.mp4` | (interactivo) |

Cada subcomando expone sus parámetros; consúltalos con `--help`:

```bash
uv run python pipeline.py extract --help
uv run python pipeline.py track --help
```

Parámetros frecuentes:

- `extract --k-skip N` — procesa 1 de cada N frames (default 5).
- `extract --start-seconds S` — salta un arranque malo (rotación casi pura).
- `track --min-angle G` — ángulo mínimo de triangulación (descarta ruido de escala).
- `dense --min-views N` — conserva solo puntos confirmados por ≥N pares.

## Resultados

Las nubes `.ply` se abren en **MeshLab** o **CloudCompare**. El visor interactivo
proyecta los puntos sobre el video:

- Slider **Frame** — navega en el tiempo.
- Slider **Fondo %** — opacidad del video (0 = fondo negro, 100 = video normal).
- `q` o `ESC` — cerrar.

## Demo web (API + frontend)

Una demo de la librería: backend FastAPI que expone el pipeline por HTTP +
frontend Vite/React con **visor 3D** (Three.js) para subir un video, elegir las
etapas, ver el progreso y explorar la nube/malla resultante en el navegador.

```bash
# 1) Backend (instala FastAPI con el extra `api`):
uv sync --extra api
uv run uvicorn backend.app:app --reload --port 8000

# 2) Frontend (en otra terminal):
cd frontend
npm install
npm run dev          # http://localhost:5173 (proxy /api -> :8000)
```

Abre http://localhost:5173, sube un video, marca las etapas y pulsa
**Reconstruir**; al terminar, haz clic en un artefacto para verlo en 3D.

Endpoints principales del backend: `POST /api/jobs` (video + config),
`GET /api/jobs/{id}` (estado/progreso/eventos), `GET /api/jobs/{id}/artifacts/{name}`.

## Estructura del proyecto

```
reconstruct3d/         Paquete de la librería (instalable)
  api.py               API de alto nivel (clase Pipeline)
  cli.py               Orquestador CLI (subcomandos)
  core.py              Cámara, features, front-ends, base de datos SfM
  init_sfm.py          Par semilla + triangulación inicial
  track_sfm.py         Registro incremental PnP + BA local
  bundle_adjust.py     BA local (ventana) y global + fusión de puntos
  dense_mvs.py         Densificación stereo multi-vista
  mesh.py              Wrapper del mallado CGAL (compila y llama al binario)
  calibrate.py         Calibración de K desde un video de tablero
  chunked.py           Reconstrucción por chunks con solape + fusión
  viewer.py            Visor POV interactivo (OpenCV)
  cgal_mesh/           Programa C++ CGAL (mesh_reconstruct.cpp + CMakeLists)
pipeline.py            Shim de compatibilidad -> reconstruct3d.cli
backend/               Demo: API FastAPI (app.py)
frontend/              Demo: Vite + React + Three.js (visor 3D)
camera.example.json    Plantilla de intrínsecos por dispositivo
pyproject.toml         Paquete + dependencias (fuente de verdad)
data/                  Videos de entrada (no versionados)
outputs/               Artefactos regenerables (no versionados)
```

## Publicar en PyPI

El paquete está listo para publicarse (`twine check` PASSED; nombre `reconstruct3d`
libre). Pasos:

```bash
uv build                      # genera dist/*.whl y dist/*.tar.gz
uv run --with twine twine check dist/*

# Ensayo en TestPyPI (recomendado):
uv publish --publish-url https://test.pypi.org/legacy/ --token <TEST_PYPI_TOKEN>

# Publicación real:
uv publish --token <PYPI_TOKEN>
```

Notas para quien instale desde PyPI:

- `pip install reconstruct3d` trae los front-ends **SIFT/ORB** (sin torch).
- El front-end **spglue** (SuperPoint+LightGlue) requiere instalar LightGlue a
  mano (no está en PyPI):
  `pip install "reconstruct3d[spglue]"` y luego
  `pip install "lightglue @ git+https://github.com/cvg/LightGlue.git"`.
- El paso **`mesh`** necesita CGAL nativo (CGAL + cmake + compilador); es opcional
  y se compila bajo demanda. El resto del pipeline funciona sin él.

## Notas técnicas

- **Reset:** si el tracking se estanca, vuelve a correr `init` antes de reintentar.
- **Calibración:** los intrínsecos por defecto son de un iPhone @ 540×960. Para
  otro dispositivo, pasa `--camera tu_camara.json` (ver sección *Cámara*). El
  video se redimensiona a la resolución de calibración antes de extraer.
- **`req.txt`** queda como referencia histórica; la gestión real de dependencias
  es `pyproject.toml` + `uv`.

Para contexto de arquitectura, convenciones y *gotchas*, ver **[CLAUDE.md](CLAUDE.md)**.
