Skip to content

Repository files navigation

🌍 Worldscope Server

🔍 Semantic search over live camera snapshots

Worldscope indexes thousands of live public camera feeds and lets you search them by natural language ("snowy mountains", "airport runway at night") using CLIP. Image and text share one embedding space, so a text query ranks the snapshots by visual similarity.

🌐 Search UI (Hugging Face Space)

Worldscope

🏗️ Architecture

Embeddings are computed on demand by Hugging Face Jobs (GPU), stored in a public HF Storage Bucket, and searched in memory by a small FastAPI app — no Redis, no workers, no database.

 Hugging Face Jobs (on demand)
download (CPU) ─────────────► embed (GPU) ─────────────► cleanup (CPU)
cameras + snapshots CLIP embeddings prune unreferenced
→ bucket + manifest.parquet → embeddings.parquet images in the bucket
│
▼
FastAPI server / Gradio Space loads embeddings.parquet
into memory and serves brute-force cosine search
  • Data sources: a Google Sheet of camera URLs plus three live APIs — FAA weather cameras, USGS VolcView ashcams, and AlertWest (~16k cameras total).
  • Storage:hf://buckets/<ns>/worldscope holds the snapshots (served via public …/resolve/… URLs), plus manifest.parquet and embeddings.parquet.
  • Search: ~15k × 512-dim vectors (~30 MB) → in-memory numpy cosine search, sub-millisecond.

📂 Layout

PathPurpose
app/main.pyFastAPI app: search, text embedding, deduplicated /refresh, image redirect
app/clip_model.pyLoads CLIP once; embed_text() for queries
app/index.pyIn-memory embeddings index + brute-force cosine search
app/hf_jobs.pyTrigger/poll HF Jobs; build bucket URLs
app/cameras.pyCamera list (Google Sheet + FAA, USGS VolcView, AlertWest APIs)
jobs/download_job.pyHF Job (CPU): fetch cameras + snapshots → bucket + manifest
jobs/embed_job.pyHF Job (GPU): manifest + images → embeddings.parquet
jobs/cleanup_job.pyHF Job (CPU): delete images not referenced by embeddings.parquet
scripts/*.pyOne entrypoint per pipeline stage (used by CI)
.github/workflows/refresh-embeddings.ymlSequential download → embed → cleanup pipeline
space/Gradio search frontend (deployed as a HF Space)

🌐 API

  • GET /images?query=storms — semantic search; returns top matches with image URLs + scores.
  • POST /embeddings/text{ "text": ... } → CLIP text embedding.
  • POST /refresh — start the download → embed → reload pipeline (deduplicated: returns 409 if one is already running). GET /refresh reports status.
  • GET /image/{path} — redirect to the snapshot's public bucket URL.

🔄 Refreshing embeddings

Run the pipeline on Hugging Face infrastructure in either of two ways:

  • GitHub ActionsActions → "Refresh embeddings" → Run workflow. Three sequential jobs (download → embed → cleanup); each submits an HF Job with the right hardware and the next only runs if the previous succeeded.
  • APIPOST /refresh on a running server (chains the same stages, dedup-guarded).

⚙️ Setup

uv sync # install deps
cp .env.example .env # fill in HF_TOKEN, HF_BUCKET, FAA_API_KEY
uv run uvicorn app.main:app # run the server

Key env vars (see .env.example): HF_TOKEN, HF_BUCKET (<ns>/worldscope), FAA_API_KEY, CLIP_MODEL, and the job flavors HF_DOWNLOAD_FLAVOR / HF_EMBED_FLAVOR.

Note: HF Jobs require a Pro/Team/Enterprise account with pre-paid credits. For GitHub Actions, set HF_TOKEN (and optionally FAA_API_KEY) as repository secrets.

About

Semantic search on images using CLIP embeddings

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages