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

cv2.ft.FT12D_process

The fuzzy transform, upgraded so gradients don't come out as staircases

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
cv2.ft.FT12D_process
  • matrix
  • kernel
  • mask
  • nparray

Same shape as FT02D_process - image in, kernel, optional mask, float array out - but a different fit underneath. The F⁰ transform (FT02D_*) replaces each window with a single weighted average, which is why its output can look slightly terraced across a smooth gradient. The F¹ transform used here (FT12D_*) fits a plane to each window instead, so a ramp inside a window survives the round trip and only the noise gets averaged away.

If you're choosing between the two, that's the whole decision: F⁰ for flat regions and heavy noise, F¹ when your image has gradients that shouldn't come out banded.

How it works

The F-transform is a two-stage model fit rather than a convolution. The forward pass walks a (2r+1)² window over the image, where the window weights are the "basic function" supplied by cv2.ft.createKernel; the inverse pass reconstructs pixels from the overlapping window representations. F¹ keeps a linear term per window on top of the constant, so the reconstruction has a slope available. That extra degree of freedom is exactly why the F¹ family carries three coefficient outputs in its polynomial variant: an average, and a gradient in each direction.

process is the one-call version: forward and inverse in a single step. OpenCV's docs describe it as sufficient and optimised for the cv::Mat case, which is a polite way of saying you don't need the separate two-node version unless you want to touch the intermediate representation.

Inputs and outputs

  • matrix - IMAGE, MASK or NPARRAY. An IMAGE becomes a uint8 BGR frame, and only frame 0 of a batch: the ft functions aren't frame-batch safe in this pack, so a clip gives you one frame.
  • kernel - required, from cv2.ft.createKernel, with a channel count that matches the image (3 for BGR, 1 for greyscale). Radius is a property of the kernel: (2r+1) square, so radius grows cost quadratically. The freshly-created kernel defaults to radius 0, which is a 1×1 no-op.
  • mask - optional; non-zero = known, zero = excluded. Excluded pixels are reconstructed from their neighbours, which makes the masked mode a fill rather than a filter.

Output: one nparray, 32-bit float, in the same numeric range as your input. NPARRAYs don't preview, so Preview CV Array or CV Array → Image is how you look at it.

Does it beat a blur?

Sometimes. It's a weighted local fit, so it's more aggressive about preserving the local trend than a Gaussian of comparable radius, and unlike a bilateral filter it has no range term to tune and no intensity threshold to cross. What it isn't is fast: F¹ costs more than F⁰, which already costs more than a separable Gaussian. If you want edge-preserving smoothing and don't specifically want this maths, reach for a guided or bilateral filter instead and save yourself the mystery.

Where it earns its keep is in a pipeline that already speaks F-transform - feeding the same components into gradient computation, or reconstructing after you've edited the intermediate array - and in the noise-robust derivative work its sibling FT12D_polynomial does.

Install

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"

Restart ComfyUI. ft is a contrib module in a shared site-packages/cv2, so an ordinary opencv-python install can wipe the whole family out of your node menu; tools/repair_opencv_contrib.py --check is how you confirm that's what happened. Python ≥ 3.12, recent ComfyUI on the V3 node API, no model downloads.

Where people get burned

  • Radius 0. No error, no effect, wasted afternoon.
  • Mask direction. Non-zero means keep. A painted hole mask needs cv2.bitwise_not first.
  • Channel mismatch between kernel and image. Throws inside cv2 without saying "channels".
  • Treating the output as a picture. It's a float NPARRAY; the preview node will normalise it and can make a perfectly correct result look wrong.
Categoryimage/CV/low-level/ft

Inputs (3)

NameTypeDefaultDescription
matrixNPARRAY,IMAGE,MASKInput matrix. 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.
kernelNPARRAY,IMAGE,MASKKernel used for processing. Function `ft::createKernel` can be used. 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.
maskoptNPARRAY,IMAGE,MASKMask used for unwanted area marking. This function computes $F^1$-transfrom and inverse $F^1$-transfotm in one step. It is fully sufficient and optimized for `cv::Mat`. 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.

Outputs (1)

NameTypeDescription
nparrayNPARRAY—