Nodes/ComfyUI CV/CV Flow To Color
ComfyUI Node

CV Flow To Color

Look at your vector field without lying to yourself

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Flow To Color
  • angle
  • magnitude
  • nparray
◄angle_unitradians (0-2*pi, cartToPolar default)►
◄max_magnitude0.00►

You've got a field of per-pixel directions - optical flow, image gradients, whatever - and you want to see it. The obvious move is to dump the numbers as greyscale, and it's wrong: an angle of 359° and an angle of 1° are nearly the same direction and come out as black and white, so the wrap-around shows up as a fake hard edge everywhere. This node renders the standard way instead: hue = direction, brightness = magnitude.

It's the debug helper from bmad4ever's ComfyUI CV (bmad4ever/comfyui_cv, forked from Gerold Meisinger's opencv-comfyui). A separate node rather than a display mode, in keeping with the pack's rule that data nodes never draw - you choose what gets rendered and what stays numbers. There's a companion in the same pack, CV Draw Flow Grid, which draws a sparse arrow grid instead of a dense colour field; that's the better choice when you care about a handful of sample points.

Inputs

angle - an HxW angle field. It pairs directly with cv2_cartToPolar, whose two outputs are exactly an angle map and a magnitude map; that's why the tooltip says what it says.

angle_unit - radians (0–2π) or degrees (0–360). Match what produced the data: cartToPolar defaults to radians, and a degrees/radians mix-up gives you a rotationally scrambled picture that looks like a bug in your flow. The pack's own CV Optical Flow (Farneback) node emits radians specifically so it plugs in here with no conversion.

magnitude (optional) - an HxW field of magnitudes, shapes must match. With it, weak vectors fade toward black and strong ones burn bright. Without it, every pixel renders at full brightness and you only see direction, which is fine for a quick orientation check and useless for judging confidence.

max_magnitude - the value mapped to full brightness; larger values clip. Leave it at 0 for auto (the field's own maximum), which is the right default when you're looking at one field. Set it explicitly to compare two renders on the same scale - auto-scaling makes a nearly-static frame look as busy as a fast one, and that's the mistake this knob exists to prevent.

Output

nparray - a BGR uint8 rendering. Hue carries the angle (0° at hue 0, then round the wheel - OpenCV's hue range is 0–179, so the degrees get halved internally), value carries the magnitude. Wire it to CV Array → Image or a preview node.

The tooltip carries a caveat worth repeating because it's the kind of thing that gets glossed over: direction is encoded in hue alone, and hue is exactly the axis a colour-blind reader loses. Read this as a picture of where the motion changes and how coherent it is. When you need the actual angle at a pixel, take it off the numbers with Inspect CV Data, not off the colour.

Install

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

Or search ComfyUI CV in ComfyUI Manager. Python ≥ 3.12, recent ComfyUI with the V3 node API. Example workflows referencing it: workflows/39_optical_flow_playground.json (the full flow playground), workflows/76_raft_optical_flow_dnn.json, workflows/06_edges_gradients_playground.json and workflows/03_debug_visualization_playground.json.

Common issues

A hard seam across the image. If you rendered angles as greyscale, that's the 0/360 wrap and it's your renderer's fault, not the data's. This node is the fix. If you're already using this node and see a seam, your angle field is being fed in the wrong unit.

Everything is dark or everything is white. Dark: magnitudes are tiny and max_magnitude is set way too high. White: no magnitude connected, so every pixel is at full brightness by design.

It doesn't resemble the flow you expect. Check the field before you blame the render - cartToPolar on a non-dense input, or a flow field's dx/dy handed over as if they were angle and magnitude, both produce colourful nonsense. Print the array's shape and range with an inspect node first.

Categoryimage/CV/features

Inputs (4)

NameTypeDefaultDescription
angleNPARRAYHxW per-pixel angles (cv2_cartToPolar output).
angle_unitCOMBOradians (0-2*pi, cartToPolar default)Set to degrees when cartToPolar ran with angleInDegrees=true.
max_magnitudeFLOAT0.000–1000000000Magnitude mapped to full brightness; larger values clip. 0 = auto (the field's own maximum) - set it explicitly to compare renders across images on the same scale.
magnitudeoptNPARRAYHxW per-pixel magnitudes; scales brightness so weak vectors fade out. Must match the angle shape.

Outputs (1)

NameTypeDescription
nparrayNPARRAYBGR uint8 rendering: HUE is the angle (0 deg / east at hue 0, then round the wheel) and BRIGHTNESS is the magnitude. Direction is carried by hue alone, which is the axis a colour-blind reader loses - read this as a picture of where the motion changes, and take the angle itself off the numbers ('Inspect CV Data') rather than off the colour.