Nodes/ComfyUI CV/CV Track Window
ComfyUI Node

CV Track Window

MeanShift tracking you can actually chain from frame to frame

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Track Window
  • density
  • window
  • window
  • bbox
  • box_edges
  • center
  • found
◄methodmeanShift (fixed-size window)►
◄iterations10►
◄epsilon1.0►
◄min_density0►

Histogram back-projection tracking is one of those things that sounds ancient and still works annoyingly well: build a colour histogram of the thing you want to follow, back-project it over each new frame so matching pixels light up, then slide a box uphill until it sits on the brightest clump. No model, no training, milliseconds per frame.

CV Track Window is the slide the box uphill part - one tracking step of cv2.meanShift or cv2.CamShift, exposed so you can loop it.

Why this node and not the raw wrapper

Because the raw one can't be chained. The generated cv2_meanShift wrapper returns the rect as a text literal, which you can read but not wire. This node keeps the window as a wireable NPARRAY, which means frame N's output window feeds frame N+1's input, and you have a tracker.

The pattern is one node per frame: your density map for that frame goes into density, the previous step's window goes in beside it, out comes the new window.

How it works

The density map is a single-channel likelihood map - CV Back Project output in the textbook case, but a threshold mask or a heatmap works too. Colour input gets converted to grayscale for you. The node hill-climbs: iteratively recompute the window's centre of mass and re-centre, stopping after iterations (default 10) or when the centre moves less than epsilon (default 1 px) between rounds.

  • meanShift keeps the window size fixed. Steady, predictable, wants an object whose apparent size doesn't change much.
  • CamShift also fits size and orientation as it goes. Better when the subject rotates or scales, and prone to ballooning on a weak or noisy density map - a classic way to end up with a box the size of the frame.

The gate is min_density: the minimum average density inside the resulting window for the step to count as found. Leave it at 0 and any stray pixel counts as a lock. On a 0-255 back-projection, raising it to something like 32 is the difference between a tracker and a box that wanders off.

Inputs and outputs

Required: density, window, method, iterations, epsilon, min_density. The window is four numbers (x, y, w, h) - seed the first frame with a CV Scalar literal, then feed it the node's own output from then on.

Five outputs, and they're more useful than you'd expect:

  • window - (x, y, w, h) int32, for the next frame. When found is false it echoes the input window unchanged, so the track holds its last position instead of falling over.
  • bbox - the same thing as core BOUNDING_BOX data, which plugs into the core Draw BBoxes node.
  • box_edges - the outline as four segments, ready for CV Draw Segments. Under CamShift this is the rotated box; under meanShift it's the plain rectangle.
  • center - a 1x2 array. Collect these and draw the trajectory with CV Draw Points or CV Draw Segments.
  • found - boolean. Branch on it with an if/else when you want to stop drawing a track that's been lost for a while.

Install

Manager → ComfyUI CV, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv

Restart. Python ≥ 3.12, current ComfyUI on the V3 node API, dependency is opencv-contrib-python-headless~=5.0.0.93 (contrib matters - a non-contrib opencv-python installed later over the top breaks most of the pack). No models involved: this is classical CV, which is exactly why it's fast. GPL-3.0, fork of opencv-comfyui, LLM-assisted code, no support promise.

Where people get burned

A bad density map, not the tracker. meanShift never questions the map - it climbs whatever mass is there. If your back-projection lights up the background as much as the subject, the box will cheerfully slide onto the background. Fix the back-projection before you tune the tracker.

CamShift ballooning. It grows the window to fit mass, so a smeared density means a growing box that never shrinks back. If the subject's outline is what you care about, meanShift plus a sensible min_density is the boring, better answer.

A window off the frame. The node clamps and returns found=false rather than raising - which is data, not a crash. An object leaving the frame is a real result, so handle it as one.

Losing the position convention. (x, y, w, h) - corner plus size, not centre plus size. Mixing that up gives you a window that looks like it's tracking something, just not the thing you picked.

Categoryimage/CV/features

Inputs (6)

NameTypeDefaultDescription
densityNPARRAY,IMAGEDensity/likelihood map the window climbs on - usually an 'CV Back Project' map. Color input is converted to grayscale. 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.
windowNPARRAYSearch window as 4 numbers (x, y, w, h): a 'CV Scalar' literal for the first frame, the previous 'CV Track Window' window output afterwards.
methodCOMBOmeanShift (fixed-size window)meanShift keeps the window size fixed (steady, needs a roughly constant object size); CamShift also fits size + orientation (box_edges shows the rotated box) but can balloon on weak densities.
iterationsINT101–1000Maximum hill-climbing iterations per step; 10 is the classic tracking value.
epsilonFLOAT1.00–100Stop when the window center moves less than this many pixels between iterations.
min_densityFLOAT00–255Minimum AVERAGE density inside the result window for 'found' to be true. 0 accepts any non-zero mass; raise it (e.g. 32 on a 0-255 back-projection) so a few stray matching pixels do not count as a locked track.

Outputs (5)

NameTypeDescription
windowNPARRAYTracked window (x, y, w, h) as int32 - wire into the next frame's window input. Echoes the input window when found=false.
bboxBOUNDING_BOXThe same window as core BOUNDING_BOX data ({x, y, width, height}) - chain into the core 'Draw BBoxes' node.
box_edgesNPARRAYThe box outline as 4 segments (4x4 endpoint quadruples) for 'CV Draw Segments' - CamShift's actual rotated box, meanShift's window rectangle.
centerNPARRAYBox center as a 1x2 float array - collect centers to draw the trajectory with 'CV Draw Points' or 'CV Draw Segments'.
foundBOOLEAN—