CV DFT Flags
One flags node, many DFT calls — and the one OpenCV scaling trap it fixes
- flags
- value
CV DFT Flags authors a flags value once and fans it out. It has no image input, it changes no pixels - it's a value node for cv2_dft / cv2_idft, in the same family as CV Warp Flags, CV DCT Flags and CV Chessboard Flags.
Why does that need its own node? Two reasons, and the second is the important one.
Reason one: repetition. The pack ships ~470 auto-generated cv2_* wrappers, and each one exposes its function's flags as a parameter. A Fourier pipeline has several of them - forward transform, spectrum multiply, inverse transform - and all of them must agree on layout. Wire one flags node into all of them and the configuration has exactly one source. That's the standard "primitive value node" pattern: break the value out of the widget so the graph can fan out from a socket.
Reason two: it's a typed constant, not a magic number. In the UI the flags field renders as a base dropdown plus one toggle per flag, and the node's value output resolves those names to the actual integer by OR-ing the bits. So you don't have to remember that DFT_SCALE and DFT_REAL_OUTPUT are the flags you want for an inverse transform - you check two boxes.
The bits, and the trap
The toggles are DFT_COMPLEX_OUTPUT, DFT_REAL_OUTPUT, DFT_SCALE, DFT_INVERSE, DFT_ROWS and DFT_COMPLEX_INPUT, on a none (0) base.
For a forward transform you want DFT_COMPLEX_OUTPUT: that returns the full 2-channel (re, im) spectrum, which is the format the rest of the spectrum tooling - magnitude, mulSpectrums, masking - actually expects. Leave it off and OpenCV packs the result into the compact CCS layout instead, which is more memory-efficient and a nuisance to work with.
For the inverse, the usual answer is DFT_SCALE | DFT_REAL_OUTPUT. And here is the trap the node description is built around: OpenCV's inverse DFT does not divide by N unless you ask it to. Skip DFT_SCALE and your reconstructed image comes back N times too bright - a uniform scale error that is maddening to debug if you don't know it's a flag. DFT_REAL_OUTPUT collapses the conjugate-symmetric spectrum back to a single real channel, which is what you want for an image.
DFT_ROWS treats each row as an independent 1-D transform, and DFT_INVERSE is the flag form of cv2_idft.
Inputs and outputs
One input: flags, a string of pipe-joined constant names, e.g. none (0) | DFT_COMPLEX_OUTPUT. In the UI that's the dropdown-plus-toggles widget; if you edit it by hand, a typo raises a loud error rather than silently producing zero (the name resolution validates against the real OpenCV constants). Old workflows that saved while this was a plain combo keep resolving.
Two outputs:
flags- the pipe-joined string. Wire this into theflagsinput ofcv2_dftorcv2_idft.value- the resolved integer, for a rawintflags parameter or forMathExpression.
Install
Part of comfyui_cv (bmad4ever/comfyui_cv). Search "ComfyUI CV" in ComfyUI Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Then restart ComfyUI. Python ≥ 3.12 and a recent ComfyUI built on the V3 node API - this pack is a V3 rewrite, so an older ComfyUI will fail to load the whole pack rather than just this node. Dependencies are just OpenCV, numpy and torch; the OpenCV pin is deliberate and the wheel must be the contrib one, since all four distributions share one site-packages/cv2 and a plain opencv-python installed by another pack will quietly remove the contrib nodes. tools/repair_opencv_contrib.py --check diagnoses that, --apply repairs it.
Common issues
- The inverse transform is blown out.
DFT_SCALEmissing. It's the number one Fourier mistake in every language, and now you know. - The spectrum looks like a single-channel image when you expected two.
DFT_COMPLEX_OUTPUToff. The CCS layout is a valid packed representation, just not the one the magnitude/mulSpectrumsnodes take. DFT_ROWSon one transform and not the other. Layout flags have to match between forward, multiply and inverse, or the maths doesn't compose. This is exactly the bug the single-fan-out flags node prevents - use one node, wire it everywhere.- You wired
valueinto something expecting the string. The low-level wrappers take the string; rawintparameters and expression nodes takevalue. Both exist so you don't have to convert. - Everything about this feels like a lot of ceremony for four booleans. It is. But the alternative is typing
DFT_SCALE | DFT_REAL_OUTPUTinto three separate widgets and getting one of them wrong.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| flags | STRING | none (0) | Pipe-joined cv2 constant names, e.g. "none (0) | DFT_COMPLEX_OUTPUT". In the UI this renders as a base dropdown with one toggle per flag. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| flags | STRING | The pipe-joined flags string - wire it into the flags input of cv2_dft / cv2_idft nodes. |
| value | INT | The resolved integer (constants OR-ed together). |