Nodes/camera-comfyUI/DepthEdgeFilter
ComfyUI Node

DepthEdgeFilter

Kill the 'flying pixels' before they ruin your point cloud

By Alexankharin·Created about a year ago·Updated about a month ago· 36
DepthEdgeFilter
  • depth
  • mask
  • valid_mask
relative_threshold0.050
dilate1

If you've ever turned a depth map into a point cloud and seen a halo of stray dots hovering around the silhouette of every object, you've met "flying pixels." They're the points at depth discontinuities - the boundary between a foreground object and the background behind it - where the depth value is garbage, partway between the two. DepthEdgeFilter finds those boundary pixels and hands you a mask you can use to throw them away before they pollute the 3D.

It's the first filter I'd recommend adding to any depth-to-point-cloud graph, right after the estimator. The pack's own README calls it out in the video-to-4D pipeline as an optional cleanup for exactly this reason, and it costs almost nothing to run.

How it works

The heuristic is dead simple and effective: a pixel is an edge when the depth gradient relative to the depth itself is too big. The relative_threshold (default 0.05) is the sensitivity knob - a pixel gets flagged as an edge where |depth gradient| / depth exceeds it. Raise it toward 0.1+ if you're losing too much real geometry, lower it toward 0.02 if stray points are still getting through.

dilate (default 1) grows the detected edges by that many pixels using max-pool, which is usually a good idea: the true garbage zone is wider than a single pixel, and dilating one or two pixels catches the penumbra of bad estimates. There's also an optional mask input - a validity mask that gets ANDed with the result. That's handy for combining this with other filters (like a sky mask) so the output is a single validity mask you can pass downstream.

The output is valid_mask (MASK), and the polarity is worth memorizing: 1.0 where depth is valid, 0.0 on the edges. So you don't take the mask and keep it - you take it and use it to cut. In DepthToPointCloud you'd wire it into the mask input to drop those points; in a splat pipeline you'd mask the depth before lifting.

Where it slots in

  • After DepthEstimatorNode and before DepthToPointCloud - the classic position, keeps flying pixels out of the cloud entirely.
  • In VideoToFusedSplats-style pipelines the README suggests it as a companion to depth-based motion masking, since the same edge pixels also flicker between frames in video.
  • It accepts depth shaped [H,W], [T,H,W], or with a trailing channel dim, so it works on single frames and frame sequences alike.

Install

Shared camera-comfyUI install: Manager → search "camera-comfyUI" → Install, or clone https://github.com/Alexankharin/camera-comfyUI into custom_nodes/ and run python install.py. It's pure torch math on the depth tensor - no model downloads, no CUDA requirement, runs on CPU fine.

Troubleshooting

  • Your point cloud is now full of holes at object borders: threshold too aggressive or dilate too high. Back relative_threshold up toward 0.08–0.1 and drop dilate to 1.
  • Stray points still in the cloud: threshold too loose. Step it down 0.03–0.04 and re-check; also make sure the depth itself is ray depth, not Z-depth, or the whole geometry is bowed and the "edges" you're filtering are meaningless.
  • Mask looks inverted from what your next node expects: remember, 1 = valid here. If the downstream node wants 1 = edge, hit it with an invert before wiring it in.
CategoryCamera/PointCloud

Inputs (4)

NameTypeDefaultDescription
depthTENSOR
relative_thresholdFLOAT0.0500–10Mark a pixel as edge where |depth gradient| / depth exceeds this value.
dilateINT10–64Grow detected edges by this many pixels (max-pool dilation).
maskoptMASKOptional validity mask ANDed with the edge-filter result.

Outputs (1)

NameTypeDescription
valid_maskMASK