Mpi Pano Depth
Real geometry out of a 360 pano (without measuring it like a flat photo)
- image
- depth_path
A 360 panorama is the cheapest way to author a camera move: generate or shoot a pano, then walk a virtual camera through it. To walk it you need depth - and depth models are trained on flat pinhole photos. Feed one a 2:1 equirect and it reads the top of the frame as far sky and the bottom as near floor, because that's what the image looks like. The result is depth for a photograph that pretends to be a pano.
MpiPanoDepth does it the way MoGe's own panorama script does: split the equirect into views, measure each one as a normal image, then stitch the measurements back into a single equirect depth field.
How it works
The pano is sliced into 12 icosahedron views - 768px each, 100° field of view, arranged to cover the sphere evenly - and MoGe v1 runs on every one of them. Then the log-distance gradients from the views are merged back into a single equirect by least squares (a Poisson-style integration, wrapping in x). That's the key move: it isn't blending twelve depth maps and hoping, it's solving for the one depth field whose local slopes agree with all twelve measurements, which is why the result has no visible seams at the view boundaries.
This is MoGe's own infer_panorama.py, as modified by Matrix-3D (Skywork, MIT), vendored here with the CLI, file output and mesh export stripped out. The lineage is worth knowing because it means the maths is the reference implementation's, not a reimplementation.
One detail the author got right and you'd notice if it were wrong: pixels MoGe's validity mask rejects - sky, and anything with undefined geometry - are written at twice the farthest valid depth, i.e. a far dome. Otherwise the sky would sit flush with the farthest buildings, and any later reprojection or camera move would glue the two together.
The pano is resized to your depth_width (default 2048) by depth_width/2 with area interpolation before any of this, so the depth grid is your choice of resolution, not the source pano's.
Inputs and outputs
- image - a 2:1 equirectangular 360 panorama.
- model - a MoGe v1
.safetensorsfrommodels/moge/. The combo lists every safetensors in that folder. - resolution_level - 0–9, MoGe's per-view detail, default 9.
- depth_width - 512–4096 in steps of 64, default 2048. Height is half that, as equirect demands.
One output: depth_path, a STRING pointing at a raw little-endian float32 file under output/scenes/, depth_width/2 rows × depth_width columns, no header. Same format as its sibling MpiLiftDepth - it's for a downstream 3D tool or host app, not for the graph. If you wanted an image you could look at, this isn't that node.
Practically, the pair is: MpiPanoDepth gives you the scene's geometry, and MpiWanMaskedVideo (with the Matrix-3D 360 LoRA) renders a camera path through it, inventing what the path walks into.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/MadPonyInteractive/ComfyUi-MpiNodes
# restart ComfyUI
Then drop a MoGe v1 .safetensors - the ViT-L checkpoint (Ruicheng/moge-vitl, re-saved as safetensors) - into ComfyUI/models/moge/. The pack never downloads weights for you. The vendored MoGe code is MIT; the config for that one checkpoint is hardcoded in scene.py, so a different MoGe checkpoint isn't a drop-in.
There's no requirements.txt in the repo - the pack leans on scipy, opencv and torch from the existing ComfyUI environment. Those imports live at module level, so if one is missing the entire pack fails to load rather than just this node.
Where people get burned
It's twelve forward passes plus a sparse least-squares solve. At resolution_level 9 there's nothing instant about it; treat it as a real compute step, not a preprocessor.
"MoGe found no valid depth in this panorama" is the one raise, and it fires when the validity mask comes back empty - a pano that is all sky, a flat synthetic gradient, a heavily painted-out frame. Usually that's the input, not a bug.
The model holds ~1.3 GB of RAM on the CPU between runs and moves to the GPU only for the pass, so its cost keeps appearing in a workflow even when you're not running it.
Don't assume the depth grid matches your pano. The default 2048-wide grid is a measurement resolution you picked, and the source gets area-resampled into it - so if your camera path tool expects depth at a specific size, set depth_width to it explicitly rather than assuming the pano's own dimensions come through.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | A 2:1 equirectangular 360 panorama. | |
| model | COMBO | MoGe v1 weights in models/moge/. | |
| resolution_level | INT | 90–9 | MoGe detail per view, 0-9. |
| depth_width | INT | 2048512–4096 | Width of the depth grid; its height is half. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| depth_path | STRING | — |