Skin Tone Uniformity
Fix splotchy AI skin without melting it to plastic
- image
- image
- mask_preview
Portraits straight out of a diffusion model have a tell: the skin is right in patches, but not uniform. One cheek is pinker than the other, the forehead has a blotch, the neck is a different tone entirely. Skin Tone Uniformity is the Darkroom pack's answer - it evens skin color out while deliberately leaving the texture alone. That last part is the whole trick.
How it works
This is a mask-weighted mean pull, not a blur. The node figures out which pixels are skin using hue/saturation/luminance ranges, then pulls those pixels toward the mean skin color of the region. Because it operates on color, not brightness, pores, grain, and texture survive. That's what separates it from every "skin smoothing" filter that just wipes detail.
The smoothing_radius control decides how local the pull is. Low values mean each skin patch averages toward its own neighborhood - good for fixing one blotchy cheek. High values mean everything averages toward the whole-frame global skin tone - good for making a mixed-lighting face read as one color. Default 60 is a reasonable middle.
Pick a preset for the skin type you're dealing with - Universal, Light/Fair, Medium/Olive, Dark/Deep, Warm/Golden, Cool/Pink - and amount (0–100) sets how aggressively it evens things out. The presets handle the HSL detection for you.
The two outputs are both important
Most Darkroom nodes give you one output. This one gives you two:
image- the corrected result.mask_preview- a visual of exactly which pixels the node thinks are skin.
Wire mask_preview into a preview node the first time you run it. If it's painting over hair, lips, or background, that's your signal to either switch presets or drop to Custom and set hue_center, hue_width, saturation_min/max, and luminance_min/max yourself. The mask output is a genuinely thoughtful touch - half the battle with skin-tone tools is knowing what they're touching, and here you get to see it.
Practical notes
- Don't chase amount=100. A fully uniform result reads as mannequin skin. 40–70 is where the magic lives; leave the
strengthblend at 1.0 and control the effect throughamount. - It's a spatial filter, so it can't be LUT-baked. The README's bake chain explicitly excludes it (it uses pixel neighborhoods), so if you're exporting a .cube grade, apply this after extraction, not inside the chain.
- Works in the Darkroom/Raw subcategory for a reason - it's a finishing step, meant to run after your exposure and white balance, before or alongside grain.
Installing it
It's part of the full Darkroom pack, so install once and every node appears:
cd ComfyUI/custom_nodes
git clone https://github.com/jeremieLouvaert/ComfyUI-Darkroom.git
pip install -r ComfyUI-Darkroom/requirements.txt
Or search "Darkroom" in ComfyUI Manager. Needs scipy, opensimplex, rawpy, exifread, Pillow. Restart, and you'll find it under AKURATE/Darkroom/Raw. No GPU, no model downloads, nothing to license - it's pure numpy math, and it's fast.
It's not a miracle worker on badly out-of-focus or hugely inconsistent skin, but for the standard "great in patches, uneven as a whole" problem, it's the cleanest fix in the pack. Reach for it after White Balance, before Film Grain.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| preset | COMBO | Universal — all skin tones | Skin tone range preset. Each covers different skin types. Select 'Custom' to set HSL ranges manually |
| amount | FLOAT | 600–100 | How much to even out skin color (0 = off, 100 = fully uniform) |
| smoothing_radiusopt | FLOAT | 6010–100 | Color averaging radius. Lower = local patches only, Higher = global average |
| hue_centeropt | FLOAT | 250–360 | (Custom only) Center hue for skin detection |
| hue_widthopt | FLOAT | 4510–90 | (Custom only) Hue range width |
| saturation_minopt | FLOAT | 0.080–0.5 | (Custom only) Minimum saturation |
| saturation_maxopt | FLOAT | 0.850.3–1 | (Custom only) Maximum saturation |
| luminance_minopt | FLOAT | 0.100–0.5 | (Custom only) Minimum luminance |
| luminance_maxopt | FLOAT | 0.920.5–1 | (Custom only) Maximum luminance |
| strengthopt | FLOAT | 1.000–1 | Blend between original (0) and corrected (1) |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask_preview | IMAGE | — |