BBoxes Scheduler
Lock One Person's Face Through the Whole Video
- bboxes
- bboxes
Here's the failure mode this node exists to kill: your face detector finds two people in every frame, you tell your workflow "crop person one's face," and halfway through the clip it quietly starts cropping person two. Detectors don't keep identity - they just report boxes, and when two faces cross, the box ordering flips. BBoxScheduler is the fix: it filters a per-frame BOUNDING_BOX list down to the person you actually want, and lets you write a tiny schedule that says "at frame 25, switch to the other person." It's the core of the BBoxNodes pack, and it's the node people who build multi-person Wan 2.2 animation reach for specifically.
How it works
It's pure bookkeeping, no AI involved. You hand it the detector's bbox list and a person_index, and for every frame it keeps only the box at that index - 0 means keep all, 1 means the first box in the frame, and so on, 1-based. The schedule input is where it gets interesting: a comma-separated string of frame_index:person_index pairs that overrides the person starting at the given frame. The author's example, 0:1, 25:2, 27:1, reads "use person 1 from frame 0, person 2 from frame 25, back to person 1 from frame 27." You set it once at the handful of frames where the boxes swap, and the node holds that person until the next schedule entry or the end of the clip.
There's also insert_dummy_bbox. When every box gets filtered out of a frame - say your person walks behind someone and their box vanishes - it inserts a one-pixel box at (0,0) instead of returning nothing. That sounds silly until your downstream face-crop node throws an error or silently breaks because it got an empty frame. With the dummy box, the crop node has something to chew on and the pipeline doesn't fall over. Turn it on for cropping pipelines, off when you want genuinely empty frames.
The output is the filtered BOUNDING_BOX list, which feeds straight into whatever crop node you're using for face extraction.
The inputs that matter
- person_index - which box to keep per frame (
0= all,1+ = that person). - schedule (optional) - the
frame:personstring above. Leave it empty to just useperson_indexfor every frame. - insert_dummy_bbox - the one-pixel safety box when everything's filtered out.
One honest caveat the author flags too: setting both person_index and a schedule technically works (they AND together - a box has to match both), but it's a confusing way to work. Pick one mechanism per pipeline.
Installing it
ComfyUI Manager, search ComfyUI-BBoxNodes, install. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/masternc80/ComfyUI-BBoxNodes
cd ComfyUI-BBoxNodes && pip install -r requirements.txt
Restart after, and remember the only dependency is pillow - no models, no heavy install. The pack does need a reasonably current ComfyUI because it's built on the new comfy_api.latest API; update ComfyUI if the node doesn't show up.
Troubleshooting
The two things that trip people up: the bbox input is not KJNodes-compatible (this pack's own BOUNDING_BOX type - socket won't connect to KJNodes output), and schedule indices are 1-based, same as person_index. If your schedule seems to be ignored, check you wrote frame:person with no spaces around the colon and commas between pairs - the parser is strict about that. And if you're wondering where the boxes jump, that's what the pack's visualize nodes (and their burned-in frame numbers) are for: find the jump frame, then schedule it.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| bboxes | BOUNDING_BOX | [object Object] | Bounding boxes (not compatible with KJNodes) |
| person_index | INT | 00–10 | The person index on the image starting from 1. 0 for all persons |
| insert_dummy_bbox | BOOLEAN | Insert dummy one-pixel bbox when all bboxes are filtered out | |
| scheduleopt | STRING | Schedule in format 'frame_index:person_index, ...' |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| bboxes | BOUNDING_BOX | Filtered out bboxes (Not compatible with KJNodes) |