Nodes/opencv-comfyui/OpenCV meanShift_0
ComfyUI Node

OpenCV meanShift_0

The 1990s tracker hiding in your node list

By geroldmeisinger·Created about a year ago·Updated about a year ago· 35
OpenCV meanShift_0
  • probImage
  • int
  • literal
window
criteria

meanShift_0 is a piece of classical computer vision that predates most of the things ComfyUI does, and it shows: it's a tracking algorithm. Given a window and a probability image, it iteratively slides that window toward the densest region of the probability distribution - think "follow the bright blob." It's the ancestor of the object trackers in every security camera and the thing OpenCV's older tracking demos are built on.

In the diffusion world, its honest use case is narrow: video. If you're working frame-by-frame on video output and want to track a region across frames (say, to keep a face crop stable before feeding frames to an image model, or to mask a moving subject), mean shift is a cheap, deterministic way to do it without running a detection model on every frame. You feed it a probability map - often a color backprojection, where pixels matching your target color are bright - and a starting rectangle, and it returns where the rectangle ended up after converging.

The node is part of opencv-comfyui's auto-generated ~635-node surface, so it comes with all the pack's rough edges, starting with the twin: meanShift_0 and meanShift_1 are the same overload pair (MatLike vs UMat) with identical inputs and outputs.

Inputs and outputs

  • probImage - one NPARRAY. This should be a single-channel probability/backprojection image, not a regular color photo. Feed it a 3-channel BGR image and OpenCV will assert on it (img.type() == CV_8UC1 is the classic complaint). The README's fix: convert with cvtColor to grayscale first if your source is single-channel-ish.
  • window - a STRING literal, parsed from Python syntax: the initial rectangle as (x, y, width, height), e.g. (100, 50, 60, 60).
  • criteria - a STRING literal: the termination criteria as (type, max_iterations, epsilon), e.g. (3, 10, 1.0) where 3 means "stop on count OR epsilon." This is the classic OpenCV TermCriteria; you'll see the same shape in CamShift and cornerSubPix.
  • Outputs int (iterations actually run) and literal (the updated window as a string-rendered rectangle, ((x, y, w, h))). The literal output is text, not an array - you read it or feed it to a string-capable node; it's not an image.

Setup and the gotchas

cd ComfyUI/custom_nodes
git clone https://github.com/geroldmeisinger/opencv-comfyui

or ComfyUI Manager → "opencv-comfyui" → install → restart (needs opencv-contrib-python, numpy, torch). The literal inputs are the trap: if you type 100, 50, 60, 60 without parentheses you'll get invalid syntax (<unknown>, line 0) from the pack's ast.literal_eval. Use full Python tuple syntax.

Real talk about expectations: wiring mean shift into a still-image workflow almost never makes sense - there's no temporal sequence to track. It earns its keep on per-frame video pipelines, and even then, frame batches are limited because Image2Nparray only accepts batch_size==1, so you process frames one at a time. For that specific job, though, it's a neat zero-download way to keep a window glued to a subject.

Categoryimage/OpenCV

Inputs (3)

NameTypeDefaultDescription
probImageNPARRAY
windowSTRING
criteriaSTRING

Outputs (2)

NameTypeDescription
intINT
literalSTRING