Nodes/ComfyUI CV/cv2.inpaint
ComfyUI Node

cv2.inpaint

For dust, scratches and logos — the fix you should try before you spend a diffusion pass

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
cv2.inpaint
  • src
  • inpaintMask
  • result
◄inpaintRadius0.0000►
◄flagsINPAINT_TELEA►

Generative inpainting is the wrong tool for a dust speck. You don't need a model to reconstruct a scratch on a scanner glass - you need the pixels next to it. cv2.inpaint is the twenty-year-old classical algorithm for exactly this, it runs in milliseconds on the CPU, and it's deterministic: same input, same output, every time.

This is one of the ~470 auto-generated cv2.* wrappers in bmad4ever/comfyui_cv. It's a straight wrapper, so there's no cleverness between you and OpenCV here - which is fine, because the algorithm is the point.

What it actually does

You give it an image and a mask, and it fills the masked pixels starting from the edge of the hole and working inward, using the pixels it already trusts. Two methods:

  • INPAINT_TELEA (the default) uses fast marching plus a weighted average of known neighbours. Great on small defects, spots, dust, thin text, and reasonably fast.
  • INPAINT_NS solves a transport equation over the region - better for thin lines and constant-background areas (scratches, hairline cracks, a scratch through a sky), slower and fussier elsewhere.

Neither of them knows anything. They propagate what's around, which is why they're brilliant at small holes and embarrassing at large ones. A fifty-pixel-wide hole comes back as a smudge. That's the trade: for anything a human would call "content", you want a model; for anything a human would call "a defect", this wins on speed, cost and the fact that it can't hallucinate a second nose.

The inputs

  • src - the image. Takes a ComfyUI IMAGE, a MASK or an NPARRAY directly; 8-bit 3-channel, 8/16-bit 1-channel or 32-bit float single-channel is what OpenCV wants.
  • inpaintMask - the hole. Non-zero pixels are the area to be inpainted. That convention is worth tattooing somewhere, because the pack contains sibling nodes that do the opposite: ft.inpaint and xphoto.inpaint both treat non-zero as known, keep this, and the pack's own tooltips warn about it. If you switch between inpainting experiments, check the direction before blaming the algorithm.
  • inpaintRadius - the circular neighbourhood each filled pixel considers. The widget default is 0, so set it: 3 is a sane start for speck and scratch removal, and bigger radius smears more.
  • flags - INPAINT_TELEA or INPAINT_NS.

One output: result, and it echoes src. An IMAGE link in gives you an IMAGE out (COMFY_MATCHTYPE_V3), so it drops straight into Save Image, a preview, or a compositing chain without a conversion node. Pass it an NPARRAY and you get an NPARRAY back - the type travels with the wire.

A workflow shape that works

Make the mask with something that's already good at that job - CV Color Range for a colour-defined defect, a threshold on a difference image, or a hand-drawn mask from core's mask editor - then feed both into this node and preview result. Because the algorithm is deterministic, you can tune inpaintRadius and watch it converge; there's no seed lottery. Step up to generative inpainting only when the hole stops being a defect and starts being an object.

Installing the pack

Manager → search comfyui_cv, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Python ≥ 3.12 and a recent ComfyUI on the V3 node API. inpaint is core OpenCV, not contrib, so it survives a wheel mix-up - but if you ever need the contrib nodes from this pack, install the contrib wheel. Installing plain opencv-python over a contrib one silently strips the contrib submodules from the shared site-packages/cv2, and tools/repair_opencv_contrib.py --check is how you find out.

Where people get burned

Inverted mask. Covered above, and it's the single most common confusion around this node.

Radius left at 0. Fresh node, nothing visibly changes, "the node is broken". It isn't.

Big holes. You'll get a smear that's obviously a smear. Use it for defects or use a model - the halfway version, a big mask and a huge radius, looks worse than either.

Mask and image different sizes. cv2 asserts. Derive the mask from the same image and it can't drift.

Note that the pack is a personal, LLM-assisted project whose README explicitly says not to use it in production without reviewing the code yourself. Individual nodes like this one are thin wrappers and genuinely useful; the disclaimers are about the whole library, not a reason to avoid a one-line call into a stable OpenCV function.

Categoryimage/CV/low-level/cv2 I

Inputs (4)

NameTypeDefaultDescription
srcCOMFY_MATCHTYPE_V3Input 8-bit, 16-bit unsigned or 32-bit float 1-channel or 8-bit 3-channel image. The image output(s) echo this input's format. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
inpaintMaskNPARRAY,IMAGE,MASKInpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that needs to be inpainted. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
inpaintRadiusFLOAT0.0000-1e+38–1e+38Radius of a circular neighborhood of each point inpainted that is considered by the algorithm.
flagsCOMBOINPAINT_TELEAInpainting method that could be cv::INPAINT_NS or cv::INPAINT_TELEA The function reconstructs the selected image area from the pixel near the area boundary. The function may be used to remove dust and scratches from a scanned photo, or to remove undesirable objects from still images or video. See for more details.

Outputs (1)

NameTypeDescription
resultCOMFY_MATCHTYPE_V3Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY.