Upscale Face Restore
The pass you run before you look at the eyes
- images
- original_images
- restored
- face_mask
- pass_info
Every upscaler that adds detail also rewrites faces, and it rewrites them worst of all - soft skin, dead eyes, a face that is almost the right person. The standard community advice is "give the face its own pass," and Upscale Face Restore is that pass: detection, restoration with CodeFormer or GFPGAN, a feathered paste back into the frame, and a colour match so the patch doesn't sit there glowing.
How it works
Detection first. The source uses facexlib's RetinaFace ResNet50 when it's available, and falls back to an OpenCV Haar cascade when it isn't - the model weights are fetched on first use from the original release files, SHA-256 verified before install. Each detected box gets padded by face_pad_frac (default 0.25, i.e. 25% of the face's width and height) so the crop has context and the seam doesn't land on a jawline.
Restoration next, through one of the EDSR-era face models ComfyUI people already know: CodeFormer with a fidelity dial, or GFPGAN v1.4. face_model defaults to auto (CodeFormer → GFPGAN → skip), which tries CodeFormer, falls back to GFPGAN, and - if neither is available - does detection only and reports it rather than failing. That last behaviour means "skipped" looks like "nothing happened," so read pass_info.
Then the paste: the restored crop is blended back through a Gaussian feather of blend_radius pixels (default 20). Set it to 0 and you get a hard rectangle, which is exactly as visible as it sounds.
And finally the step that makes the difference between "restored" and "obviously restored": colour_correct (on by default) histogram-matches the restored face to a reference - your original_images if you connect them, otherwise the restored image itself - at colour_strength 0.8. This is what stops a diffusion-era face model's slight colour drift from turning the face into a warmer, flatter, pasted-on patch.
The dials you'll actually use
fidelity_weight (default 0.75) is CodeFormer's fidelity control: 0 is maximum enhancement and creative liberty, 1 is faithful to input. The tooltip recommends 0.5–0.8 "for most upscaled content," and that matches what the upscaling community has always said about face passes - you want the model to clean up, not to cast a new actor.
min_face_px (default 64) is your performance control and a quality control at once: smaller faces get skipped, which is usually correct, because a 30-pixel face has no information to restore and a model will happily invent one.
face_model values are auto (...), codeformer, gfpgan_v1.4 and skip (detection only) - skip is the debugging mode, letting you confirm detection before you blame restoration.
Outputs: restored, face_mask (an IMAGE, handy for QC or for masking a later pass to the faces only) and pass_info.
Install
Manager → search Radiance → Install → restart → refresh the browser. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt
Windows portable: use python_embeded\python.exe. CodeFormer, GFPGAN and RetinaFace weights download on their own on first use - SHA-pinned - into ComfyUI's models folders. RADIANCE_ALLOW_DOWNLOADS=0 or RADIANCE_UPSCALE_OFFLINE=1 turns that off, and on an offline machine you'll silently get the detection-only path.
One dependency note worth knowing before you judge the result: facexlib is not in the pack's requirements. If it isn't installed in ComfyUI's Python environment, detection falls back to the OpenCV Haar cascade, which is markedly worse at angles and profiles. A pip install facexlib in the right environment gets you RetinaFace; a wrong looking face mask is often just this.
Honest expectation setting
Face restoration models predate the current generation of generators and they have a house style. They will smooth texture, they will make a face look like a slightly better-lit version of itself, and on a real photograph of a real person they can nudge recognisability. The community rule of thumb exists for a reason: recognisable faces get their own pass, watched, at a modest fidelity weight - and if it's someone's actual face, check it against the original before you ship it.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | Upscaled image batch (B,H,W,C) float32. | |
| face_model | COMBO | auto (CodeFormer → GFPGAN → skip) | Face restoration model. Auto tries CodeFormer first, falls back to GFPGAN, skips if neither is available. |
| fidelity_weight | FLOAT | 0.750–1 | CodeFormer fidelity: 0 = maximum enhancement (creative), 1 = faithful to input (precise). 0.5–0.8 is recommended for most upscaled content. |
| blend_radius | INT | 200–80 | Gaussian feather radius in pixels at face crop edge. Higher = softer transition. 0 = hard paste. |
| face_pad_frac | FLOAT | 0.250–0.6 | Extra padding around each detected face bbox (fraction of face width/height). 0.25 = 25%. |
| min_face_px | INT | 6416–256 | Smallest face (in pixels) to process. Smaller faces are skipped. |
| colour_correct | BOOLEAN | true | Apply histogram-match colour correction after restoration to cancel diffusion colour drift. |
| colour_strength | FLOAT | 0.800–1 | Strength of histogram-match correction. 1.0 = full match to input colours. |
| original_imagesopt | IMAGE | Original (pre-upscale) images for colour reference. Used by histogram-match correction. Leave disconnected to use the restored images as self-reference. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| restored | IMAGE | — |
| face_mask | IMAGE | — |
| pass_info | STRING | — |