BD Center Median Luma
Make your skin shader's median sit dead-center — the V-curve fix
- image
- mask
- image
- measured_median
- shift_applied
Some shaders are picky about where your image's brightness sits. The V-curve skin shaders this pack's GLSL pipeline targets assume the input's median luma lands at the curve's midpoint - the u_float0 uniform - so the tint dominates evenly across shadows and highlights. Real generated images don't cooperate; a face that reads too dark overall pushes the median off-center and the whole tint lands wrong. BD Center Median Luma fixes the position: it computes the luma median of your input, then adds a single constant offset so the median lands exactly on your target_center. The node description is precise about what it does and doesn't do: it preserves the histogram shape (spread and contrast unchanged) and only shifts position. No contrast remap, no tone curve - a straight additive shift.
This is deliberately the other half of a two-node pair. BD Normalize Luma controls the spread (range); this node controls the position (median). The description says it plainly: normalize first to set the spread, then center to set the median position, then feed the result to the shader.
The inputs that matter
- image - the source, single or batch. Each frame is centered independently.
- mask - strongly recommended. The median should be measured from the subject, not the background. Without a mask, background pixels bias the measurement. Wire a skin or body mask here and the defaults handle the rest.
- target_center - where the median should land, default 0.5. Set it to your V-curve midpoint (
u_float0). - luma_standard - the RGB→luma weights:
bt709(default, modern sRGB/Rec.709, right for Unity/Unreal),bt601(legacy NTSC, matches Photoshop Desaturate), oraverage. Pick bt709 unless you have a specific reason not to. - statistic -
median(default) is robust to cheek highlights and deep shadows;meanis smoother but outlier-sensitive. Median is the right call for V-curve centering. - calc_from_mask - which pixels are used to measure. ON (default) measures mask pixels only. OFF measures the whole image, with
exclude_transparentas a fallback for images that already carry a body-shaped alpha. - apply_to_mask_only - where the shift is applied. ON (default) shifts only within the mask, leaving the background untouched. OFF shifts everything.
Those last two are independent, and the combos are worth a moment: you can measure from the mask but apply to the whole image, or measure the whole image and apply only in the mask. The defaults (measure and apply in-mask) are right for most cases.
Outputs: image (the centered result), measured_median (FLOAT, what the input's median actually was), and shift_applied (FLOAT, the offset added - negative if the input was too bright, positive if too dark). Both floats are great for wiring into diagnostics or feeding the shader's uniform directly.
Installing it
Part of BizaNator/ComfyUI-BrainDead (BrainDeadGuild). ComfyUI Manager → search "BrainDead" → install, or:
cd ComfyUI/custom_nodes
git clone https://github.com/BizaNator/ComfyUI-BrainDead
cd ComfyUI-BrainDead
pip install -r requirements.txt
Restart; it's under 🧠BrainDead/Segmentation.
Gotchas
The classic failure is skipping the mask. Without it, a dark or bright background drags the measured median off, and the "centered" result is still off-center - because the shift was calibrated on the wrong population. If your source already has a proper alpha, calc_from_mask=OFF + exclude_transparent=True is the honest fallback. Second: this node shifts position only. If your image is also too flat or too contrasty, center it and then normalize the spread - or the other way around; the order matters less than doing both. And one subtlety: if your shader expects a mean rather than a median, flip the statistic - the two disagree exactly when your image has outlier highlights, which skin often does.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Source image (single or batch). Each frame is centered independently. | |
| luma_standard | COMBO | bt709 | RGB → luminance weighting: bt709 (default): 0.2126 R + 0.7152 G + 0.0722 B — modern sRGB/Rec.709 standard. Use for Unity/Unreal, AI pipelines, modern display work. bt601: 0.299 R + 0.587 G + 0.114 B — legacy NTSC weights, matches Photoshop 'Desaturate' and older tools. average: (R+G+B)/3 — simple, not perceptual. |
| target_center | FLOAT | 0.500–1 | Desired median luma after centering. Set to your V-curve midpoint (u_float0). Default 0.5 keeps things symmetric. |
| statistic | COMBO | median | median = robust to outliers (cheek highlights, deep shadows). mean = sensitive to outliers but gives a smoother shift. Median is the right default for V-curve centering. |
| calc_from_mask | BOOLEAN | true | Controls which pixels are used to MEASURE the median. ON (default): median is computed from mask pixels only (where mask > 0.5). Recommended — background doesn't bias. OFF: median is computed from the whole image. Use with exclude_transparent to skip fully-transparent background pixels (e.g. when the source already has a body-shape alpha). Independent from apply_to_mask_only — you can measure from the mask but apply the shift to the whole image, or vice versa. Has no effect when no mask is wired. |
| apply_to_mask_only | BOOLEAN | true | Controls WHERE the computed shift is APPLIED. ON (default): only pixels WITHIN the mask are shifted; pixels outside pass through unchanged. OFF: the shift is applied to the whole image. Independent from calc_from_mask — common combinations: calc_from_mask=ON, apply=mask → measure+apply in mask only calc_from_mask=ON, apply=whole → measure mask, shift entire image calc_from_mask=OFF, apply=mask → measure whole image, apply in mask calc_from_mask=OFF, apply=whole → whole-image both (mask irrelevant) Has no effect when no mask is wired. |
| exclude_transparent | BOOLEAN | true | When calc_from_mask is OFF (measuring from whole image), exclude pixels where the source alpha < 0.5 from the median calculation. Prevents a fully-transparent background from biasing the median when the source already has a body/head alpha. Ignored when calc_from_mask is ON, or when the image has no alpha channel. |
| preserve_alpha | BOOLEAN | true | ON: alpha channel passes through unchanged. OFF: alpha is also shifted (rarely what you want). |
| maskopt | MASK | Optional region mask (e.g. skin mask). Used for calculation and/or application depending on calc_from_mask / apply_to_mask_only. STRONGLY RECOMMENDED — without a mask, background pixels bias the median (use exclude_transparent as a fallback). |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | Centered image (RGB shifted by the same constant). |
| measured_median | FLOAT | The actual masked-luma median found in the input. Useful for debugging or wiring to another node. |
| shift_applied | FLOAT | The constant added to RGB. = target_center - measured_median. Negative if input was too bright, positive if too dark. |