Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

School 3DGS Capture Platform

My CAS project as vice president of the computerization club. The goal is to build a 3D Gaussian Splatting model of the entire school and enter it in Explorer Global. Buildings are planned to be shot by drone, and the interiors are shot by student volunteers with their phones. Collecting that many indoor photos by hand doesn't scale, so this web app exists: volunteers upload photos and get per-photo quality results when submitting their complete task — nobody finds out three weeks later that a whole floor has to be re-shot.

The whole thing is designed to run on one ordinary desktop in the club room (i5-12400F + 32 GB + RTX 3090), which also does the reconstruction work.


Contents


What it does

Volunteers (phone, /v) Admins (desktop, /admin)
How they get in Real-name account + password One of three fixed admin passwords
What they see Where to go, how to shoot, how many photos, instant per-photo feedback Global progress, checkpoint planning, photo review, submission decisions, volunteer accounts, training jobs, live server metrics

Everything runs behind a single port: the FastAPI backend also serves the built React frontend, so volunteers just open http://<lan-ip>:8000 on their phones.

Requirements

  • Python 3.11+ and uv — required
  • Node.js 18+ — only if you change the frontend. The built frontend/dist is committed to this repo on purpose, so a deployment machine never needs Node.
  • Enough disk space for the photos (a full school is tens of GB)
  • A decent GPU for the training part of 3dgs

Quick start

Every platform goes through the same two steps — uv sync, then launch — the wrapper scripts just make it one action.

Windows

git clone <repo-url>
cd campus-splat
.\start-server.cmd

macOS / Linux

git clone <repo-url>
cd campus-splat
chmod +x start-server.sh     # only needed once
./start-server.sh

Manual (any platform)

uv sync
uv run python scripts/serve.py            # add --port 8080 / --reload / --skip-build

Whatever route you take, the launcher will:

  1. sync the Python environment with uv sync
  2. check for the frontend build (and build it if Node is available and it's missing)
  3. print the LAN address volunteers should open
  4. start the server on 0.0.0.0:8000

Then open:

URL
App entry http://127.0.0.1:8000
Admin console http://127.0.0.1:8000/admin
API docs (Swagger) http://127.0.0.1:8000/docs

Volunteers on the same network use the LAN address the launcher prints, e.g. http://192.168.1.25:8000.

Fixed administrator accounts

Three permanent administrator identities log in using only their assigned password. Obtain the password for your administrator identity from the platform maintainer. They cannot be deleted. Signing out ends only the current session. Each administrator owns the tasks they create and can edit/delete only their own tasks, checkpoints, photos and submissions. Only administrator 001 can access training, logs, model previews and exports for their own tasks. Training executes on the computer running the platform backend and saves output in its local data/training/ directory (or the configured data directory). To store output on administrator 001's computer, run the platform backend on that computer. Existing tasks migrate to administrator 001. Task codes are generated automatically: five random letters and digits, unique and immutable. Newest tasks appear first, including in the sidebar. All three administrators can list active volunteer accounts and change their usernames/passwords or archive them. As requested, this list includes current passwords; these recoverable credentials are stored with the account ID.

Where the data lives, and starting over

data/ holds everything: photos, the SQLite database, training output and logs. Two settings move it around:

Want to Set
Put everything on a big disk THREEDGS_DATA_DIR=E:\campus-splat-data
Keep only the photos elsewhere (they are the bulky part) THREEDGS_UPLOAD_DIR=F:\campus-photos

With an external photo folder the database stores absolute paths, so moving that disk later means updating the variable too.

The photo tree is meant to be readable. Every folder and file name is ASCII, so you can find a photo without opening the app:

data/uploads/SanHaoLou/3FShiYanShi302/0001_ZhangSan.jpg
             │         │               │    └ who shot it (pinyin of the name they joined with)
             │         │               └ its position inside that checkpoint
             │         └ the checkpoint — 点位名, transliterated ("3F 实验室 302")
             └ the task — 任务名, transliterated ("三号楼")

Task and checkpoint names are normalized when they are created (CamelCase for English, pinyin for Chinese), so typing in Chinese is fine — the name only ever changes on disk. Renaming a task later does not move its photos: the folder is fixed at creation. Vendors, tools and scripts all get plain ASCII paths, which is what makes find, rsync, COLMAP and Windows happy.

Training output lives in data/training/<task folder>/<YYYYMMDD-HHMM>/ — one self-contained folder per run, grouped under the task it belongs to, so ls data/training says which building it is and a second run of the same task gets its own timestamped folder instead of overwriting the first. Inside: plan.json, the hard-linked input/, and output/ with the poses, block clouds, merged.ply, transforms.json and manifest.json. The System page breaks the disk usage down by photos / training output / thumbnails, and each run in the Training page has a 🗑 button: delete the record, optionally together with the files. Deleting the record alone is the default because a run is hours of GPU time — and note that a run's input/ is hard links, so a photo removed from uploads/ keeps living (and taking space) inside the run that used it until that run's folder is deleted too.

Opening folders: the admin console has 📂 buttons (System page: data / photo folders; task detail: that task's photos; photo review: show the selected photo). They open the file manager on the machine running the server — clicking from your laptop still opens it on the server, not on your laptop.

Deleting tasks: use the task page. Global reset is disabled for all fixed administrators so other administrators' tasks and permanent volunteer IDs cannot be erased. Deleted tasks release unfinished task slots; account and assignment records remain in the database. Original media is removed when requested.

How volunteers and admins use it

Volunteers — open /join or /v

  1. Register with a real name and a password. Active usernames must be unique; passwords may repeat. Registration assigns a permanent ID, starting at 00000.
  2. Log in with username/password. Volunteers cannot edit their name or ID, but can change their password under Settings (/v/mine).
  3. The board lists the checkpoints that are free to take. One checkpoint at a time: take one, shoot it, upload it, hand it in, then take the next. While a checkpoint is taken and not yet handed in, the backend refuses another one.
  4. Follow the checkpoint's shooting instructions — a per-angle script, where to find the spot, and an optional reference image. Selected photos are drafts in IndexedDB on the current browser/device; they survive a reload but do not sync between devices.
  5. Upload that checkpoint's photos. The upload itself is validated on the spot (unreadable files, byte-identical duplicates) so a mistake is caught while the volunteer is still standing there, and the heuristic quality check — blur, exposure, near-duplicates — runs in the background while the phone polls for the verdicts.
  6. Hand the checkpoint in (提交审核). It does not wait for the rest of the task: the volunteer hands in one checkpoint, waits for nothing, and starts the next.
  7. A checkpoint the admin sends back returns to "mine to shoot" with the review note attached — only that checkpoint has to be reshot, the approved ones stay approved.
  8. Approved checkpoints move to the approved list on the board.

Closing an account (/v/mine) permanently preserves the ID, name, password and historical records, revokes sessions and releases the username. Registering the same name later allocates a new ID; old IDs never get reused. The display pads IDs to at least five digits. Work already handed in stays with the administrators for review.

Administrators — open /admin

  • Tasks & checkpoints: create, edit descriptions, configure required photos and reference images, or delete your own tasks. Automatically generated task codes cannot be changed. Your tasks appear above the navigation's task-management entry. Batch import takes one line per checkpoint: 点位名|位置|长x宽x高 (for example 3F 实验室 302|302|8x6x3.5) — the old 楼栋/楼层/房间 columns are gone. The required photo count is derived from the size (2*(l*w + l*h + w*h) / 5 m², see backend/app/services/shots.py) unless an optional 4th column overrides it.
  • Volunteer accounts: view every active volunteer's name, permanent ID and password; edit names/passwords or archive accounts. Changing a password revokes the volunteer's existing sessions.
  • Checkpoint photo review (点位照片最终审核): every handed-in checkpoint shows up here. All administrators see every checkpoint of every task, photos and quality verdicts included; administrator 001 may judge any checkpoint, while 002/003 only the checkpoints of tasks they published. A verdict is per checkpoint — approve, or send back with a note, in which case only that checkpoint has to be reshot and the approved ones stay approved. Decisions are serialized and cannot be repeated.
  • Trial reconstruction (试解算): the button on that page runs COLMAP once on one checkpoint's photos and answers the two questions the review needs: can this be reconstructed at all, and how good is it, 1-100. It takes minutes for a room, runs one at a time, and keeps only a JSON report — data/recon/<checkpoint>/ is deleted afterwards and the model is never reused by training (COLMAP is non-deterministic, so the report is evidence for a human, not a promise). The report lists the registration ratio, how many connected blocks the photos formed, the reprojection error, the worst photos, and a rule-based "what to shoot next" list for the volunteer. Mock mode (THREEDGS_RECON_MODE=mock) fakes the report where COLMAP is not installed.
  • Administrator passwords: each administrator picks their number at login (001/002/003) and changes their own password under System. Forgotten ones are reset on the server with uv run python backend/scripts/reset_admin_password.py 002 (the server must be stopped first). Hashes live in admin_accounts; config.ADMIN_PASSWORDS only seeds the very first start.
  • Photo review, training and 3D preview: the original quality override, CSV export, reconstruction pipeline and placement editor remain available for your own tasks.
  • System: monitor server hardware and resource usage, and change your own password. Global data deletion is disabled.

Legacy password-only administrator sessions and task-code volunteer sessions are invalidated once during migration. New accounts persist across server restarts.

The photo quality check

Deliberately heuristic, not a full photogrammetry run — so a volunteer standing in a corridor gets an answer in milliseconds instead of waiting minutes:

Check Method
Blur Laplacian variance, normalized to a 1024 px long side
Over/under-exposure Ratio of blown (>=250) and crushed (<=8) pixels
Brightness & contrast Grayscale mean and standard deviation
Resolution Long side in pixels
Compression artifacts Bytes per pixel — catches photos re-sent through WeChat/QQ
Missing capture info EXIF presence (time, GPS, camera)
Near-duplicate frames 64-bit dHash, Hamming distance (catches "shoot 20 of the same wall")

Consequently it runs in a background worker (backend/app/services/quality_jobs.py): the upload request only streams the file to disk and refuses byte-identical repeats, then returns; the phone watches each photo flip from checking to its verdict, and the photo page can start the next batch meanwhile. Set THREEDGS_QUALITY_INLINE=1 to judge inside the request instead. A photo left in checking by a crash is re-queued at the next startup, a photo that cannot be decoded ends up rejected with the reason, and a manual verdict from the review page always wins over the heuristic one — including when the admin judges a photo the worker has not finished checking yet.

Each photo gets a 0–100 score, structured issues, and actionable advice in the volunteer's own language. Admins can always override a verdict manually.

Project layout

campus-splat/
├─ pyproject.toml            Python dependencies + PyPI mirror (used by uv)
├─ uv.lock                   Locked dependency versions
├─ .python-version           Pins Python 3.11
├─ start-server.cmd          One-click launcher — Windows
├─ start-server.sh           One-click launcher — macOS / Linux
├─ scripts/serve.py          Launcher logic: env check, frontend check, LAN addresses
├─ scripts/setup_gsplat.ps1   One-click gsplat install (method A of the toolchain doc)
├─ backend/                  FastAPI backend
│  ├─ app/
│  │  ├─ main.py             App entry point (also serves the built frontend)
│  │  ├─ config.py           Every tunable + quality thresholds
│  │  ├─ models.py           Task / Checkpoint / Photo / QualityReport / TrainingRun
│  │  ├─ quality/            Heuristic quality engine
│  │  ├─ routers/            auth · admin · volunteer · media
│  │  └─ services/           Ingest, stats, training pipeline, point-cloud
│  │                         plumbing (splat.py), system metrics
│  ├─ scripts/run_training.py   The graded pipeline: COLMAP per scope → block
│  │                           split → 3DGS per block → merge/align/export
│  │                           (with a mock mode that walks the same stages)
│  ├─ scripts/seed_demo.py      Demo data for the preview: synthetic photos →
│  │                           tasks → uploads → two mock runs
│  └─ tests/                 pytest suite
├─ frontend/                 React + Vite + TypeScript
│  ├─ dist/                  Build output — committed on purpose
│  └─ src/pages/             admin/ (desktop) and volunteer/ (phone)
├─ data/                     Runtime data: photos, SQLite DB, training output (git-ignored)
└─ docs/                     Pipeline design, trainer setup for the 3090 box, drone selection, deployment

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages