Skip to content

2.0: ONNX export, depth estimation from a photo, browser demo - #3

Merged
cobanov merged 2 commits into
mainfrom
feat/v2-onnx-estimate-web
Aug 30, 2026
Merged

2.0: ONNX export, depth estimation from a photo, browser demo#3
cobanov merged 2 commits into
mainfrom
feat/v2-onnx-estimate-web

Conversation

@cobanov

@cobanov cobanov commented Aug 30, 2026

Copy link
Copy Markdown
Owner

What this is

depth2normal did the easy half. You still needed a depth map to start, and Python to run the result. This covers both ends, and fixes a bug in the middle.

The bug

Gradients were computed on raw pixel values, so a 16-bit depth map produced gradients 256 times larger than the 8-bit copy of the same surface. Measured on assets/depth.png at the defaults:

z average Pixels tilted more than 30 degrees
1.0, 8-bit 249.5 6.5%
1.0, 16-bit 197.9 48.5%
2.0, either 249.5 6.5%

The two 2.0 rows are the same bytes. --range raw reproduces the old behaviour.

What is new

  • ONNX export (depth2normal export): two Conv nodes and a normalisation, 1.1 to 6.1 KB, dynamic height and width, strength as a graph input. Agrees with the NumPy path to within one level out of 255 on at most 2.8% of pixels, and runs about 5x faster (sobel: 41 ms to 5 ms on a 2048x1152 map).
  • Depth estimation (--estimate): Depth Anything V2 Small (Apache-2.0) through ONNX Runtime, downloaded once, checksummed, cached. 230 ms for a 640x480 photo on an M4 Pro CPU.
  • Browser demo (web/): no build step, both models run client side, nothing is uploaded. Verified end to end in Chrome.
  • SciPy removed: all three gradient methods are separable, so pure NumPy matches SciPy to 1e-13 at the same speed (57 ms against 60 ms) and 19.5 MB less to install.
  • --invert for depth maps where bright means far.
  • Borders reflect instead of repeating the edge pixel, which is what makes the exported graph match exactly.

Housekeeping

README rewritten with measured numbers, run.py dropped, Python 3.14 added to the CI matrix, plus a CI check that the committed web models still match the code. 71 tests, ruff clean.

The CLI keeps its old shape: depth2normal depth.png -o normal.png works exactly as before.

Gradients were computed on raw pixel values, so a 16-bit depth map produced
gradients 256 times larger than the 8-bit copy of the same surface and
`strength` meant something different in each file. Depth is now rescaled to a
fixed 0-255 range first (`--range auto|minmax|raw`, `raw` keeps the old
behaviour), so both copies produce identical normals.

Around that:

- `export` writes the conversion as a standalone ONNX graph (dynamic H/W,
  strength as a graph input) that matches the NumPy path to within one level
  out of 255, and runs about 5x faster.
- `--estimate` runs Depth Anything V2 Small through ONNX Runtime, so a
  photograph is a valid input. The model is downloaded once, checksummed and
  cached.
- `web/` is a no-build browser demo running both models client side.
- SciPy is gone: all three gradient methods are separable, so pure NumPy
  matches it to 1e-13 at the same speed and 19.5 MB less to install.
- Borders reflect instead of repeating the edge pixel, which is what makes the
  exported graph match exactly.
- README rewritten, `run.py` dropped, Python 3.14 added to CI.
… 3.11+

The converter still runs on 3.10; only depth estimation is gated. Verified by
running the suite on 3.10, where the ONNX Runtime tests skip.
@cobanov
cobanov merged commit 254908c into main Aug 30, 2026
11 checks passed
@cobanov
cobanov deleted the feat/v2-onnx-estimate-web branch August 30, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant