single_image_deducker
Pull a hidden image out of a carrier, right inside your graph
- image
- image
single_image_deducker is a steganography decoder, not an AI node. No model to download, no API to call, no key to hunt for - feed it a carrier image that has a hidden image buried in its pixels and it digs the payload back out as a normal IMAGE tensor. The name starts making sense once you learn "duck" is a small Chinese stego toolchain (the node even ships with buttons to the author's online decode tool, tutorials, and a couple of QQ groups). The encode side lives in that ecosystem; this node is the decode side, and it's the only part that lives in ComfyUI.
Why you'd reach for it
It's niche, but if you're in the right place it saves a step nobody else saves. Someone shares a "ducked" image - watermarked, or holding a private image inside a harmless-looking carrier - and instead of saving it to disk, firing up a separate stego app, and loading the result back in, you just drop it in and the recovered image flows straight into whatever comes next: Save Image, an upscaler, img2img, a VAE decode. No file juggling. If you're not trading duck-encoded images, you don't need this - but when you do, there's no other node doing it.
How it works
Classic LSB steganography with a few honest engineering choices. The decoder reads the low bits of each color channel and tries three bit-depths - 2, 6, then 8 - until a valid header parses. One detail worth knowing: it deliberately skips a region in the top-left corner (40% of the width by 8% of the height), because that's where the encoder's watermark/logo lives. The decoder knows not to read payload out of the part of the image that's meant to be a badge.
The payload header is compact: a length, a flag for whether a password was set, and if so a salted SHA-256 hash plus the salt. Password-protected payloads are XOR'd with a keystream derived from your password and salt; give the right one and it decrypts cleanly, get it wrong and you get 密码错误 ("wrong password"). The header also carries the original filename extension, and that's the gate: only png, jpg, jpeg, bmp, and webp are allowed through. Anything else - a hidden video or text file - raises 解码结果不是图片 ("decoded result is not an image"). This node is single-image only, and it means it.
Inputs and outputs
Only two inputs, and one output you'll actually use:
image(required,IMAGE) - the carrier. Wire it from aLoad Imagenode (or any image you already have in your graph).password(optionalSTRING, default empty) - leave it blank unless the source image was protected, in which case the author's tool set a password on encode.- Output:
image(IMAGE) - the recovered hidden image. Wire it into anything that takes an image. One gotcha: a batched tensor gets flattened to its first frame, so feed it single images, not a video batch.
Install
ComfyUI Manager, search for single_image_deducker - or the manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/arctan90/single_image_deducker
Then restart ComfyUI. Its requirements.txt is just pillow and numpy, which any working ComfyUI already has (torch ships with ComfyUI), so there's no dependency hell and no model files to fetch. This one is refreshingly light in a custom-node ecosystem where half the installs come with a 2GB checkpoint.
Troubleshooting
The errors it throws are the README, in practice:
解码结果不是图片- the hidden payload is a video or text file, and this node only unpacks images. Use the author's desktop encode/decode tool for those.密码错误/需要密码- you hit a password-protected payload with the wrong password, or no password at all. Get the real one.- Payload-length / capacity errors - the carrier is corrupt, recompressed, or simply not a duck-format stego image. Try the original file; heavily compressed or resized carriers lose their payload and can't be recovered.
- Import error on startup - the pack isn't in
custom_nodes, orpip install -r requirements.txtnever ran.
One honest caveat to close on: stego images are a two-way door, and the wider community treats "hidden file inside an image" with justified wariness. This particular node is safe in that it only extracts validated image bytes and errors on everything else - it can't unpack a payload as an executable. But if a stranger hands you a mysterious carrier, the sensible habit is to think about what's inside before you ask a machine to dig it out.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| passwordopt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |