DPR GS Latent Noise
Turning a Message Into Sampling Noise
- LATENT
What it is
This is the embed side of the Gaussian Shading pipeline. You type a watermark message into it and it manufactures the noise tensor that DPR_KSamplerAdvanced samples from. The README's "plug-and-play" pitch is honestly accurate here: for normal use you change the message field and nothing else.
It's not a pixel-space watermark. You won't see a logo, and you won't see a pattern. What you get is a LATENT noise tensor that is statistically indistinguishable from plain Gaussian noise - that's the entire basis of the "performance-lossless" claim, and the code even runs a Kolmogorov–Smirnov test on the output and raises if the noise fails to look Gaussian.
How it works
In the pack's nodes.py, the message gets UTF-8 encoded and repeated to fill every latent position, then encrypted with ChaCha20 using the key and nonce. Each window of encrypted bits is mapped to a standard-normal sample via the inverse normal CDF. At the default window_size of 1, every latent element carries exactly one bit - which is why a 512×512 image can hold a few kilobytes of watermark in its 4×64×64 latent. The result is a tensor shaped like your latent (channels × height/8 × width/8), and the KS test above runs on it before it's handed back.
The inputs that matter:
message- the one you edit. Keep it short; it gets repeated across the whole latent anyway, and extraction majority-votes over the copies.keyandnonce- the defaults are baked into the pack, and DPR_Extractor must use the same values. A shared public default means this is provenance, not security: anyone with the pack can decode an image watermarked with the default key. Generate your own if you want it to mean anything.use_seed/seed- leaveuse_seedat 1 with a fixed seed and you get a reproducible watermark. Set it to 0 and the watermark is freshly randomized each run (fine, but you can't recreate the same noise twice).Image_width/Image_height- must match your EmptyLatentImage exactly, or the sampler downstream throws a shape error.channels- 4 for SD 1.5 and SDXL-style VAEs, 16 for Flux. Match your model's VAE latent channels; it's a 4 vs 16 story, not a "more is better" one.batch_size- stack 1–64 watermarked latents in one go.
Output: a single LATENT, meant for DPR_KSamplerAdvanced's watermarked_latent_noise input.
Gotchas
- Don't expect to see anything. The output is noise that looks like noise; the watermark only matters after it's been diffused into an image and inverted back out during extraction.
- Keep width, height and channels in sync with the rest of your graph, and use the same key, nonce and message on the extractor.
Install
One of three nodes in the ComfyUI-GaussianShadingWatermark pack - an unofficial reimplementation of Gaussian Shading (CVPR 2024) by lthero-big. Clone it into custom_nodes and restart:
cd ComfyUI/custom_nodes/
git clone https://github.com/lthero-big/ComfyUI-GaussianShadingWatermark.git
Or use ComfyUI Manager and search "GaussianShadingWatermark". Requirements are scipy, cryptography, numpy and friends - nothing heavy, no model files. It's a small research-grade pack, so take the baked-in default key and the minimal maintenance history as what they are: a clever experiment, not a finished product.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| use_seed | INT | 10–1 | — |
| seed | INT | 420–4294967295 | — |
| channels | INT | 44–16 | — |
| Image_width | INT | 51264–8192 | — |
| Image_height | INT | 51264–8192 | — |
| key | STRING | 5822ff9cce6772f714192f43863f6bad1bf54b78326973897e6b66c3186b77a7 | — |
| nonce | STRING | 05072fd1c2265f6f2e2a4080a2bfbdd8 | — |
| message | STRING | lthero | — |
| window_size | INT | 11–100 | — |
| batch_size | INT | 11–64 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LATENT | LATENT | — |