Nodes/MKRShift_Nodes/Highlight Recovery
ComfyUI Node

Highlight Recovery

Pull detail back from blown whites

By criskb·Created 7 months ago·Updated 5 months ago· 0
Highlight Recovery
  • image
  • mask
  • image
  • mask
  • highlight_recovery_info
settings_json{"threshold":0.72,"softness":0.1,"recovery":0.72,"chroma_preserve":0.6,"desaturate_clips":0.18,"mix":1.0,"mask_feather":12.0,"invert_mask":false}

You know the problem: a face under a window, a bright sky, a white shirt - and the model burned it to pure clipping. No amount of curve-pulling fixes a highlight that has no data left in it, because there's literally nothing under the white. Highlight Recovery is the MKRShift node that rebuilds the appearance of detail in those clipped areas instead of just darkening them. It's the digital-imaging trick film cameras have always done by nature - roll the highlights off instead of hard-clipping - applied retroactively to a finished frame.

It's in MKRShift Nodes/Color/Finish, the pack's finishing lane, and it sits next to its sibling x1HighlightRollOff. The two are easy to confuse: Roll-Off is a tonal shoulder (it reshapes the top of the curve so highlights never hard-clip), while Recovery is a rebuild - it actively re-synthesizes plausible values inside the clipped region. If you have one blown patch you need to save, this is the one.

How it works

The node builds a soft mask of the highlight region first: pixels whose luma sit above threshold get caught by a softness-blurred smoothstep, so the effect fades in rather than slicing at a hard line. Inside that region it measures how far each pixel overshoots, then compresses that overshoot with a soft-knee curve controlled by recovery. The result is a compressed luminance that keeps highlight gradients instead of a flat white ceiling.

The clever part is chroma_preserve. A clipped pixel's color is unreliable (it's been clamped toward white), so the node rebuilds color in two ways and blends between them: a scalar rebuild that just scales the original color by the new luminance, and a normalized-color rebuild that re-projects the ratio of the RGB channels. chroma_preserve at 1.0 keeps hue relationships more natural; lower values trust the original (blown) color more. desaturate_clips lets you deliberately drain the clipped region toward neutral - the honest look for genuinely overexposed highlights - instead of letting it go an artificial pink.

The knobs that matter

The defaults in the settings bundle are decent starting points: threshold 0.72, softness 0.1, recovery 0.72, chroma_preserve 0.6, desaturate_clips 0.18, mix 1.0. The two you'll actually touch:

  • threshold - where "highlight" begins. Too low and you're recovering midtones into mush.
  • recovery - how much the overshoot compresses. 0 is a no-op; 1 is aggressive.

It takes an optional mask (so you can save only the blown sky, not the blown shirt), outputs the finished image, a mask holding the highlight region it operated on, and a highlight_recovery_info string with the settings it used. Wire the info string to a Show Text node if something looks off.

Installing and gotchas

cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes

then restart, or search MKRShift_Nodes in ComfyUI Manager. No pip extras, no models - this is numpy math on the CPU of your GPU.

Honest caveat: this can't invent data that was never captured. Recovery rebuilds plausible highlight gradients, which reads as detail, but it won't resurrect a face that's been clipped to 100% white across the whole cheek - that's what your mask is for, and what a re-roll of just that region handles better. Where it shines is the in-between: highlights at 0.85–0.99 luma that just need the shoulder rebuilt before you grade the rest of the frame.

CategoryMKRShift Nodes/Color/Finish

Inputs (3)

NameTypeDefaultDescription
imageIMAGE
settings_jsonSTRING{"threshold":0.72,"softness":0.1,"recovery":0.72,"chroma_preserve":0.6,"desaturate_clips":0.18,"mix":1.0,"mask_feather":12.0,"invert_mask":false}
maskoptMASK

Outputs (3)

NameTypeDescription
imageIMAGE
maskMASK
highlight_recovery_infoSTRING