Simple Depth of Field
Fake bokeh that respects real depth
- image
- depth_map
- PROCESSED_IMAGE
You've got a render - a portrait, a street scene - and it's flat. Everything is equally sharp, which is exactly how AI images look and exactly how photos don't. Simple Depth of Field fixes that: feed it an image and a depth map, tell it where the camera focuses and how wide the sharp zone is, and it blurs everything outside that zone with a strength that scales with distance. Real depth of field, computed from geometry instead of painted on.
How it works
The mechanism is worth understanding, because it's the difference between this and a lazy vignette blur. The node takes the depth map's first channel, computes a circle of confusion per pixel as |depth − focal_point|, and clamps everything within focal_range/2 of the focus plane to zero (sharp). Beyond that, blur ramps up continuously toward max_blur. The clever part is how it applies that ramp: the node builds a six-level "blur pyramid" - the image blurred at six kernel sizes, from zero up to max_blur - then blends adjacent levels per pixel based on that pixel's circle of confusion. Six discrete blurs plus per-pixel triangle interpolation gives a smooth, stepless falloff. It's a nice piece of engineering, and it's pure PyTorch (separable Gaussian kernels via conv2d with reflect padding), so it runs on the GPU and handles batches without the ImageMagick dependency.
The three inputs
Three inputs do the work:
- focal_point (0–1, default 0.5) - where on the depth scale the focus sits. With a standard depth map where white is near and black is far, 0.5 focuses mid-scene, lower pulls focus close, higher pushes it back. If your depth map is inverted (black = near), the whole thing flips - flip the map or you'll be baffled why the "near" stuff is blurry.
- focal_range (0–1, default 0.1) - width of the in-focus band. 0.1 is a fairly shallow, portrait-y zone; widen it for a deeper focus.
- max_blur (1–255, default 15) - how strong the blur gets at maximum depth. 15 is gentle; crank it for dreamy background melt.
Getting a depth map
Where does the depth map come from? Any depth estimator that outputs a grayscale image - Depth Anything, MiDaS, ZoeDepth - wired in as an IMAGE. The KB's depth essay calls this the "raw geometry" use of depth maps, as opposed to feeding them to ControlNet: this is depth as a post-processing input instead of a generation constraint. Keep the depth map the same resolution as the image; they're both plain IMAGE tensors, and mismatched sizes are the usual way this node gets confused.
Install caveat
For the classic portrait use - subject pops, background melts - you'll likely want max_blur well above the default. One install caveat that has nothing to do with the algorithm: this node never touches ImageMagick, but it shares a module with the pack's wand-based Selective Blur node, and that module imports wand at the top. No ImageMagick/wand on your machine means the whole comfyui-fams pack fails to register - this node included. Get ImageMagick and wand installed first, then everything shows up.
Because it's a deterministic post pass, it's cheap, repeatable, and far faster than re-rolling the image with depth ControlNet. For "make it look like it was shot with an actual lens," it's the good kind of hack.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| depth_map | IMAGE | — | |
| focal_point | FLOAT | 0.500–1 | — |
| focal_range | FLOAT | 0.100–1 | — |
| max_blur | INT | 151–255 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| PROCESSED_IMAGE | IMAGE | — |