Donut Tone Lab · Learned Auto Tone
A 2,883-weight model that fixes flat gamma, not another diffusion pass
- image
- image
- report
What it actually is
A final-image automatic tone correction. Not a diffusion pass, not an img2img re-roll, not a LoRA. It measures your finished image, predicts a single global gamma and gain pair from those measurements, and applies it: out = clamp(in) ** gamma * gain.
That's a very small thing, which is exactly why it's interesting. The post-processing layer's whole lesson is that a gamma curve is a gamma curve, and you should reach for the millisecond deterministic operation instead of burning a sampler pass with its face-rewriting and seed lottery. This node is that idea with a tiny learned model picking the curve for you - 169 image statistics → 16 → 8 → 3, i.e. 2,883 weights and biases total. It ships as a JSON file and doesn't look at filenames, ratings or histories - no per-image overrides, no "it remembers your last ten images" business.
Reach for it when everything you generate comes out a touch flat or a touch hot and you're tired of nudging a gamma slider on every single frame. Don't reach for it expecting detail recovery - a global curve cannot invent clipped highlights or fix white balance.
How it works
The analyzer is a port of the pack's browser-based Tone Lab v4 trainer, and the feature order is a versioned contract (donut_srgb256_features_v4.0), so a model trained in the HTML tool means the same thing here. It builds an sRGB proxy of the frame, then measures luminance distribution, histograms, saturation and chroma statistics. Transparent pixels (alpha below 250) are excluded, and if there are fewer than 16 opaque samples the node deliberately does nothing rather than guess. The MLP returns gamma, gain and a no-change score.
strength scales the prediction exponentially - internally gamma ** strength and gain ** strength - so 1.0 is the trained prediction, 0 is exact passthrough, and 0.5 is a half-correction rather than a half-off switch. Applying to RGB only, alpha is preserved, and dtype/device are not quantized. It also writes a JSON report per frame, which is the honest way to find out what it decided.
The inputs
enabled(default false) - this is the real switch, and off is an exact passthrough with no model loaded at all. The node ships disabled on purpose and the V5 workflow wires it in that way.model_name-None,donut-tone-v4-r12.json,donut-tone-v4-general-synth.json, plus anything else ending in.jsoninComfyUI/models/donut_tone/. The bundled folder is last in the search path, so a same-named file in your own model root wins - prefer versioned filenames.strength(0–1, step 0.05) - start at 1, back it off if the grade is too strong for your taste.apply_to_edits(default false) - per the author's tooltip: off skips correction when V5 Editing is on, on grades the preserved surroundings too.edit_mode- an optional boolean input, wired from the workflow rather than toggled by hand. You can ignore it.
Outputs are image and report. The report is a JSON string: per-frame gamma, gain, whether it applied, the effective values, slider-equivalent numbers and the sample count. Wire it to a text display node if you want to see the thinking; otherwise leave it dangling.
Which checkpoint? r12 is the original photo-fit model (revision 12), tighter and conservative. general-synth was fit on a much larger synthetic random-offset set, so it generalizes across brightness content better but is calibrated much more loosely - and its gain head was never trained, so it corrects gamma only. The pack's own checkpoint card notes the two disagree substantially on flat, bright frames, so A/B them on your own images rather than trusting either blind.
Installing it
Whole-pack install, same as every other Donut node - Manager, search DonutNodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/DonutsDelivery/ComfyUI-DonutNodes.git donutnodes
cd donutnodes
python -m pip install -r requirements.txt
Both checkpoints ship inside the pack at models/donut_tone/ - nothing to download, no extra dependency. If you train your own in the pack's browser trainer (tools/donut_tone_lab_feature_learner_v4.html), export model only and drop the JSON in:
ComfyUI/models/donut_tone/my-tone-v1.json
Then refresh ComfyUI's model list. The model folder is a normal registered model root, so extra model paths work too.
Where people get burned
- "Tone Lab is enabled: select the bundled checkpoint…" - you enabled it and left
model_nameatNone. Pick a file. - "Invalid Tone Lab model-only JSON" - you exported a session instead of a model-only export. Sessions carry training backup data and are not node checkpoints. Related: files over 1 MiB are rejected outright.
- It "did nothing" and you don't know why. Read
report. It saysapplied: falsewith a reason -disabled,strength_zero, orediting_protectedwhen editing is on andapply_to_editsis off. That's the node being correct, not broken. - Realistic expectations. It predicts one global gamma/gain for the whole frame: not local exposure, not white balance, not clipped-detail recovery.
- Fresh-checkpoint caveat. The bundled checkpoint card is unusually straight about this: numerical compatibility is tested against synthetic proxies, and CUDA plus full live-workflow quality validation in the intended environment is still outstanding. A/B against your existing grade before you standardize on it.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| enabled | BOOLEAN | false | Off is exact passthrough; no model is loaded. |
| model_name | COMBO | None | Select bundled donut-tone-v4-r12.json or your Tone Lab v4 Export model only JSON. |
| strength | FLOAT | 1.000–1 | 1 uses the trained prediction. 0 is exact passthrough. Intermediate values scale log gamma/gain. |
| apply_to_edits | BOOLEAN | false | Off skips correction when V5 Editing is on. On globally grades preserved surroundings too. |
| edit_modeopt | BOOLEAN | false | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| report | STRING | — |