LC Auto Adjust π
The levels button ComfyUI never shipped
- image
- mask
- image
You have an image that's fine except it's flat, and no node in stock ComfyUI fixes flat. So you do the thing everybody does: back to img2img with a low denoise, roll a new seed, watch the face change, and settle. This node exists so you don't have to. It's the Levels dialog from Photoshop, wearing a ComfyUI hat.
What it actually does
Auto levels. It measures the tonal range your image is actually using, then stretches it to fill 0β1. That's the whole trick, and it's deterministic - no model, no sampler, no seed lottery. Pure torch, no extra dependency, milliseconds.
The measurement is a 256-bin histogram of the pixels you selected. The stretch takes the lowest and highest bins that hold more than a bit of the population and maps them to black and white. Which pixels count as "selected" is where the modes come in.
The inputs you'll actually touch
mode picks what gets stretched:
- RGB stretches each color channel on its own, which also kills color casts - a too-blue shot gets its blue channel pulled back into line.
- lum + sat stretches saturation first, then luminance in Lab.
- luminance moves brightness only and leaves hues where they are.
- saturation stretches only the saturation channel.
- mono stretches a black-and-white version, so you get a grey image out.
strength (0β100, 0.1 steps) blends the result back toward your original. It defaults to 100, and honestly 70β80 is often the nicer look if the full stretch is clipping highlights you cared about.
Then brightness, contrast, saturation, and per-channel red / green / blue balance - all applied after the levels, all in 0.1 steps, all defaulting to 0. Positive balance brightens that channel, negative darkens it; the gamma curve on the node is gentle near 0 and steepens fast past Β±50, so use small numbers. Balance is ignored in mono mode.
The one worth understanding: the optional mask. Per the author's own tooltip, it limits both where the levels are measured and where they land. Masking a face means the histogram is built from the face only, so skin gets stretched against skin and not against a bright window. That's different from what most people assume, and it's the reason the node is genuinely useful in a portrait workflow rather than a one-trick levels button. Wire a face mask from LC Person Mask or LC Create Mask and it becomes a targeted exposure fix. If your image already has an alpha channel and you connect nothing, that alpha is used as the mask.
Output is a single image. It's an ordinary IMAGE, so it goes wherever a preview, a save, or the next node wants it.
Where it sits in the workflow
Think of it as the layer that stops you throwing a diffusion pass at a job a lookup table does perfectly. Regenerating to fix a color cast costs you a seed, a face, and thirty seconds of GPU - and gets you a different image, not a corrected one. Reach for this first, and only escalate to a re-gen when the problem is actually content.
Its natural partner is LC Image Blend Advance: auto-adjust a cutout before compositing it, so it stands a chance of matching the background it's landing on. Note what this node does not do - it levels your subject against its own histogram, it doesn't match it to the background's statistics. That's a color-match job, and it's a different node's business.
Install
ComfyUI Manager, search LC MaskMaker (the registry name is "ComfyUI LC MaskMaker Nodes"), or:
cd ComfyUI/custom_nodes
git clone https://github.com/lonecatone23/ComfyUI_LC_MaskMaker_nodes
# restart ComfyUI
Manager runs the pack's install.py, which only pip-installs packages that are missing and never touches torch, numpy or pillow. This node needs none of them anyway - it's torch-only, so it works even on a machine where the model-backed nodes in the pack are failing to load.
If it goes wrong
- Your whites look blown after stretching. Expected - auto levels assume your image is under-using its range. Pull strength down toward 70 and take contrast slightly negative.
- Colors shifted when you only wanted brightness. You're in RGB mode. Switch to luminance, which works in Lab and leaves chroma alone.
- The masked result looks washed out compared to the unmasked one. The mask narrowed both the measurement and the application, so the stretch is computed from a smaller slice of tones. That's the feature; if you want a global stretch applied locally, that isn't what this does.
- Mono still shows color. It shouldn't - mono expands a single grey channel to all three. If you're seeing color, you're looking at a downstream node's output.
One honest note on the pack itself: it's a single author's side project, not a famous node set, and it has essentially no discussion anywhere. What that buys you here is a small, readable, dependency-free node that does one deterministic thing - which is exactly what you want from the levels button.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | An alpha channel, if there is one, is kept and used as the mask when no mask is connected. | |
| mode | COMBO | RGB | RGB: stretch each color channel on its own (also removes color casts). lum + sat: stretch saturation, then luminance. luminance: stretch brightness only, colors stay put. saturation: stretch saturation only. mono: stretch a black and white version. |
| strength | FLOAT | 100.00β100 | How much of the result is used, in percent. |
| brightness | FLOAT | 0.0-100β100 | Trim after the auto levels. |
| contrast | FLOAT | 0.0-100β100 | Trim after the auto levels. |
| saturation | FLOAT | 0.0-100β100 | Trim after the auto levels. |
| red | FLOAT | 0.0-100β100 | Color balance: positive brightens the red channel, negative darkens it. Not used in mono mode. |
| green | FLOAT | 0.0-100β100 | Color balance for the green channel. |
| blue | FLOAT | 0.0-100β100 | Color balance for the blue channel. |
| maskopt | MASK | Limits both where the levels are measured and where the result is applied. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | β |