Image Auto Levels
The one-click contrast fix — levels that measure themselves
- images
- images
- black_point
- white_point
A flat render, a hazy plate, a washed-out scan - they all share the same problem: the image's tones don't actually use the range they're allowed. If nothing in the frame is darker than 20% grey and nothing brighter than 80%, the picture reads as grey and flat no matter how good the content is. The classic fix is levels: set a black point, set a white point, and stretch everything between to fill the range. The classic annoyance is that you have to look at the image and guess where those points should be.
Image Auto Levels removes the guessing. It measures where the frame's tones actually start and stop - finds the real black and white points in the data - and stretches the frame to fill the range. Flat render in, contrast out, nothing set by hand. That's the whole node, and it's a genuinely good default when you want "make this less washed out" without thinking about it.
How it works
The measurement is the mechanism, and the settings are all about protecting that measurement from liars:
method-on brightnessstretches all three channels by the same amount, keeping the colour as it was - pure contrast.per channelstretches red, green and blue separately, which adds contrast and pulls out a colour cast at once. Hazy or flat → on brightness; hazy and bluish → per channel.clip_low/clip_high- the share of the darkest and lightest pixels allowed to go fully black/white. This is the subtle but crucial knob.0.0means the single darkest pixel sets the black point - so one stuck pixel, one dust speck, one specular hit ruins the whole stretch. The default0.001ignores that thousandth of extreme pixels;0.02gives a deeper, more aggressive crush. If your image is a render, you almost never want0.0.strength- how far toward the stretched result the frame moves.1.0is the full stretch,0.5is halfway,0.0is untouched. When full stretch looks too crunchy, this is the dial that keeps the measured points but backs the effect off.
The batch behavior is quietly excellent: the whole batch is measured together, so a sequence of frames gets one consistent stretch rather than each frame flickering as its own measurements wobble. For video, that's the difference between a working grade and a strobing mess.
What comes back
Besides the images output, the node tells you what it found: black_point and white_point are the brightnesses the stretch read as black and white, on a 0-to-1 scale. white_point of 0.6 means the frame was two-fifths short of white before you fixed it; a value of 1.0 means it already reached white and there was nothing to gain. Wire those into a log, or just read them to know whether the node actually had work to do.
Installing it
WAS Node Suite v3, standard: ComfyUI Manager → search WAS Node Suite v3, or clone into custom_nodes, restart. No weights, no dependencies - it's a histogram measurement and stretch, running on torch.
Where does it fit versus the rest of the toolbox? This is the automatic version of what WAS Image Levels Adjustment does with two points you type in, and it belongs to the same deterministic post-processing family as gamma and sigmoid curves - instant, millisecond, no diffusion pass. Reach for it first on flat or hazy frames, add a gamma or curve on top when you want character, and save your re-rolls for problems that actually need a model. Auto Levels is where you start, not where you stop - but it's a very good start.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | The frames to stretch. The whole batch is measured together, so a sequence keeps a steady exposure rather than flickering frame to frame. | |
| method | COMBO | `per channel` stretches red, green and blue separately, which adds contrast and neutralises a cast at once; `on brightness` stretches all three by the same amount, which keeps the colour as it was. | |
| clip_low | FLOAT | 0.00100–0.2 | Share of the darkest pixels allowed to go fully black. 0.0 = the single darkest pixel sets the point, so one stuck pixel ruins it; 0.001 = a thousandth, which ignores those; 0.02 = a deeper crush. |
| clip_high | FLOAT | 0.00100–0.2 | Share of the lightest pixels allowed to go fully white. 0.001 = a thousandth, which ignores a specular hit; 0.02 = brighter, with more of the highlight blown. |
| strength | FLOAT | 1.000–1 | How far towards the stretched result the frame moves. 1.0 = the full stretch, 0.5 = halfway, 0.0 = the frame untouched. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | The stretched frames. |
| black_point | FLOAT | The brightness the stretch read as black, on a 0 to 1 scale. 0.0 = the frame already reached black and nothing was gained below. |
| white_point | FLOAT | The brightness the stretch read as white, on a 0 to 1 scale. 1.0 = the frame already reached white; 0.6 = it was two fifths short. |