DocRes Loader
The node you wire once and then stop thinking about
- model
ComfyUI's whole library is built for generating pictures. DocRes is the opposite kind of tool: it's a document-restoration model that takes a photo of a bent, shadowed, blurry page and hands you back a flat, clean one. No prompt, no sampler, no seed. It runs once and it either fixed your scan or it didn't.
The DocRes Loader is the boring half of that. It reads the weights off disk, puts them on the right device, and hands out a model handle that every task node in the pack shares.
Why a loader node at all
Because the model is expensive to load and cheap to reuse. The pack splits cleanly into a loader plus one task node per job (dewarping, deshadowing, appearance, deblurring, binarization), and that split is the same convention ComfyUI uses everywhere - CheckpointLoaderSimple feeding six samplers instead of six checkpoints in memory.
Under the hood the loader calls into a module-level cache keyed by model and device, so the Restormer backbone is read from disk exactly once per process. The object it returns travels along the wire as a bundle holding the model, the device, and the compute dtype, and a dewarper is attached lazily the first time a dewarping task asks for one. Run four restoration stages in a row and you paid for one load.
Inputs and output
One input, one output. That's it.
device - auto (the default), cuda, mps, or cpu. auto picks CUDA if you have it, then Apple's MPS, then falls back to CPU. Leave it on auto unless you're diagnosing something: forcing cuda on a machine without it raises "CUDA was requested but is not available" rather than quietly falling back, which is arguably the right behaviour but will read like a crash if you typed it in by hand.
model (DOCRES_MODEL) - wire it into the model input on DocRes Restore nodes. Fan it out to as many as you like; each node copies a reference, not a model.
The dtype is worth knowing about because it's where a lot of the pack's memory discipline comes from. Everything loads as fp16, matching the released inference code. That isn't just a speed choice - Restormer's decoder keeps full-resolution 96-channel activations, so a 2634x2096 scan gets heavy fast, and fp32 doubles every one of those buffers.
Install
Manager can do it: search for DocRes or comfyui-docres. By hand:
cd ComfyUI/custom_nodes
git clone https://github.com/zjkhurry/comfyui-docres.git
pip install -r comfyui-docres/requirements.txt
Then restart ComfyUI - the nodes land under image/DocRes.
The dependencies are the good news: einops, opencv-python, scikit-image, safetensors. Four lines, and three of them are already sitting in your ComfyUI environment. scikit-image is the only one that's genuinely added, and it's there for the Sauvola binarization prompt.
The weights are not in the repo. This loader only needs docres.safetensors (58 MB), which downloads from Hugging Face on first use into comfyui-docres/weights/. Nothing to do; first run is just slower.
Where it goes wrong
The first run looks frozen on a download. That 58 MB fetch happens inside the load call, so ComfyUI looks frozen for a few seconds with no progress bar. If you're offline or behind a proxy, it fails there. Drop the file into ComfyUI/models/ yourself and it's used as-is - the search order is the node's own weights/ folder first, then ComfyUI's model folders, then the Hub. A half-finished download isn't kept around; the partial file is deleted so the next run starts clean.
huggingface_hub isn't in requirements.txt. The auto-download path imports it, and while the packaging metadata lists it, the requirements file doesn't. In practice every normal ComfyUI install already ships it, so this only bites people running a hand-built venv. If the loader dies on ModuleNotFoundError: huggingface_hub, that's your fix.
Your own laziness is not the problem. Unlike most "why is my ComfyUI so slow" stories from the ecosystem, this loader can't leak: it's not per-image, it's not re-reading weights per run, and it doesn't hold a second copy for the DDC nodes - those load their own weights on their own.
One thing it deliberately does not do: prefetch the 227 MB MBD segmentation net. That only shows up when a dewarping task actually runs. If a dewarp node pauses the first time and never again, that's what happened.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| device | COMBO | auto | 4 options: auto, cuda, mps, cpu |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| model | DOCRES_MODEL | — |