Nodes/Atlas Camera/Atlas Learned Solve (GeoCalib) 🧠
ComfyUI Node

Atlas Learned Solve (GeoCalib) 🧠

The camera solver for AI images with no straight lines

By mikejamesvfxΒ·Created 3 months agoΒ·Updated a day agoΒ· 1
Atlas Learned Solve (GeoCalib) 🧠
  • image
  • raw_meta
  • solve
  • report
β—„height_modemeasure_from_depthβ–Ί
β—„camera_height_m1.60β–Ί
β—„depth_modeldepth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hfβ–Ί
β—„sensor_width_mm36.00β–Ί
β—„weightspinholeβ–Ί
β—„deviceautoβ–Ί
β—„focal_length_mm0.00β–Ί

The atlas-camera pack has two ways to recover a camera from a photograph: a deterministic geometric solve that hunts for vanishing points in straight lines, and this one - a learned prior called GeoCalib that just looks at the image and predicts focal length and gravity. If you've ever tried to matchmove an AI-generated image, you know why the learned route exists: diffusion output is full of buildings with subtly bent walls and no two edges agreeing on a vanishing point. Atlas Learned Solve is the node you reach for when the clean-edge approach chokes.

GeoCalib is a known quantity in the community - it's the repo people cite for camera intrinsics estimation (an r/StableDiffusion VR pipeline post name-drops it as the best of the intrinsics estimators it tested). Here it's wrapped as a first-class Atlas/01 Β· Input & Camera node feeding the same ATLAS_SOLVE type as every other solver.

How it works

GeoCalib predicts focal length and the gravity (up) vector directly from image content, so it doesn't depend on clean straight edges converging to consistent vanishing points. From there the node derives the horizon and camera orientation, and - depending on your settings - uses the solved focal to drive a Depth Anything V2 metric depth pass that measures the ground plane. The big wrinkle is scale: a pinhole solve gives you angles, not meters, so the camera height has to come from somewhere.

That's what height_mode is about. measure_from_depth fits the ground plane with Depth Anything V2 and gets eye height from the geometry - no assumed height. assume just uses camera_height_m (default 1.6 m). The node's own changelog is honest about the failure mode this exists to catch: an assumed scale tier is a structurally valid solve that exports cleanly and is wrong - roughly 10x too small on wide exteriors. Read the report output; it tells you which scale tier you landed in.

Inputs and outputs

Required: image. That's it.

The ones a beginner actually touches:

  • focal_length_mm - 0 means GeoCalib predicts it. Set it (or wire in AtlasLoadRAW's raw_meta) and the measured EXIF focal wins; GeoCalib still supplies gravity/roll. This is the "stop guessing" upgrade.
  • depth_model - the metric depth backend. Default is Depth-Anything-V2-Metric-Outdoor-Large-hf (Apache, transformers-only, no extra install). MoGe-2 is the interior specialist (MIT, [moge] extra) but masks sky, so it's poor outdoors. The tooltip's blunt: DA3 variants are experimental and the NESTED one is non-commercial.
  • sensor_width_mm - 36 mm default; wire real sensor data when you have it.
  • weights - pinhole (no lens distortion, best for clean AI renders) vs simple_radial. Keep pinhole for generated images.
  • device - auto/cuda/mps/cpu.

Outputs: solve and report.

Installing it

This is a [neural] tier node - it needs GeoCalib, which ships only from GitHub:

cd <ComfyUI>/custom_nodes
git clone https://github.com/mikejamesvfx/atlas-camera.git

Then install the neural extra into ComfyUI's Python. On a portable (embedded) ComfyUI, install GeoCalib --no-deps to protect your torch/numpy ABI, then add its runtime deps:

<ComfyUI>/python_embeded/python.exe -m pip install --no-deps "git+https://github.com/cvg/GeoCalib.git"
<ComfyUI>/python_embeded/python.exe -m pip install opencv-python "kornia<0.8.3" transformers

That kornia<0.8.3 cap matters: an uncapped resolve pulls kornia 0.8.3, which breaks ComfyUI-LTXVideo's import entirely. Restart after installing. If GeoCalib is missing the node fails soft with an informative message rather than crashing the graph - the pack's contract for absent deps.

Common issues

The assumed-scale trap is the one to internalize: if your exported camera is tiny on a wide exterior, you got the assumed tier. Feed it a real focal via raw_meta and let it measure depth instead of assuming 1.6 m. And check the licensing note in THIRD_PARTY.md - Depth Anything V2's large weights are CC BY-NC 4.0 while small/base are Apache 2.0, so pick the variant that fits your use.

CategoryAtlas/01 Β· Input & Camera

Inputs (9)

NameTypeDefaultDescription
imageIMAGEβ€”
height_modeoptCOMBOmeasure_from_depthmeasure_from_depth = fit the ground plane with Depth Anything V2 (no assumed eye height); assume = use camera_height_m.
camera_height_moptFLOAT1.600.01–1000Fallback / assumed camera height when not measured or low-confidence.
depth_modeloptCOMBOdepth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hfMetric depth backend (fed the solved focal). V2-Metric-Outdoor (DEFAULT) / V2-Metric-Indoor: Apache, transformers-only (NO extra install), best all-round; Outdoor wins on sky/exterior scenes. MoGe-2 (Ruicheng/moge-*): MIT, cleanest on ENCLOSED/INTERIOR shots but masks sky (poor outdoors) β€” needs [moge]. DA3* (EXPERIMENTAL): strong metric, heavy deps, DA3NESTED is non-commercial CC BY-NC β€” needs [neural-da3]. (4-scene A/B 2026-07-13.)
sensor_width_mmoptFLOAT36.00β€”
weightsoptCOMBOpinholepinhole = no lens distortion (best for clean AI renders).
deviceoptCOMBOauto4 options: auto, cuda, mps, cpu
focal_length_mmoptFLOAT0.000 = GeoCalib predicts the focal. >0 (or a wired AtlasLoadRAW raw_meta) = trusted focal (e.g. EXIF) wins; GeoCalib still supplies gravity/roll.
raw_metaoptATLAS_RAW_METAβ€”

Outputs (2)

NameTypeDescription
solveATLAS_SOLVEβ€”
reportSTRINGβ€”