Nodes/ComfyUI CV/cv2.ft.inpaint
ComfyUI Node

cv2.ft.inpaint

The third inpainter, and the one with the backwards mask

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
cv2.ft.inpaint
  • image
  • mask
  • result
◄radius0►
◄functionLINEAR►
◄algorithmMULTI_STEP►

This is cv2.ft.inpaint, the fuzzy-transform inpainter from OpenCV's contrib ft module. In ComfyUI terms it's not a generative inpaint - it doesn't touch your diffusion model, has no prompt, and won't invent plausible eyelashes. It reconstructs missing pixels from the surrounding ones using a weighted local fit, and it does it in milliseconds on the CPU.

So why open it? Because sometimes that's exactly right: dust, a scratched overlay, a scan line, a small over-painted blob, a watermark-ish defect on a flat background. Instruction-editing models took most of the "fix what's wrong" job a while ago, and the thing masked inpainting still owns is not disturbing everything else. A deterministic fill is the extreme version of that: it changes the hole and nothing else, ever, with no seed lottery.

The mask is inverted, and that's the whole article

Three inpainters live in this pack and they share one parameter name with two opposite meanings:

| Function | Non-zero in the mask means | | --- | --- | | cv2.inpaint (photo module) | the HOLE - repair these pixels | | cv2.xphoto.inpaint (contrib) | KNOWN - trust these pixels | | cv2.ft.inpaint (this node) | KNOWN - trust these pixels |

You paint a hole in ComfyUI's mask editor, so you have a hole mask. Handed to this node, non-zero means "known" - meaning it will faithfully keep your carefully painted blob and rebuild everything around it. The pack's own tooltip says it, the demo workflow says it, and everyone still does it once. Put a cv2.bitwise_not between your mask and this node:

MASK (hole = 1)  ->  cv2.bitwise_not  ->  mask   (hole = 0 = missing)
IMAGE ---------------------------------->  image

The inputs

image is the polymorphic socket and the one that decides the output format - IMAGE in, IMAGE out, MASK in, MASK out. mask takes the same types. radius is the radius of the fuzzy basic function, and it's roughly the widest gap the algorithm can bridge: a hairline needs 2–5, a hole that's 30 px across needs a radius in that neighbourhood, and the cost goes up with it.

function is the shape of the basic function, and it only offers LINEAR here. That's not the pack being lazy: the SINUS kernel raises on every call in the pinned OpenCV 5.0 build, so offering it would just be a broken dropdown.

algorithm is the interesting one:

  • MULTI_STEP - the default. Refines outward with several radii, automatically.
  • ITERATIVE - repeats the partial computation until it settles. Slowest, smoothest.
  • ONE_STEP - and here's the trap: it's broken on this OpenCV build. It returns NaN at every pixel where the mask is zero, regardless of radius or mask convention. The node's default is MULTI_STEP precisely because of that, and ONE_STEP stays selectable only so old saved workflows still load. Don't pick it because it sounds cheapest.

The output result echoes the input format, and the wrapper quietly re-casts depth for you - the ft module hardcodes 32-bit float output, so a uint8 IMAGE comes back as uint8 rather than as a float array you then have to normalise.

Install

Manager → 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"

Restart. ft is a contrib module, so if this node is missing from your menu or throws module 'cv2' has no attribute 'ft', you've got a non-contrib wheel overwriting the contrib one in the shared site-packages/cv2. tools/repair_opencv_contrib.py --check diagnoses it; --apply fixes it. No models to download - this one is pure maths.

Where people get burned

  • Non-zero = known. Not the other way round. cv2.bitwise_not first.
  • ONE_STEP gives you a NaN-speckled image on the pinned build. It looks like a bug in your pipeline; it isn't.
  • Radius is a reach limit. If your hole is wider than the radius can span, you get a plausible-looking smudge rather than an error - so preview the result instead of trusting the run.
  • If you actually want texture back (bricks, grass, fabric), this is the wrong tool. Frequency-selective extrapolation (cv2.xphoto.inpaint, FSR_BEST) reconstructs texture; the fuzzy transform reconstructs smoothness. Workflow 18_inpainting_playground.json in the pack wires all three side by side so you can watch the difference.
Categoryimage/CV/low-level/ft

Inputs (5)

NameTypeDefaultDescription
imageCOMFY_MATCHTYPE_V3Input 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.
maskNPARRAY,IMAGE,MASKMask used for unwanted area marking. 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.
radiusINT0-2147483648–2147483647Radius of the basic function.
functionCOMBOLINEARFunction type could be one of the following: - `ft::LINEAR` Linear basic function.
algorithmCOMBOMULTI_STEPAlgorithm could be one of the following: - `ft::ONE_STEP` One step algorithm. - `ft::MULTI_STEP` This algorithm automaticaly increases radius of the basic function. - `ft::ITERATIVE` Iterative algorithm running in more steps using partial computations. This function provides inpainting technique based on the fuzzy mathematic.

Outputs (1)

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