Nodes/MKRShift_Nodes/Skin Tone Protect
ComfyUI Node

Skin Tone Protect

Skin Tone Protect keeps faces from going green or clown-red

By criskb·Created 7 months ago·Updated 5 months ago· 0
Skin Tone Protect
  • image
  • reference_image
  • mask
  • image
  • skin_mask
  • skin_protect_info
settings_json{"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}

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 a reference_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, or reference_restore. If you have a reference, use reference_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.

CategoryMKRShift Nodes/Color/Finish

Inputs (4)

NameTypeDefaultDescription
imageIMAGE
settings_jsonSTRING{"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_imageoptIMAGE
maskoptMASK

Outputs (3)

NameTypeDescription
imageIMAGE
skin_maskMASK
skin_protect_infoSTRING