ComfyUI Node

DPR Extractor

Reading the Watermark Back — and Why It's Not the Image

By lthero-big·Created 2 years ago·Updated 10 months ago· 5
DPR Extractor
  • latents
  • STRING
  • STRING
  • STRING
  • STRING
key5822ff9cce6772f714192f43863f6bad1bf54b78326973897e6b66c3186b77a7
nonce05072fd1c2265f6f2e2a4080a2bfbdd8
messagelthero
window_size1
watermarkMethodDPRW

The one thing to understand first

DPR_Extractor is the verification half of the Gaussian Shading pipeline, and it's where beginners get burned, because it does not take an image. The latents input is a LATENT: specifically, the starting noise recovered from your generated image via DDIM inversion.

Gaussian Shading hides the watermark in the sampling noise, not the pixels. So to read it you have to walk the sampling trajectory backwards - VAEEncode your generated image, run the same sampler in reverse (flipped sigmas plus an inverse Euler sampler), and feed that reversed latent in here. The pack's example workflows do all of this; dropping the extractor in cold and expecting an IMAGE input is the classic mistake.

How it works

For every value in the recovered latent, the node inverts the embedding mapping (a normal CDF) to get the encrypted bit back, decrypts the stream with ChaCha20 using the same key/nonce, majority-votes across the repeated copies of the message, and decodes the result as UTF-8. It then compares against the message you typed in and returns a bit-accuracy score.

The inputs:

  • latents - the DDIM-inverted latent, not pixels.
  • message - enter the same message you embedded. It sets the expected message length and is the reference for the accuracy readout. Leave it blank and you still get the decoded message, but no accuracy score.
  • key / nonce - must match the values you used in DPR_GS_Latent.
  • window_size - must match the embedding window_size (leave it at 1).
  • watermarkMethod - decorative. The code always runs the GS extraction regardless; the GS/DPRW dropdown is a promise the pack doesn't keep yet.

Outputs are four STRINGs: the original message in binary, the extracted binary, the extracted message, and accuracy as a percentage. Wire them into a text display node (the example uses pysssss's ShowText), and the pack also writes everything to its own timestamped log - it creates a ./logs folder wherever ComfyUI is launched from on first run.

The extraction workflow is a whole chain

You can't just slap this node into a graph. The example workflows ([GS]majicmixRealistic-DDIM-inversion.json for SD 1.5, plus PixArt and Flux variants) show the full inversion chain, and they need extra packs: an inverse sampler (SamplerInversedEulerNode, from the comfyui_InversedSampler pack) plus text/helper nodes. Load the workflow and ComfyUI will name whatever's missing - install those, then run.

Gotchas

  • Determinism is the whole game. Inversion must use the same sampler, scheduler and step count as generation, or the recovered noise won't match and you'll get garbage hovering near 50% accuracy.
  • As shipped, the extractor's Python method expects a message_length argument that no input in the UI provides - an authoring bug. If you hit a TypeError naming message_length, that's the pack, not your workflow; patch nodes.py to drop the parameter, or wait for a fix.

Install

The pack is an unofficial reimplementation of Gaussian Shading (CVPR 2024) by lthero-big. Clone it, restart, and install the inversion pack mentioned above:

cd ComfyUI/custom_nodes/
git clone https://github.com/lthero-big/ComfyUI-GaussianShadingWatermark.git

Or use ComfyUI Manager and search "GaussianShadingWatermark". Dependencies are scipy, cryptography, numpy and Pillow - nothing exotic, no model downloads. It's a research-grade pack, so expect to babysit the workflow a little; when it works, it's a neat provenance demo.

CategoryDPRW/extractor

Inputs (6)

NameTypeDefaultDescription
latentsLATENT
keySTRING5822ff9cce6772f714192f43863f6bad1bf54b78326973897e6b66c3186b77a7
nonceSTRING05072fd1c2265f6f2e2a4080a2bfbdd8
messageSTRINGlthero
window_sizeINT11–100
watermarkMethodCOMBODPRW2 options: GS, DPRW

Outputs (4)

NameTypeDescription
STRINGSTRING
STRINGSTRING
STRINGSTRING
STRINGSTRING