depth-anything-3-blackhole

Depth Anything 3, metric branch (DA3Metric-Large: DinoV2-L backbone + DPT head, 0.35 B params) for metric monocular depth in metres, running on a single Tenstorrent Blackhole p150a via tt-nn: the 24-block DinoV2-L backbone on chip in bf16 (HiFi4, fp32 accumulate), patch-embed and the DPT head on the host CPU in bf16. Port-reported 5.23 fps median (7.9x the fp32 CPU baseline), KITTI Eigen AbsRel 0.0930 (canonical 0.0906), Pearson PCC 0.999641 vs the canonical DA3-Metric output. Weights: depth-anything/DA3Metric-Large (Apache-2.0, pinned); port code Apache-2.0, source github.com/changh95/tt-Depth-Anything-3 @ dc5fe3171bdbc4fa1c5896f864d8825553c422ba.

Runs on p150 (mesh P150).

Packaged and published with tt-model-manager 0.1.0 (manifest schema 5.1).

Quickstart

tt-model pull  changh95/depth-anything-3-blackhole --with-weights
tt-model serve changh95/depth-anything-3-blackhole

pull --with-weights downloads the Docker image and the depth-anything/DA3Metric-Large weights at 4010e39f3634a45bc60553321fb49fb760bd594e (into your HF cache; they are not in the image). serve starts the model's own HTTP server on port 20000 (or the next free port, if that one is busy); the first start compiles kernels for your device, which takes several minutes, and the server is ready when it logs Application startup complete.

With tt-cli

tt serve changh95/depth-anything-3-blackhole        # pulls the image + weights, starts the server
tt model stop changh95/depth-anything-3-blackhole

The server listens on the port serve printed (20000, or the next free one). This is the model's own FastAPI app, not an OpenAI API: tt-model curl and the /v1/models hint on the ready card do not apply (/v1/models answers only so they do not 404). The real routes are GET /health, GET /info, POST /predict.

Call it

PORT=20000
curl -s localhost:$PORT/health      # {"status":"ok"|"starting", "model":..., "device":"blackhole:0"}
curl -s localhost:$PORT/info        # weights repo+revision, source commit, input/output contract

# one RGB image (base64 PNG/JPEG) -> metric depth. Default: 16-bit PNG at the model's 518x518.
python3 - <<'EOF'
import base64, io, json, urllib.request, numpy as np
from PIL import Image
img = base64.b64encode(open("media/source_1.png", "rb").read()).decode()
req = {"image": img,                   # required
       "output_format": "png",         # "png" (default, uint16 PNG) | "npz" (float32, lossless) | "json"
       "resize_to_original": False,    # True -> depth resized to the input's HxW (bilinear)
       "max_depth_m": None,            # optional clamp in metres (no sky head: sky can be huge)
       "png_scale": 1000.0}            # png only: uint16 = round(depth_m * scale); 1000 = mm
r = json.load(urllib.request.urlopen(urllib.request.Request(
    "http://127.0.0.1:20000/predict", json.dumps(req).encode(), {"Content-Type": "application/json"})))
u16 = np.array(Image.open(io.BytesIO(base64.b64decode(r["depth"]["data"]))))
depth_m = u16.astype(np.float32) / r["depth"]["scale"]
print(r["shape"], r["original_size"], r["stats_m"], r["timing_ms"])
EOF

Response fields: depth (format png16|npz|json, data base64, plus scale / saturation_m / saturated_pixels for png16 or keys/dtype for npz), shape of the returned map, original_size [H, W] of the input, model_input_size [518, 518], resized_to_original, units: "metres", stats_m (min/p05/median/p95/max), timing_ms (preprocess / infer / postprocess / total). Errors: 400 bad image, 503 while starting, 500 with the exception text.

Input handling: the image is squashed (no letterbox) to 518x518, ImageNet-normalised, exactly as the port's KITTI evaluation was run; one image per request, batch 1. depth = exp(raw head output) in metres; the port has no sky head, so far-sky pixels can exceed 80 m -- pass max_depth_m (the KITTI eval caps at 80) or use the float npz output. The 16-bit PNG saturates at 65535 / png_scale metres (65.5 m at the default 1000; use png_scale: 256 for the KITTI GT convention, 256 m).

First boot

The 1.34 GB model.safetensors is downloaded into your HF cache before the container starts. On the first start the 24 DinoV2 blocks are JIT-compiled (170 kernels, several minutes); the kernel cache persists under `/.cache/tt-model/depth-anything-3-blackhole/cacheso later starts take well under a minute. The server logsLoading weights, Warming upandWarmup complete; it is ready when uvicorn prints Application startup complete. Steady state is ~190 ms per image on the port author's box (~75 ms on chip, ~110 ms for the CPU DPT head, which wants AVX512_BF16 and physical cores; DA3_TORCH_THREADS` overrides torch's thread count).

Smoke test (from this repo, after serve):

python code/models/server/smoke_test.py --url http://127.0.0.1:20000

Results reported by the port (KITTI Eigen test split)

Metric CPU baseline (fp32) Chip pipeline (bf16)
Throughput 0.66 fps 5.23 fps (median, 7.9x)
KITTI Eigen AbsRel 0.0906 0.0930
KITTI Eigen RMSE (m) 3.156 3.127
KITTI Eigen delta<1.25 95.91% 95.88%
Pearson PCC vs canonical DA3-Metric 1.000000 0.999641

Three KITTI Eigen test images through the chip pipeline (turbo colormap, log-clipped 1-50 m; bright = near, dark = far):

Source Predicted depth
source 1 result 1
source 2 result 2
source 3 result 3

What is in code/

models/experimental/depth_anything_v3/ -- reference/dinov2_l_dpt.py (pure-torch DA3-Metric, loads the safetensors via HF_MODEL / TT_WEIGHTS_REVISION or DA3_WEIGHTS_DIR), tt/ttnn_da3_metric.py (24-block backbone on chip, bf16 channels-last CPU head; owns the device), eval/ (KITTI Eigen loader, metrics, canonical comparison; DA3_EVAL_DATA points at the KITTI download), tests/ (fps/PCC benchmark and KITTI eval, pytest). models/server/app.py is the FastAPI app above; models/server/smoke_test.py the smoke test. TODO.md / results.tsv are the port's iteration log. The eval/test harness (KITTI data, opencv-python, the canonical depth_anything_3 package) is not part of the serving image's dependency set.

Licensing

Port code: Apache-2.0 (github.com/changh95/tt-Depth-Anything-3 @ dc5fe31). Weights: depth-anything/DA3Metric-Large, released by ByteDance under Apache-2.0 (the nested DA3NESTED-GIANT-LARGE-1.1 checkpoint, which is CC BY-NC 4.0, is no longer used). Upstream model: ByteDance-Seed/Depth-Anything-3. Weights are never baked into the image; they are fetched into your own HF cache.

Provenance

The exact sources the image was built from — code/ in this repo is byte-identical to the model code inside the image:

component built from
tt-metal 8b98410e730bb504fea43a88609756e34821d91d
code/ digest 049807d21fe970d4 (sha256, first 16 hex digits)
built 2026-09-12T04:52:43+00:00 by tt-model 0.1.0
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support