Nodes/ComfyUI-BFSNodes/BFS Video Compare
ComfyUI Node

BFS Video Compare

Two videos, one player, and a diff map that catches the frame you'd have missed

By alisson-anjos·Created 7 months ago·Updated about 17 hours ago· 126
BFS Video Compare
  • guide
  • target
  • audio
  • reference
  • difference
  • report
  • mean_difference
◄fps24.000►
◄gain2.0►
◄sizeguide►
◄preview_height720►
◄label_guideguide►
◄label_targettarget►

The problem isn't "watch a video"

You ran a shot loop with a body-swap or Edit Anything LoRA over 200 frames, and the join handed you guide (the footage the loop was supposed to follow) and images (what the model made). Now decide if it worked. So you scrub one preview widget, then the other, then back - and by the third pass you can't tell whether the face drifted at second four or you already checked that.

Humans are terrible at frame-exact comparison. This node puts both clips in one player - wipe, overlay, side by side - and computes the per-pixel difference on the server, so "where did it change" stops being an eyeball question. It's the last node of the BFSNodes shot loop but takes any two IMAGE batches, so people keep it installed without touching the planner. Useful timing, too: reference-driven video (IC-LoRAs, identity refs, guides) is now how you put a specific person in a shot, and its failure mode is quiet - fine in isolation, an identity slide against the source.

How it works

It all happens in Python, on the tensors, before the browser compresses a frame. Frames go in chunks of 16, each bilinear-scaled to whatever size you picked. Per pixel, the absolute RGB difference is averaged over the three channels, and that number drives both the per-frame mean difference (0–255) and a colour ramp for the map: black for equal, through red and yellow, to white for very different. The same pass gets the MSE behind the report's PSNR.

Then it writes three H.264 MP4s (CRF 14) into ComfyUI/temp/bfs_compare/<id>/ - guide, target, difference, audio on the target - and hands the browser those paths plus the per-frame numbers. A JS widget draws the player: wipe ↔, wipe ↕, overlay, or side by side, any of the three on top; click the strip under the scrubber to jump to a frame. ⬇ save and ● rec export the layout as MP4 or GIF.

Inputs that matter

guide and target are the only required ones, and the naming is the wiring: guide is the original, target is what you're judging.

fps is the one people forget. IMAGE batches carry no frame rate, so the node can't read it - this number drives player timing and the report's seconds. Leave it at 24 on a 30 fps clip and both lie.

size is which video's resolution both get scaled to; guide is the default because the join already puts its guide at comparison_height. preview_height (720) only sizes the player - the difference is measured first, so shrinking it changes nothing.

gain is sensitivity, default 2, and it's generous: the map saturates long before the numbers get interesting. Treat colour as "where", not "how much".

Optional reference takes up to 12 pictures - the refs list from BFS Shot Planner / Shot Unpack, or a batch - shown as a numbered strip under the videos. Handy for putting the face you asked for next to the face you got.

Outputs

difference is the map as an IMAGE batch, one frame per compared frame, at the compared resolution. report is a string - frame count, sizes, mean difference, PSNR, the most and least different frames, scaling notes - also printed to console. mean_difference is one float: mean absolute RGB difference over all frames, 0–255.

Most people wire none of them; the player shows all of it. But mean_difference deserves a home (a note, a threshold, a filename) - it turns "does this look okay" into a number you can track across seeds.

Install

ComfyUI Manager, search ComfyUI-BFSNodes, or:

cd ComfyUI/custom_nodes
git clone https://github.com/alisson-anjos/ComfyUI-BFSNodes.git
cd ComfyUI-BFSNodes && pip install -r requirements.txt

Restart. No models, no downloads.

Mind what that line drags in: torch, numpy, Pillow, safetensors, librosa, opencv-python, scenedetect. Only the first four matter here - the rest belong to the Shot Planner. FFmpeg isn't required either: it tries ffmpeg on PATH, then imageio-ffmpeg, then PyAV, which ComfyUI already ships.

Where people get burned

Length mismatch fails quietly. Different frame counts and it compares the first n - the shorter one. The player looks complete; the note lives only in report and the console.

Mismatched resolutions invent difference. Both are bilinear-resampled to the size video, and resampling the same content from two sources leaves edge halos - 1080p against 720p never reads as zero.

It's an output node, so it runs on every queue - three MP4 encodes per generation. Fine for a five-second shot, tedious overnight. The temp files are typed temp for a reason; export what you want to keep.

Blank player after a frontend update. Custom-widget nodes are exactly what breaks when the ComfyUI frontend gets rewritten - hard-refresh first. Node missing from the menu entirely? Check the startup console for [BFSNodes] Video Compare node not loaded:; the pack imports each node in a try/except and prints why it skipped.

CategoryBFS/shot loop

Inputs (10)

NameTypeDefaultDescription
guideIMAGEThe original video (e.g. BFS Shot Join's 'guide' output).
targetIMAGEThe result to compare with it (e.g. BFS Shot Join's 'images').
fpsoptFLOAT24.0001–240Frame rate of both videos (connect BFS Shot Join's 'fps').
audiooptAUDIOSoundtrack for the player (e.g. BFS Shot Join's 'audio').
gainoptFLOAT2.00.5–64How strongly differences light up in the map: a mean RGB difference of 1/gain (128/255 at 2) is already white. Raise it to see small changes.
sizeoptCOMBOguideWhich video's size the other is scaled to before comparing. The Join's guide is at comparison_height, so 'guide' is lighter.
preview_heightoptINT7200–4096Height of the player's videos (0 = the compared size). Only the preview: the difference is measured before it.
referenceoptIMAGEOptional reference pictures (e.g. the people swapped in), shown in a strip below the videos, numbered as <Picture n>, and in what ⬇ save / ● rec download. Takes the refs list of BFS Shot Planner / Unpack (pictures of any size) or a batch; up to 12.
label_guideoptSTRINGguide—
label_targetoptSTRINGtarget—

Outputs (3)

NameTypeDescription
differenceIMAGEThe difference map, one frame per compared frame (black = equal, white = very different).
reportSTRINGFrame count, sizes, mean difference, PSNR and the most / least different frames.
mean_differenceFLOATMean absolute RGB difference over all frames, 0-255.