Nodes/Blind_watermark_Comfyui/πŸ“– Extract Text Watermark
ComfyUI Node

πŸ“– Extract Text Watermark

Read the invisible copyright message out of an image

By TianDongLΒ·Created about a year agoΒ·Updated about a year agoΒ· 22
πŸ“– Extract Text Watermark
  • image
  • extracted_text
β—„wm_length0β–Ί
β—„password_img1β–Ί
β—„password_wm1β–Ί
β—„processesautoβ–Ί

This is the payoff node for the whole text-watermarking idea. WatermarkEmbedText wrote a string invisibly into an image; this node pulls it back out and gives it to you as text. If you embedded a copyright notice into everything you post, this is how you check whether that leaked copy on someone's blog still carries your claim on it.

The workflow is deliberately simple: embed β†’ save the PNG β†’ (days later, after it's been re-encoded and cropped) load it β†’ extract. But "simple" hides one non-negotiable detail: you must feed it the same wm_length and the same two passwords the embed node used. Get any of those wrong and you get garbage.

The inputs that matter

  • image - the image you believe is watermarked.
  • wm_length - the watermark's length in bits, straight from the embed node's wm_length output. The tooltip says it: "get from embed node." There's no way to derive it from the image, so if you didn't keep it, you're stuck - which is exactly why the embed node exposes it as an output you can wire directly across. In a saved workflow, run the embed once and drag the wire from wm_length into this node's input and it's locked in.
  • password_img / password_wm - must match the embed exactly. These are the key; there's no guessing them from the image.
  • processes - CPU pool setting; "auto" is correct for everyone.

The single output is extracted_text (STRING). Wire it into any ShowText / text display node. Expect the recovered string to be exact if the image hasn't been mangled too hard - the library recovers the bits, not a fuzzy guess.

How it works

Same reverse-transform mechanism as the pack's other extractor, but for text: the carrier goes through DWT-DCT-SVD, each block's singular values are read to recover a bit, the bits are un-shuffled with password_wm, and the result is decoded from UTF-8 back into the string. The node also enforces a minimum image size based on wm_length (roughly max(int(sqrt(wm_length)*4), 128)) and refuses to run on images too small to plausibly hold the message.

Two failure behaviors to know. On invalid inputs it returns a string starting with ERROR: rather than crashing, so an ERROR: output is the node talking, not it breaking. And because the library decodes with error-tolerant UTF-8 (errors='replace'), a mangled watermark can come back as a string with replacement characters - which is your cue that the signal was damaged, not that the node is broken.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/TianDongL/Blind_watermark_Comfyui.git
pip install -r requirements.txt
# restart ComfyUI

Or "Blind Watermark" via ComfyUI Manager. CPU-only, no model files. Actual requirements: numpy, opencv-python, setuptools, PyWavelets, qrcode, pyzbar. Ignore the README's suggestion to install blind-watermark from pip - the pack vendors its own copy of that library.

Troubleshooting

  • Garbled text - passwords mismatch, wrong wm_length, or the image was re-encoded (JPEG saves are cumulative damage; save the watermarked file as PNG and don't re-save).
  • ERROR: output - the node's guardrails fired, usually the "image too small" check. Bigger carrier or shorter message.
  • Works on the fresh image but not the copy you found - that's the watermark's resilience being tested, and it lost. Heavy cropping or aggressive filtering will do that. Nothing to fix except expectations: this survives scaling, mild rotation and normal re-encoding, not a repaint.
CategoryBlindWatermark/Extract

Inputs (5)

NameTypeDefaultDescription
imageIMAGEβ€”
wm_lengthINT00–999999Watermark length (get from embed node)
password_imgINT11–999999β€”
password_wmINT11–999999β€”
processesCOMBOauto7 options: auto, 1, 2, 4, 8, multiprocessing, +1

Outputs (1)

NameTypeDescription
extracted_textSTRINGβ€”