SmaugC137 commited on
Commit
968f908
·
1 Parent(s): 1b63144

docs: update readme for engine/web/desktop

Browse files
Files changed (1) hide show
  1. README.md +114 -37
README.md CHANGED
@@ -1,51 +1,128 @@
1
  # Splasher
2
 
3
- Outil de **labélisation** dont le cœur est générique : on lui donne un *dataset
4
- synchrone* — à chaque instant, un **pack de canaux nommés** (nuage de points 3D,
5
- image caméra, pose, …) — et on labélise soit une **grille 2D vue-de-dessus (BEV)**,
6
- soit directement les **points 3D**, soit les deux.
7
-
8
- Premier cas d'usage : la **traversabilité**. Mais rien n'est câblé en dur : pas de
9
- schéma de classes imposé, pas de sémantique monde imposée, et **aucune dépendance
10
- obligatoire à un format de dataset**. apairo n'est qu'un adaptateur d'entrée optionnel.
11
-
12
- ## Idée
13
-
14
- - Plusieurs canaux synchronisés, affichés comme références : on se balade librement
15
- dans le nuage 3D, on regarde les images caméra.
16
- - On choisit les **canaux** à afficher (dock *Canaux* : montrer/masquer chaque nuage
17
- ou caméra disponible dans la source plusieurs caméras et nuages possibles).
18
- - On **dessine la grille de carrés** (vue de dessus) : son étendue et la taille de
19
- chaque carré, créée explicitement via **« Nouvelle grille »**. L'annulation (undo)
20
- est **par frame**.
21
- - On **sélectionne un rectangle** à la souris sur cette vue de dessus. Selon la cible :
22
- - **Grid** : remplit les carrés couverts de la classe active (sortie = raster d'IDs).
23
- - **Points** : assigne la classe aux points 3D dans le rectangle (sortie = labels par point).
24
- - Mode **Sélection** (façon bureau) : tracer un rectangle sélectionne des cellules
25
- (**Shift** = ajouter, sélections non contiguës possibles), puis on **applique** la classe
26
- à toute la sélection d'un coup. Changer de grille demande **confirmation** si une
27
- labélisation existe déjà.
28
- - **Cumul** : on peut cumuler ±N frames **recalées par leurs poses** dans le repère du
29
- frame courant (nuage plus dense pour mieux labéliser). La grille et les labels restent
30
- **par frame** : un coup de pinceau sur le nuage cumulé est **décumulé** vers chaque
31
- frame source. (Nécessite un canal `POSE`.)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  ## Installation
34
 
35
  ```bash
36
  cd ~/dev/splasher
37
- uv sync # cœur seul (numpy + Qt)
38
- uv sync --extra apairo # + adaptateur apairo (optionnel)
 
 
 
 
 
 
 
 
 
 
 
39
  ```
40
 
41
- ## Démo (zéro donnée externe)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  ```bash
44
- uv run python examples/demo_arraysource.py
 
 
 
45
  ```
46
 
47
- ## Entrée
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- Le cœur consomme une `Source` : `__len__`, `__getitem__(i) -> Frame`, `channels()`.
50
- `ArraySource` en construit une depuis des tableaux numpy en mémoire. `ApairoSource`
51
- (extra `apairo`) enveloppe tout dataset apairo synchrone.
 
1
  # Splasher
2
 
3
+ A **labeling** tool with a generic core: you give it a *synchronous dataset* — at each
4
+ timestamp, a **pack of named channels** (3D point cloud, camera image, pose, …) — and you
5
+ label either a **top-down 2D grid (BEV)**, the **3D points** directly, or both.
6
+
7
+ First use case: **traversability**. But nothing is hard-wired: no imposed class schema, no
8
+ imposed world semantics, and **no mandatory dependency on any dataset format**. apairo is
9
+ just one optional input adapter.
10
+
11
+ ## Idea
12
+
13
+ - Several synchronized channels, shown as references: you roam freely in the 3D cloud and
14
+ look at the camera images.
15
+ - You choose which **channels** to display (the *Channels* dock: show/hide each cloud or
16
+ camera available in the source multiple cameras and clouds supported).
17
+ - You **draw the grid of cells** (top-down view): its extent and cell size, created
18
+ explicitly via **"New grid"**. Undo is **per frame**.
19
+ - You **drag a rectangle** with the mouse on the top-down view. Depending on the target:
20
+ - **Grid**: fills the covered cells with the active class (output = raster of IDs).
21
+ - **Points**: assigns the class to the 3D points in the rectangle (output = per-point labels).
22
+ - Interaction: **left-drag = apply**, **right-drag = erase / deselect**,
23
+ **Shift-drag or middle-button = pan**, **wheel = zoom**. An **ego marker** (X = forward,
24
+ Y = left) is drawn at the origin in both the BEV and the 3D views, using the same axis
25
+ convention so the two read consistently.
26
+ - **Selection** mode (desktop-style): left-drag adds cells to the selection, right-drag
27
+ removes them (non-contiguous selections allowed), then you **apply** the class to the whole
28
+ selection at once. Changing the grid asks for **confirmation** if a labeling already exists.
29
+ - **Accumulation**: you can accumulate ±N frames **registered by their poses** into the
30
+ current frame's frame of reference (a denser cloud helps labeling). The grid and labels
31
+ stay **per frame**: a brush stroke on the accumulated cloud is **de-accumulated** back to
32
+ each source frame. (Requires a `POSE` channel.)
33
+
34
+ ## Architecture (swappable GUI)
35
+
36
+ Layers from generic to specific — each depends only on the previous one:
37
+
38
+ ```
39
+ splasher/
40
+ core/ pure numpy model (grid, label targets, BEV projection, accumulation…)
41
+ engine/ headless Session: all the state + operations, with no UI dependency.
42
+ Returns a *semantic* ViewState (points + per-point labels + channel,
43
+ BEV field, grid raster, selection, images) — not pixels.
44
+ server/ FastAPI backend on the same Session + serves the web front; desktop app.
45
+ web/ web front (vanilla, zero build) — the ONLY front.
46
+ vendor/ Three.js hosted locally (offline).
47
+ ```
48
+
49
+ **One front, one engine.** The web front is served by the backend; the desktop app
50
+ (`splasher` without `--serve`) opens that same front in a **native webview** (pywebview,
51
+ Spotify/Electron style) — so the desktop *is* the web front, in a window.
52
+
53
+ The `Session` (`splasher.engine.Session`) is the flexibility point: it draws nothing, it
54
+ exposes `view_state()` + commands (`paint_rect`, `select_rect`, `apply_selection`,
55
+ `set_frame`, `set_accum_radius`, `commit_grid`, `save`/`load`, …). Plugging in another front
56
+ = consuming this `Session` (the web does it over HTTP). **Colorization stays on the front
57
+ side**: each front draws the primitives its own way (reference helpers in
58
+ `splasher.engine.render`, mirrored in JS under `web/src`).
59
 
60
  ## Installation
61
 
62
  ```bash
63
  cd ~/dev/splasher
64
+ uv sync # core + engine only (numpy)
65
+ uv sync --extra app # + desktop app (FastAPI + uvicorn + pywebview)
66
+ uv sync --extra api # + headless web/API server (FastAPI + uvicorn)
67
+ uv sync --extra apairo # + apairo adapter (optional)
68
+ ```
69
+
70
+ ## Demo (zero external data)
71
+
72
+ ```bash
73
+ uv run python examples/demo_arraysource.py # desktop app (native window, `app` extra)
74
+ splasher # empty: file-viewer mode (browse + open files)
75
+ splasher demo # synthetic dataset
76
+ splasher demo --serve # web server → http://127.0.0.1:8077 (`api` extra)
77
  ```
78
 
79
+ **File viewer**: launched empty, *Open file…* browses the filesystem and opens individual
80
+ point clouds (`.npy`/`.bin`/`.pcd`) and images into resizable views, with a clear error for
81
+ unreadable / unsupported files. Independent from the dataset/labeling mode.
82
+
83
+ Dark brutalist black-&-blue design. Labelable top-down (BEV) view (underlay mode:
84
+ height / density / intensity) + **3D/camera panels you can add, resize, and each bind to a
85
+ channel** (3D color-by: height / intensity). Sensor placements (from `ChannelSpec.placement`)
86
+ are drawn as markers in 3D. Classes are fully editable (⚙ in the *Classes* panel). Three.js
87
+ is hosted locally (`web/vendor`) → works offline.
88
+
89
+ > Native desktop window: the `app` extra ships a Qt WebEngine (Chromium) backend, so
90
+ > `uv sync --extra app && splasher demo` opens a real native window out of the box. If no
91
+ > backend can start, it falls back to the default browser and keeps serving.
92
+
93
+ ## Input
94
+
95
+ The core consumes a `Source`: `__len__`, `__getitem__(i) -> Frame`, `channels()`.
96
+ `ArraySource` builds one from in-memory numpy arrays. `ApairoSource` (`apairo` extra) wraps
97
+ any synchronous apairo dataset.
98
+
99
+ ### apairo
100
 
101
  ```bash
102
+ uv sync --extra apairo --extra app # adapter + desktop
103
+ splasher /path/to/dataset --adapter apairo # all channels
104
+ splasher /path/to/dataset --adapter apairo --channels lidar,cam_front,pose # only these
105
+ splasher /path/to/dataset --adapter apairo --reference lidar --tolerance 0.05 # sync an async dataset
106
  ```
107
 
108
+ Two ways to pick what you work on: **load everything** (a synchronous dataset) and choose
109
+ what to *display* in the UI (the *Clouds (BEV)* toggles + **Add view** per channel), or
110
+ **select channels at load time** with `--channels a,b,c`. An asynchronous dataset needs
111
+ `--reference <channel>` (and optionally `--tolerance`) to be synchronized first.
112
+
113
+ ## API (back/front mode)
114
+
115
+ `splasher demo --serve [--host H --port P]` starts a FastAPI server driven by the same
116
+ `Session` as the desktop app. Main endpoints (each command returns the updated `ViewState`
117
+ so a front renders in a single round-trip):
118
+
119
+ | Method | Route | Purpose |
120
+ |--------|-------|---------|
121
+ | `GET` | `/api/session` | ~static description (channels, classes, n_frames) |
122
+ | `GET` | `/api/view` | current render state |
123
+ | `POST` | `/api/frame`, `/api/class`, `/api/tool`, `/api/targets`, `/api/accum`, `/api/visibility` | settings |
124
+ | `POST` | `/api/paint`, `/api/select`, `/api/selection/apply`, `/api/selection/clear`, `/api/clear`, `/api/undo` | labeling |
125
+ | `POST` | `/api/grid`, `/api/save`, `/api/load` | grid & I/O |
126
 
127
+ numpy arrays travel as `{dtype, shape, data(base64)}` (`splasher.server.protocol`), decodable
128
+ directly into a `TypedArray` on the JavaScript side. Interactive docs at `/docs`.