Frequency Merge
The Face of One Upscale, the Skin Texture of Another
- base
- detail
- image
Here's the SeedVR2 problem in one sentence: at downscale_factor 1 it keeps a face exactly right but the skin comes out plastic, and at a lower factor you get pores and fine texture but a face that's been subtly rewritten. Frequency Merge is the node that lets you stop choosing. Take the structure from one and the detail from the other.
The mechanism
It's frequency separation, and the maths is short enough to hold in your head:
image = G(base) + detail_strength × (detail − G(detail))
G is a Gaussian blur with sigma split_sigma in pixels, reflect-padded at the borders. So G(base) is base's low frequencies - shapes, shading, colour - and detail − G(detail) is detail's high frequencies: texture, fine grain, edge detail. Add them, clamp to [0, 1], done.
Gaussian specifically, because it doesn't ring. A hard frequency cut-off puts halos around every edge; a Gaussian divides smoothly, the same in every direction, and it's exact - which is also why feeding the same image into both inputs gives you that image back, up to float rounding.
Inputs and outputs
base- supplies the low frequencies. In the SeedVR2 workflow the author describes, this is thedownscale_factor1 output: its face is right.detail- supplies the high frequencies, at the same size and image count asbase. The lower-factor output, full of skin texture.split_sigma- default 3, range 0.5 to 64. The tooltip has the useful number in it: a pattern repeating every5.3 × split_sigmapixels is split half and half. At 3, that's about a 16 px period. Finer than that comes fromdetail; coarser frombase. Raise it to take more ofdetail; keep5.3 × split_sigmawell below the size of anything that must not change - eyes, mouth, the line of the jaw.detail_strength- default 1, range 0 to 2. Gain on the high frequencies. At 0 you get the blurredbaseand nothing else; above 1 you get something sharper thandetailitself.device-cpu(default) orgpu.- Output:
image, float16 only when both inputs are float16 (which is what SeedVR2 PostProcess hands you), float32 otherwise. Alpha is dropped.
The workflow it's built for
Run SeedVR2 twice from the same source, at two downscale factors, then merge. That's a two-upscale trick rather than a two-upscale cost - you do pay for both passes, so this is for hero images, not batches. The other honest caveat: it only works when the two images line up pixel for pixel. Two different upscalers, or one resize between them, and you get double edges, because you're adding one image's high frequencies onto another image's slightly shifted structure. Same size is enforced by the node; same content is on you.
Outside upscaling, the same node does texture transfer: the detail of a grain plate or a photoreal reference onto a clean render. Same rule applies - align them first.
Install
Manager, search ComfyUI-BCNodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/0xBeycan/ComfyUI-BCNodes
pip install -r ComfyUI-BCNodes/requirements.txt
Restart and search BCNodes (it's under BCNodes/image). The requirements are opencv-python, postfx and caption-audit - none of which this node uses. Delete any stray ComfyUI-AutoBypass folder from the pack's previous name.
Gotchas
Mismatched inputs stop the run with a message saying so - no silent truncation of the shorter batch. A split_sigma whose blur (3 × sigma) doesn't fit inside the image is refused with the largest value that does; that's the node protecting you from a blur that's meaningfully bigger than the picture.
Performance is fine on CPU for stills - the author's measurement is 0.3 s for a 3840×2160 image at sigma 3 on an M-series CPU, 2.5 s at sigma 32, with about half a gig of working memory beyond the inputs. Do it frame by frame on video and you'll feel it; flip device to gpu for that.
And the standard warning about any detail-transfer trick: overshoot is visible as crunch. detail_strength above 1 on already-sharp input looks like a bad sharpening pass, because that's exactly what it is.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| base | IMAGE | Gives the low frequencies: shapes, shading, colour (e.g. the SeedVR2 output made at downscale_factor 1, whose face is right). | |
| detail | IMAGE | Gives the high frequencies: skin texture, fine detail (e.g. the SeedVR2 output made at a lower downscale_factor). Same size and image count as base. | |
| split_sigma | FLOAT | 3.00.5–64 | Sigma in pixels of the Gaussian that splits low from high: a pattern that repeats every 5.3 x sigma px is split half and half, finer comes from detail, coarser from base. Raise it to take more of detail; keep 5.3 x sigma well below the size of what must not change (eyes, mouth). |
| detail_strength | FLOAT | 1.000–2 | Gain on detail's high frequencies. 1 = as in detail; 0 = none (the blurred base); above 1 sharper than detail. |
| deviceopt | COMBO | cpu | Device the images are filtered on. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | base's low frequencies + detail's high frequencies, clamped to [0, 1], RGB; float16 only when both inputs are float16. |