ComfyUI Node

Image Autotone

Photoshop's Auto Tone, as a ComfyUI node — minus the Photoshop

By SparknightLLC·Created 2 years ago·Updated about a year ago· 24
Image Autotone
  • image
  • IMAGE
shadows0,0,0
highlights255,255,255
shadow_clip0.001
highlight_clip0.001

You know the look: the generation comes out flat. Blacks are muddy gray, highlights never quite reach white, and the whole thing sits under a faint green or blue cast you didn't ask for. Some checkpoints are worse than others at this - a lot of people first hit it on basic Flux Dev, which leans washed out. The fix used to mean exporting, opening a photo editor, and dragging a black-point slider around for ten minutes. Image Autotone is that black-point slider (and more) as a single node, no export required.

It's exactly what the name says: a reimplementation of Photoshop's Auto Tone command, ported from the [image_edit] shortcode of the author's own Unprompted extension for Automatic1111. The math is credited to Gerald Bakker's writeup of Adobe's auto-adjustment algorithms - a known algorithm, not a random eyeballed hack.

How it works

Auto Tone is a per-channel contrast stretch, and the "per-channel" part is the whole trick. For each of the R, G and B channels on its own, the node builds a 256-bin histogram of pixel values, then:

  1. Clips a fraction of pixels off each end of the histogram - that's shadow_clip and highlight_clip. The darkest surviving value becomes the new black point for that channel, the brightest becomes the new white point.
  2. Remaps the whole channel so those points land on the target colors, scaling everything in between.

Because each channel gets its own black and white point, the ratios between channels change - which is how it kills color casts while adding contrast. A blue-tinged image has a blue channel that starts high in the shadows; after the stretch it gets pulled down toward black like the others. It's the same family of trick as a levels adjustment, done per color and with percentile clipping instead of hard 0/255 endpoints.

The inputs that matter

  • image - the tensor you feed in. It's a normal IMAGE, so it slots anywhere after VAE decode, an upscaler, or another image node.
  • shadows / highlights - the colors the shadow and highlight points remap to. Defaults are 0,0,0 and 255,255,255 (plain black and white), which is the "fix a flat image" mode. But they accept any RGB value as "r,g,b" or "#rrggbb", and that's where it gets fun: set shadows to a warm orange and highlights to a cool blue and you've got instant teal-and-orange split toning - a grade that would normally take you into WAS Node Suite territory or another app entirely. This is the input that makes the node worth remembering.
  • shadow_clip / highlight_clip - fractions of pixels to clip off each end, 0 to 1, not a percentage. 0.001 (the default) clips the darkest 0.1% of pixels - a light touch. 0.05 is a noticeably punchier look; anything past ~0.1 starts crushing shadows and blowing highlights on purpose.

The output is a single IMAGE tensor, same batch size and resolution as the input, ready to wire into a Preview/Save node or onward into img2img. One output, nothing to puzzle over.

Installation

This is about the simplest install in the ComfyUI registry. Via ComfyUI Manager: search "Image Autotone" (or "ComfyUI-ImageAutotone") in the node list and hit Install. Or the manual route:

cd ComfyUI/custom_nodes
git clone https://github.com/SparknightLLC/ComfyUI-ImageAutotone

then restart ComfyUI. No model files, no weights, no VRAM - the whole thing is a handful of numpy ops that run in milliseconds on CPU. pyproject.toml declares zero dependencies beyond what ComfyUI already ships (numpy, torch). There is genuinely nothing to go wrong in setup.

Gotchas

Small pack, small list of gotchas, all straight from the source:

  • Clipping is a fraction, not a percent. shadow_clip = 0.5 isn't a subtle "half a percent" - it's chopping half your dark pixels, which will wreck the image. Start at 0.001–0.01.
  • A flat channel gets skipped. If a channel's clipped histogram collapses to a single value (light == dark), the code bails on that channel rather than divide by zero. Translation: if your image has a big flat region, that channel may come out untouched - that's a feature, not a bug, and it's why the cast-fixing works as well as it does.
  • It outputs RGB only. The loop processes channels 0–2, so don't feed it a pipeline where you need the alpha preserved.

Is it a game-changer? No - it's a one-trick node, and it's near-unknown. But it's a good trick: a deterministic, repeatable one-click fix for flat generations and off-color casts that works on batches, costs nothing, and has zero failure surface. For the "every image out of this checkpoint looks like a faded print" problem, it's the fastest fix in the graph.

Categoryimage

Inputs (5)

NameTypeDefaultDescription
imageIMAGE
shadowsSTRING0,0,0The color to use for the shadows in the image. This should be a comma-separated RGB value (e.g., '0,0,0' for black) or HEX string (e.g. '#000000').
highlightsSTRING255,255,255The color to use for the highlights in the image. This should be a comma-separated RGB value (e.g., '255,255,255' for white) or HEX string (e.g. '#FFFFFF').
shadow_clipFLOAT0.0010–1The percentage of pixels to clip from the shadows. This is a value between 0 and 1.
highlight_clipFLOAT0.0010–1The percentage of pixels to clip from the highlights. This is a value between 0 and 1.

Outputs (1)

NameTypeDescription
IMAGEIMAGE