Save Bounding Boxes
Cache the detection pass before you re-render anything
- bboxes
- bboxes
Every automatic detailing pipeline has the same cost profile. Detection is the expensive half - a YOLO pass, or a SAM3 track across a hundred video frames - and the re-render is the half you're actually iterating on. So re-running detection because you changed a denoise value is pure waste. This node writes the boxes to disk so you can feed them straight back in next time.
It takes a BOUNDING_BOX and writes JSON to output/bboxes/ with a .cbbox extension. It's an output node, so it always runs, and it passes the boxes through on the way out - you can leave it inline in the graph and keep going.
How it works
The file is plain JSON, which is the nice part: you can open it, diff it, or fix a coordinate by hand. The document is a small envelope - format: "mvex_bboxes", format_version: 1, a frame_count, and then the payload under bboxes. The loader validates that envelope, so a random JSON file won't sneak in.
The payload normalizes to one of two shapes. If you hand it a single box - a dict with x, y, width, height, which is what core's PrimitiveBoundingBox emits - it stores that one box and records frame_count: 1. That's a broadcast box: it means "this rectangle applies to every frame." If you hand it a list of frames, where each frame is a box or a list of boxes, it stores the per-frame lists and frame_count is the real length. It also walks the usual nesting (an object with a bboxes key), so packs that wrap their boxes in a small struct still work. Coordinates that arrive as tensors get .item()'d to plain numbers, and if your detector stamped source_width / source_height onto the input, those ride along into the file.
Nothing else does. That's the trade-off worth knowing up front: the boxes are pixel coordinates in the frame you detected on, and the file has a slot for the resolution but doesn't enforce or check it. Save boxes from a 720p pass, feed them to a 1080p graph, and you get rectangles in the wrong place.
The inputs that matter
bboxes- theBOUNDING_BOXto write. Required.subfolder- where underoutput/the file lands. Defaults tobboxes.filename_prefix- only used when nothing else names the file, defaultbboxes.basename/video_filename/timestamp- the optional naming trio. Wirebasenamefrom Build Media IO Basename (same pack) and every artifact from that run shares one stem; otherwisevideo_filenamegives you{stem}_{timestamp}; otherwise you getbboxes_00001.cbboxand an incrementing counter.
Resolution order is: explicit basename (used as-is, no counter), then video_filename + timestamp, then filename_prefix + counter.
Output is bboxes, the same BOUNDING_BOX - normalized, not your original object, so a broadcast box comes back as a single box dict rather than whatever wrapper it arrived in.
Install
Manager, search the pack title, or:
cd ComfyUI/custom_nodes
git clone https://github.com/swanjohn99/comfyUIcostumNodes
Restart. No pip install, no model downloads - the pack's pyproject.toml lists zero dependencies, and the saver only needs folder_paths. The README in the repo doesn't cover this node; it documents the mask nodes from an earlier version of the pack, so read the source if you want detail.
Where people get burned
Two things to know. First, .pt is refused on purpose, both here and in the loader. ComfyUI's model scanner sees .pt in an output folder and starts reporting phantom missing models, so the pack uses .cbbox and rejects the old extension outright. Second, the pass-through hands you the normalized box, not the object you fed in. A single box broadcast to five consumers still works fine inline, but any extra fields your upstream detector attached are gone - only x, y, width, height, and the two optional source dimensions make it through.
And if a save throws an error about coordinates, that's the type check talking: every box needs all four keys and numeric values. A None in a detected box list is a mistyped detector output, not a limitation of the saver.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| bboxes | BOUNDING_BOX | — | |
| filename_prefix | STRING | bboxes | — |
| subfolder | STRING | bboxes | — |
| basenameopt | STRING | — | |
| video_filenameopt | STRING | — | |
| timestampopt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| bboxes | BOUNDING_BOX | — |