cv2.xphoto.inpaint
Read the mask twice, it's the opposite of cv2.inpaint
- src
- mask
- result
There are two functions called inpaint in OpenCV and they disagree about what a mask means. cv2.inpaint follows the convention everyone learns first: non-zero pixels are the hole to be filled. cv2.xphoto.inpaint, the one this node wraps, does the reverse: non-zero means the pixel is known and must be kept, zero means "fill this". Wire a normal hole mask into it and you will inpaint everything except the hole, which looks a lot like the node being broken.
Why you'd reach for it
cv2.xphoto.inpaint isn't a nicer cv2.photo.inpaint; it's a different class of algorithm. The classic TELEA/NS inpainters are diffusion-based and are honest about their limits - thin scratches, dust, a small speck, ideally a few pixels wide. Push them wider and you get the famous grey smear. xphoto's FSR (Frequency Selective Reconstruction) reconstructs in the frequency domain and, in the pack author's words, handles large missing areas rather than just thin scratches. That makes it the right tool for:
- Dust, hairs and scratches on a scan or film frame - its home turf.
- A watermark strip or a small occluder over otherwise textured content, where "copy the texture statistics" is a genuinely good answer.
- Round-tripping a repair pipeline you'll later run a generative inpaint over: FSR gives you the "classical baseline" cheaply and locally, CPU-only, no model, no VRAM.
If you want a face or a whole arm invented rather than texture continued, this is the wrong node - that's a diffusion inpaint job. FSR doesn't know what was there.
How it works
algorithmType picks the meat:
INPAINT_FSR_FAST- the default. Frequency-selective reconstruction: it estimates the dominant spectral content of the known neighbourhood and synthesises the missing block from it. Good on texture, seconds even on a big hole.INPAINT_FSR_BEST- the same idea with a finer search. Much slower, marginally better. Worth it on a hero shot, not on a batch.INPAINT_SHIFTMAP- different family entirely: it copies whole patches from elsewhere in the same image. Superb on repeating structure (brick, fabric, tiling), poor on smooth gradients, and it wants the image in a perceptual space (think Lab-like: intensity first, chroma after) - OpenCV's docs say as much, and that the colour layout matters.
The inputs that matter
src- the image with the gaps. For the FSR modes, grayscale or 3-channel BGR; forSHIFTMAP, anything from 1 to 4 channels, and again, the colour ordering is yours to get right.mask- single-channelCV_8UC1, same size as the source. Non-zero = known, zero = fill. Invert your hole mask (cv2.bitwise_not, or any invert/threshold node) before it gets here. The pack's own tooltip spells this out in capitals, which tells you roughly how often people get it wrong.algorithmType- the combo above, defaulting toINPAINT_FSR_FAST.
Output: result, in src's format. Low-level wrapper, so an IMAGE input still returns an NPARRAY - convert back with CV Array → Image.
Install
ComfyUI Manager → ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart. Dependency is opencv-contrib-python-headless~=5.0.0.93; xphoto ships only in the contrib wheel, and the pack's behaviour is only curated against that pinned version. The contrib/non-contrib clash is worth a sentence: OpenCV's four wheel variants all install into the same site-packages/cv2, so a plain opencv-python dependency in some other pack can silently strip the contrib submodules and take this node's dropdown with them. tools/repair_opencv_contrib.py --check then --apply is the fix. Python 3.12+ and a V3-API ComfyUI are both required.
Traps and gotchas
- Inverted mask convention. Covered, but again: non-zero = keep.
- Zero-size or mismatched mask - the mask has to be the same size as
src. A mask from a resized branch is a common silent mismatch. SHIFTMAPon smooth gradients produces visible patch seams, because it is literally copying rectangles. Switch to FSR there.- Your eye will lie about the result. Frequency reconstruction at full frame looks impressive; at 200% the reconstructed region is softer than its surroundings. Judge it at the crop level if the crop is where it'll be seen.
- This is one of ~470 auto-generated wrappers in the pack, and the wrapper only guards what the registry knows: the mask tooltip, the enum, the format echo. It won't stop you from feeding nonsense, so an odd crash from deep inside
cv2is on the input, not on the node.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| src | COMFY_MATCHTYPE_V3 | source image - #INPAINT_SHIFTMAP: it could be of any type and any number of channels from 1 to 4. In case of 3- and 4-channels images the function expect them in CIELab colorspace or similar one, where first color component shows intensity, while second and third shows colors. Nonetheless you can try any colorspaces. - #INPAINT_FSR_BEST or #INPAINT_FSR_FAST: 1-channel grayscale or 3-channel BGR 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. | |
| mask | NPARRAY,IMAGE,MASK | mask (#CV_8UC1), where non-zero pixels indicate valid image area, while zero pixels indicate area 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. | |
| algorithmType | COMBO | INPAINT_FSR_FAST | see xphoto::InpaintTypes |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |