Apply Blind Watermark
Apply Blind Watermark
- original_image
- watermark_image
- image
You generate something you're proud of, share it, and a week later it's on someone else's feed with their handle on it. This node is the cheap, local answer to that: it bakes a second image - a watermark - invisibly into your output, and you can extract it later to prove the image came from your pipeline. No API, no external service, no key, no GPU. It just works on the CPU, and the watermarked image looks indistinguishable from the original to the human eye.
It's part of ComfyUI-BlindWatermark by lihaoyun6, a port of the well-known fire-keeper/BlindWatermark library. Same author as the hugely popular FlashVSR_Ultra_Fast video upscaler, so this isn't some abandoned weekend script - the pack got a performance-focused update as recently as late 2025.
How it works
The math is older than diffusion and it holds up surprisingly well. The node converts your image to YUV color space, runs a one-level Haar wavelet transform (a "DWT"), chops the result into 6×6 blocks, DCT-transforms each block, and then uses SVD to tweak the largest singular value by a fixed amount. Whether that tweak is "add a bit" or "add more" encodes one pixel of your watermark. The watermark image gets squashed to a 64×64 square, its pixels are shuffled with one seed, and each block's DCT coefficients are shuffled with another seed - both seeds are derived from the seed you provide. To extract the watermark you need that exact seed; without it, the pixels are just noise.
The inputs that matter
Three inputs, that's it:
- original_image - the image you're protecting.
- watermark_image - any image; it gets resized to 64×64 and flattened to a square, so aspect ratio is ignored. A logo or a small signature works fine.
- seed - any integer up to ~1.1 quadrillion. This is the whole game: write it down, because decoding requires the same number.
The output is a single image tensor that you'd wire into a Save Image node. Note the node forces a re-run on every queue (IS_CHANGED is always true) and the result is RGB - any alpha channel gets dropped.
Installing it
The pack has no model files and no heavy dependencies. Install via ComfyUI Manager by searching "ComfyUI-BlindWatermark", or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/lihaoyun6/ComfyUI-BlindWatermark
cd ComfyUI-BlindWatermark
pip install -r requirements.txt
Restart ComfyUI. The requirements are pillow, opencv-python, numpy, pywavelets, and numba - all CPU packages. First run can stall for a few seconds while numba JIT-compiles; that's normal, not a hang.
Where people get burned
- Forgetting the seed. It's not a password; there's no recovery. Lose it and the watermark is gone forever. Store it next to the image.
- Too-small images. If the input is small relative to the 64×64 watermark (blocks don't fit), the node raises a
RuntimeError- the "Advanced" sibling node lets you shrink the watermark to fix this. - Expecting the impossible. DWT-DCT-SVD watermarks survive JPEG compression, mild resizing, and light cropping. They do not survive someone re-running the image through img2img or a denoise pass - re-diffusion scrubs them like it scrubs SynthID. This is provenance insurance against casual theft, not DRM against a determined attacker.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| original_image | IMAGE | — | |
| watermark_image | IMAGE | — | |
| seed | INT | 00–1125899906842624 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |