Skin Tone Protect
Skin Tone Protect keeps faces from going green or clown-red
- image
- reference_image
- mask
- image
- skin_mask
- skin_protect_info
Aggressive color grading is how you get cinematic looks - and also how you get portraits where the skin looks like it's been sitting under a blacklight. x1SkinToneProtect is the safety net you run after the grade: it finds the skin pixels and pulls them back toward healthy skin color, so your teal-and-orange look keeps the teal in the shadows and the orange on the walls, but not the jaundice on the face.
How it works
It builds a skin matte from the same HSV qualifier family as the pack's x1SkinToneCheck - hue window around 28°, bounded saturation and value - then re-colors everything inside that matte instead of showing it. The re-coloring depends on which mode you're in:
auto(default) - caps saturation and gently clamps hue drift, so skin that's been pushed into the blue/green or blown out gets pulled back toward the target window without flattening it.naturalize- the same corrective pass, applied on the assumption you have no reference.reference_restore- the fun one: feed areference_image(a well-exposed photo of the same person, or even a different shot you like) and it swaps skin pixels toward that reference's color. This is what you reach for when a generation or a LUT has walked the face's tone off a cliff.
protect_strength (0.70) sets how hard the correction applies; saturation_limit (0.75) is the ceiling so the node never adds the oversaturation that got you in trouble. warmth_balance shifts skin warm or cool within the window, and mix is the wet/dry blend. The skin_mask output gives you the matte, so you can see exactly what the node decided was skin.
The inputs that matter
Settings live in a settings_json string, prefilled when the node drops:
mode-auto,naturalize, orreference_restore. If you have a reference, usereference_restore; it's the most powerful and the least guessy.protect_strength(0.70) - how much correction. Start here, not with the hue knobs.hue_center(28) /hue_width(40) - the skin window. Match these to x1SkinToneCheck if you ran it first.sat_min/sat_max,val_min/val_max- the matte fences.reference_image(optional input, not JSON) - the photo to restore skin toward.
Outputs are image, skin_mask, and skin_protect_info (a text log of mode, ranges, and coverage).
Installing it
It's part of the MKRShift Nodes pack, installed as one unit. ComfyUI Manager: search "MKRShift", install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes
Restart after. No models or downloads - this is numpy HSV math, and it runs on CPU without blinking.
Gotchas
Two things to get right. First, if you wire a reference image, its batch must match your image batch (or be a single image it can repeat across the batch) - otherwise the node throws a batch-mismatch error, which is the one hard failure mode here. Second, order matters: this is a post grade node. Put it after your color corrections and LUTs, not before, or you'll protect skin that hasn't been graded yet and the final pass will re-break it. Run x1SkinToneCheck afterward to confirm the protection held - check, protect, check again is the loop that actually works.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| settings_json | STRING | {"mode":"auto","protect_strength":0.7,"hue_center":28.0,"hue_width":40.0,"sat_min":0.1,"sat_max":0.8,"val_min":0.08,"val_max":1.0,"softness":16.0,"saturation_limit":0.75,"warmth_balance":0.0,"mix":1.0,"mask_feather":12.0,"invert_mask":false} | — |
| reference_imageopt | IMAGE | — | |
| maskopt | MASK | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| skin_mask | MASK | — |
| skin_protect_info | STRING | — |