Nodes/ComfyUI ARG Toolkit/Invisible Watermark Decode
ComfyUI Node

Invisible Watermark Decode

Pull the hidden message back out of that image

By AzelusLightvale·Created 12 months ago·Updated about 10 hours ago· 1
Invisible Watermark Decode
  • images
  • STRING
length96
algorithm
types
encoding_format
other_encoding_format

Somewhere in an ARG there's an image that looks like a perfectly normal photo, and your job is to find out it isn't. IMWatermarkDecode is the tool for that second act: it takes an image and pulls the invisible watermark back out of it. It's the decode half of the pair with IMWatermarkEncode, both powered by the invisible-watermark library, and it returns the hidden text as a plain STRING.

What it needs

  • images (IMAGE) - the image(s) to decode. Works across the whole batch and concatenates the results.
  • algorithm - must match the encoder: dwtDct (fast but unreliable), dwtDctSvd (the author's recommendation, robust), or rivaGan (slow, experimental, 32-bit). The decoder also has to load the RivaGAN model if you pick it, so that path is extra slow.
  • types - must match the encoder's payload type: bytes, b16, bits, uuid, or ipv4. Getting this wrong is the classic failure.
  • length - this is the gotcha input. Invisible watermarks have no fixed message length for the byte-based types, so the decoder needs to know how many bits to read. The tooltip spells out the math: bytes wants 8 × message length bits, b16 wants 4 × the hex length, bits wants the raw bit count. Default 96, and if you're off, the decode returns garbage or throws. You generally have to know the length from the encode side - which means, yes, this node assumes you know what you're looking for.
  • encoding_format - how the recovered bytes decode to text; utf-8 default, Other for exotic codecs.

The output

  • STRING - the decoded message. The node is defensive here: if byte decoding fails (wrong encoding, or the bits aren't valid UTF-8), it falls back to printing the raw bytes representation instead of crashing - a small mercy that tells you "there's something here, but the encoding's off."

Installing it

Part of the ComfyUI ARG Toolkit:

cd ComfyUI/custom_nodes
git clone https://github.com/AzelusLightvale/ComfyUI-ARG-Toolkit

Restart ComfyUI, or install via ComfyUI Manager ("ComfyUI ARG Toolkit"). The invisible-watermark dependency pulls opencv-python and pywavelets, so expect a heavier-than-usual install on a lean ComfyUI setup.

Where people get burned

  • Not knowing the length. Decoding without the right length is the #1 source of "it returned gibberish." If the encode side isn't yours, you may have to brute-force lengths - try 96 first, then scale by your guess at the message size.
  • Mismatched algorithm or type. DWT+DCT can't read what RivaGAN wrote, and bytes payloads don't decode as b16. Match both to the encode side.
  • Degraded watermarks. Heavy editing, aggressive compression, or a small crop can destroy the bits. dwtDctSvd survives more of this than dwtDct, which the author notes is "unreliable" for good reason.
  • The library is experimental. The upstream project says it's not production-grade and runs on CPU only. Fine for a puzzle, worth remembering before you trust it for anything serious.

For the "this picture is a delivery mechanism" reveal, IMWatermarkDecode is the payoff node: feed it the image, and the hidden coordinates, UUID, or message comes out the other end. Just bring the length - this node doesn't do surprises.

CategoryARG Toolkit/Steganography

Inputs (6)

NameTypeDefaultDescription
imagesIMAGE
lengthINT961–2147483647
algorithmCOMBOIn order of appearance: - dwtDct: Discrete wavelet transform + Discrete cosine transform. Works, but unreliable. - dwtDctSvd (Recommended): Same as above, but includes singular value decomposition. - rivaGan: Uses a 32-bit RivaGAN implementation. Slow, experimental, and limited to 32 bits worth of information.
typesCOMBOIn order of appearance (and encoding length needed): - bytes (8 * total byte length): Transforms message from string to bytes. - b16 (4 * total b16 length): Text -> Byte representation -> Hexadecimal -> Byte representation. - bits (equivalent to array length): 8 bits = 1 byte. Good for transmitting small binary messages. - UUID (128 bits, fixed): Takes a UUID string and embeds that. Do not use to send messages. - IPv4 (32 bits, fixed): Takes an IPv4 address and embeds that.
encoding_formatCOMBOThe encoding format available for text, may yield different results when converting.
other_encoding_formatoptSTRINGIf, for some reason, your chosen encoding format is not available in the dropdown, select "Other" in encoding_format and type in your encoding format here. Supports all format written in Python's `encoding` module.

Outputs (1)

NameTypeDescription
STRINGSTRING