Nodes/VideoSeal Watermarking/VideoSeal Detect Watermark
ComfyUI Node

VideoSeal Detect Watermark

Read the invisible watermark back out — the other half of VideoSeal

By jc2shile·Created 2 months ago·Updated 2 months ago· 1
VideoSeal Detect Watermark
  • model
  • images
  • message
  • confidence
  • detection_map
is_videotrue
aggregationavg
batch_size32

Embedding a watermark is only half the job - at some point you need to prove it's there. VideoSeal Detect Watermark is the output node that does the reading: feed it video frames, and it decodes whatever VideoSeal message is buried in the pixels and hands you back the ID, a pass/fail verdict, and a plain-text report you can read in the UI. It's the verification half of the aperire/ComfyUI-VideoSeal pack, the counterpart to VideoSeal Embed Watermark.

This is the node you run when someone re-uploads your render, or when a client swears they never touched a file, or just when you want to check your own pipeline is actually stamping frames. Load the suspect video into ComfyUI, extract the frames, wire them in, and read the answer off the report output without even opening a console.

How it works

The model's extractor runs over every frame and produces a soft estimate of each message bit - a per-pixel logit that says "this bit is probably a 1." A single frame is noisy, so the node merges across all frames using an aggregation method (default avg; the squared and L1/L2-norm variants weight high-confidence frames more heavily). More frames, cleaner result.

Then it decodes the merged bits, trying formats in order:

  • a Reed–Solomon-protected wm_v1_ ID (this is what PixelSeal's canonical IDs use - it can correct up to eight corrupted bytes),
  • a plain text ID with its CRC32 checksum,
  • and, if you gave it an expected_watermark_id starting with wm_v1_, the legacy wm_v1_ format for backwards compatibility.

Whatever it lands on becomes decoded_watermark_id, and format_valid tells you whether the bits even formed a recognizable watermark at all. frames_checked is just the number of frames you fed it.

The input that makes it useful

Everything except two things has a sensible default:

  • expected_watermark_id - the interesting one. Leave it empty and you get whatever ID decodes (great for checking an unknown video). Fill it in with the ID you embedded and the node also computes bit_accuracy_percent - the exact bit-by-bit match between what's decoded and what you expected, which is the honest "is this really mine, and how badly is it damaged" number. You also get a confidence_percent in the report.
  • model - must match the model used to embed. videoseal-embedded content needs videoseal; PixelSeal content needs pixelseal. This is the single most common mistake, and it produces garbage instead of a clean ID.

chunk_size (default 16) sets frames per pass, device is auto. Outputs: decoded_watermark_id, format_valid, bit_accuracy_percent, frames_checked, and the report string that bundles all of it (plus payload encoding and confidence) into the UI. It's an output node, so it runs last in the graph - that's expected.

Install

Same pack as the embed node. ComfyUI Manager, search ComfyUI-VideoSeal, install, restart. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/aperire/ComfyUI-VideoSeal
# restart ComfyUI

No pip packages are installed by the pack; the model checkpoint downloads on first use into a ckpts/ folder in your ComfyUI root.

Where people get burned

  • Model mismatch. Embed with pixelseal, detect with pixelseal. Crossing models gives you a confident-sounding wrong answer.
  • Expected ID left empty. You still get decoded_watermark_id, but bit_accuracy_percent comes back as not checked. Set the expected ID if you want the pass/fail number.
  • Too few frames. Aggregation is what makes detection reliable - one or two frames decode poorly, especially after compression. Feed it a meaningful chunk of the video.
  • First-run download and missing imports. The checkpoint downloads on first detect, and if the env lacks the vendored upstream's imports (omegaconf, timm, PyWavelets, scikit-image) you'll hit ModuleNotFoundError; pip install them and retry.

Reasonable expectations: this is not magic. Invisible watermarks survive ordinary recompression and re-uploads, but a deliberate scrub with crops, resizes, and re-encodes can kill them. Treat detection as strong evidence of provenance, not a forensic guarantee.

CategoryVideoSeal

Inputs (5)

NameTypeDefaultDescription
modelVIDEOSEAL_MODEL
imagesIMAGE
is_videoCOMBOtrue2 options: true, false
aggregationoptCOMBOavg4 options: avg, squared_avg, l1norm_avg, l2norm_avg
batch_sizeoptINT321–256

Outputs (3)

NameTypeDescription
messageSTRING
confidenceFLOAT
detection_mapIMAGE