FOV & Tilt Estimator (Depth)
A depth map can tell you what lens the photo was shot with
- depth_map
- image
- annotated_image
- fov_degrees
- tilt_degrees
- info
Ever wondered what lens a photo was shot with - and wanted your generation to match it? That's this node's whole job. Give it a depth map plus the original image and it hands back two numbers: the horizontal FOV in degrees and the camera's tilt (horizon angle). No model files, no API, no weights - pure OpenCV math running on geometry already hiding in your depth map.
Why would you care? Mostly for perspective-matched work: compositing a render onto footage, or generating an image that should inherit a reference photo's viewpoint. The source's field of view tells you whether a tight 50mm-style crop or a wide 24mm shot is the honest answer - the read-the-input side of the "how do I guide perspective when generating?" question that keeps surfacing on r/comfyui.
It's the depth-based half of the ComfyUI FOV Estimator pack, and the author's own recommendation - the sibling FOV & Tilt Estimator (RGB) works straight off pixels, but trades robustness for convenience.
How it works
Instead of trusting RGB edges (which textures, shadows, and reflections love to fake), it reads depth discontinuities - where the depth map jumps from near to far, that's a real 3D edge. The code runs a Sobel gradient over the depth map to find those jumps, smooths them with bilateral filtering, then feeds them to a Hough line detector.
From there it's classic vanishing-point geometry: collect non-horizontal lines (roads, building corners), intersect every pair, and cluster the intersections with a RANSAC-style vote to find the dominant vanishing point. Its distance from the image center approximates the focal length in pixels, and hfov = 2 × arctan(width / (2 × focal_length)) converts that to degrees.
Tilt works the same way but on the horizon: find near-horizontal lines in the depth edges, take the median horizon position relative to frame center, and convert that pixel offset to degrees using the vertical FOV. No clean horizon? It falls back to analyzing the depth gradient (ground near, sky far).
One honest caveat: if no vanishing point is found (organic scenes, no converging lines, or a very wide shot where the VP lands too close to center), the node doesn't error - it returns a conservative guess based on aspect ratio (45–90°). Treat that as a failed detection, not a measurement.
Inputs and outputs that matter
Two IMAGE inputs are required:
- depth_map - from any depth node; Depth Anything V2 Large is the current default for this kind of work (MiDaS v3.1 the legacy standby, ZoeDepth and LeReS fine too). It handles 1- or 3-channel maps and normalizes them itself.
- image - the original RGB image for the overlay visualization.
Three optional knobs, and you'll rarely touch more than the first:
- depth_edge_threshold (default
0.1) - how big a depth jump counts as an edge. Lower (0.05–0.08) for subtle depth changes, higher (0.15–0.25) for noisy maps. - line_threshold (default
50) - Hough line sensitivity; drop it if lines aren't being found. - visualize (default
True) - draws detected lines, vanishing points, and horizon onto the output.
Outputs: annotated_image (the overlay, or the plain image if visualize is off), fov_degrees, tilt_degrees, and info - a formatted FOV: xx°, Tilt: yy° string you can display or parse. Wire the floats anywhere a number is useful; multi-frame batches get averaged per frame.
Installing it
Via ComfyUI Manager, search "FOV Estimator" (repo: gitcapoom/comfyui_fovestimator) and install. Manual route:
cd ComfyUI/custom_nodes/
git clone https://github.com/gitcapoom/comfyui_fovestimator.git
cd comfyui_fovestimator
pip install -r requirements.txt
Then restart ComfyUI. Requirements are numpy, opencv-python, torch - and you already have torch, so opencv-python is the only real new dependency. The heavier lift is the depth model: install a depth node (Manager's controlnet_aux Depth Anything preprocessor, or Kijai's wrappers) and let it download the weights, or this node has nothing to eat.
Where people get burned
- Trusting the fallback. No vanishing point markers in the overlay means the FOV is an aspect-ratio guess, not a measurement. Keep visualize on and sanity-check before wiring the values anywhere.
- Wide-angle shots. Vanishing points too close to center get rejected, and results clamp at 15–120°. Extreme wide and fisheye images are outside this node's pinhole assumption entirely.
- Noisy depth maps. Garbage depth in, unreliable numbers out. If your depth map looks mushy, raise depth_edge_threshold.
- Expecting camera calibration. It estimates, it doesn't calibrate - EXIF focal lengths won't match once lens distortion and cropping skew the vanishing point math.
This is a measurement utility, not a magic bullet. But when you need to know "what lens is this photo pretending to be," it's the fastest honest answer in ComfyUI - and the depth route is the one that earns your trust.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| depth_map | IMAGE | — | |
| image | IMAGE | — | |
| depth_edge_thresholdopt | FLOAT | 0.100.01–1 | — |
| line_thresholdopt | INT | 5010–300 | — |
| visualizeopt | BOOLEAN | true | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| annotated_image | IMAGE | — |
| fov_degrees | FLOAT | — |
| tilt_degrees | FLOAT | — |
| info | STRING | — |