Nodes/ComfyUI-JH-PixelPro/Filters: Dust & Spot Removal
ComfyUI Node

Filters: Dust & Spot Removal

Sensor Dust, Gone, Without Eating the Eyelashes

By jetthuangai·Created 6 months ago·Updated a day ago· 6
Filters: Dust & Spot Removal
  • image
  • mask
  • protect_mask
  • image
  • spot_mask
◄spot_size12►
◄sensitivity0.50►
◄modedark►
◄fill_modeblur►

Dust on a sensor is a solved problem that keeps being unsolved, because the cheap fix is "auto spot removal over the whole image" and that eats eyebrows. The hard part was never filling a dot - it's deciding which dots are dust and which are freckles, lashes, or catchlights.

Filters: Dust & Spot Removal (JHPixelProDustSpotRemoval) spends most of its effort on that decision, and the shape of its logic is worth knowing because it explains every weird result you'll get.

How it decides

Luminance (Rec. 709) is compared against a median background computed on a reduced-size copy - area average, then a 9×9 median in row chunks, so no big-kernel median ever runs at full resolution. The difference is thresholded at 0.01 + 0.2 × (1 − sensitivity)², and then connected blobs have to pass a stack of filters to survive:

  • bounding box no bigger than spot_size pixels
  • aspect ratio ≤ 2
  • fill ratio ≥ 0.5
  • at least ~3 px thick (a distance-transform peak of 1.3 px or more)
  • calm surroundings: the luminance spread in a window around it, with the blobs excluded, must stay under 0.25× the spot's contrast

That last filter is the one doing the real work - it rejects anything sitting on an edge or in busy detail, which is exactly where lashes and hair live. On the pack's clean sample portrait the whole operation changes about 0.006% of pixels. It's a scalpel, not a smear.

Filling then comes from normalised Gaussian convolution of the pixels outside the spots, computed at reduced size and scaled up, blended with a 1 px soft edge.

The inputs that matter

spot_size (2–64, default 12) - the size ceiling in pixels. Anything bigger, or longer, is left alone. This is your first lever when real bubbles go missing. sensitivity (0–1, default 0.5) - higher catches fainter spots and more skin texture. The tooltip admits it. mode (dark / bright / both, default dark) - sensor dust on sky is dark; white specks are the other polarity.

Then two optional masks that make it safe on real work: mask limits removal to a region (skin only), protect_mask is an absolute no-touch zone (freckles, eyes, a beauty mark). And fill_mode (blur / texture, default blur): blur is the smooth normalised-convolution fill, texture copies the best-matching nearby patch - chosen by SSD on the ring around the spot, brightness and colour-matched - so pores or grain continue through. On skin, texture is the difference between invisible and "there's a soft dot there".

Outputs: image and spot_mask.

Check spot_mask first, always

Run it into Mask Preview as an overlay before you look at the retouched image. If the mask shows long lines or a rash of pores, sensitivity or spot_size is too generous. If it shows nothing, the node found nothing - the image comes back unchanged, which is a feature and also the reason you should never assume it worked.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/jetthuangai/ComfyUI-JH-PixelPro.git
cd ComfyUI-JH-PixelPro
pip install -r requirements.txt   # the Python that runs ComfyUI

ComfyUI Manager: search ComfyUI-JH-PixelPro → Install → restart → Add Node → ComfyUI-JH-PixelPro → filters. Deps are kornia>=0.7, mediapipe>=0.10.33, opencv-python-headless>=4.8, scipy>=1.10; PyTorch from ComfyUI. No model downloads. A 2048² image peaks under 0.5 GB of GPU memory, so it's batch-friendly.

Where it goes wrong

  • It removed forehead texture. sensitivity too high. Drop it and raise spot_size instead - you want bigger spots, not fainter ones.
  • Dust on a landscape wasn't touched. Check mode; a bright speck needs bright, and mixed debris needs both.
  • Big blobs survive. Anything over 64 px needs manual cloning - that's out of scope for a spot detector by design.
  • A spot on an edge or a busy area is skipped. That's the calm-surroundings test. Use Heal / Content-Aware Fill for those instead.
  • Before sharpening. Sharpen first and the dust gets more visible while the node has more to sort through. Clean, then sharpen.

Same family, different entry point: this node's detector is the engine inside Face: Auto Blemish, which points it at detected faces and adds a redness signal, because pimples are red more than they are dark. If you're doing skin work, start there; reach for Dust & Spot when the target is a sky, a backdrop, a scan, or skin that has no face in frame.

Pack: Apache-2.0, maintained by JH (@jetthuangai), v1.10.0 with 64 nodes and genuinely good per-node docs (EN/VI). No Reddit chatter exists to validate your settings against - the pack name returns one unrelated browser extension in the corpus - so the mask preview is your QA loop. That's fine; it's a two-second check.

CategoryComfyUI-JH-PixelPro/filters

Inputs (7)

NameTypeDefaultDescription
imageIMAGEImage to clean.
spot_sizeINT122–64Largest spot diameter in pixels. Anything bigger or longer (eyes, hair, edges) is left alone.
sensitivityFLOAT0.500–1Higher catches fainter spots (and more skin texture).
modeCOMBOdarkdark = dust on the sensor, bright = white specks, both = either.
maskoptMASKOnly remove spots inside this mask (e.g. skin only).
protect_maskoptMASKNever touch spots inside this mask (e.g. freckles, eyes).
fill_modeoptCOMBOblurblur = smooth fill (original). texture = copy a matching patch from nearby so skin pores, grain or fabric continue through.

Outputs (2)

NameTypeDescription
imageIMAGE—
spot_maskMASK—