Better grading of the jumpsuit

This commit is contained in:
2026-07-16 13:03:06 +02:00
parent 38e6c7940c
commit 316441ccb8
3 changed files with 37 additions and 2 deletions
+9
View File
@@ -140,6 +140,7 @@ Keylight-style color-difference keyer) with a `color_grade` step. Key fields:
| `despill_bias` | RGB the keyed *edges* shift toward. A light neutral/skin tone reads better than cool blue. |
| `despill_strength` | How hard the edge/dominant-green despill pulls toward `despill_bias`. |
| `spill_suppress` | **Interior green-limiter — see below.** |
| `yellow_protect` | Shields saturated yellows/warm fabrics from `spill_suppress` (01). |
| `edge_erode` | Shrinks the alpha edge by N passes to kill green fringe (05). |
**`spill_suppress` — the bald-head knob.** The regular despill only acts where
@@ -158,6 +159,14 @@ Green is only ever *reduced*, never boosted, so clean pixels are untouched.
Start around `0.6`; go past `1.0` for heavy close-up spill. If skin tips
magenta/pink, ease back down. Range is `0.0``2.0`.
**`yellow_protect` — keep yellows yellow.** Because `spill_suppress` caps green
*everywhere*, it also drains green from things that are legitimately yellow (a
yellow jumpsuit, warm props), turning them orange. Blue is the tell: skin keeps
some blue, saturated yellow fabric reflects almost none. `yellow_protect`
(0.01.0) gates the green-limiter back down where `min(r,g) b` is high
(yellow) while leaving skin/scalp spill fully suppressed. Bump it toward `1.0`
if warm colours go orange; leave at `0` if you have no strong yellows.
#### Grade preview (`grade`)
Iterate on keying/grading without running a full preprocess. It seeks a few
+8
View File
@@ -140,6 +140,14 @@ class GnommoKeyConfig:
# close-up spill (2.0 = green can never exceed the smallest channel).
spill_suppress: float = 0.0
# Protect saturated yellows/warm fabrics from spill_suppress (0.0-1.0, 0 = off).
# spill_suppress caps green everywhere, which turns legit yellow (high r+g,
# low b) into orange. Blue is the tell: skin keeps some blue, yellow fabric
# reflects almost none. This gates the green-limiter down where
# min(r,g)-b is high (yellow) while leaving skin/scalp spill fully suppressed.
# 1.0 = full protection for strong yellows. Only matters when spill_suppress>0.
yellow_protect: float = 0.0
# Alpha bias: influences edge treatment (RGB)
# Can help with edge color contamination
alpha_bias: tuple[int, int, int] = None
+20 -2
View File
@@ -1215,13 +1215,30 @@ def build_gnommokey_filter(config: dict) -> str:
# boosted, so non-spilled pixels are untouched.
if cfg.spill_suppress > 0:
t = min(max(cfg.spill_suppress, 0.0), 2.0)
yp = min(max(cfg.yellow_protect, 0.0), 1.0)
def _protected(spill_ch: str, capped: str, warm_ch: str, cold_ch: str) -> str:
"""Blend the capped spill channel back toward its original value for
saturated hues that legitimately pair the spill channel with a warm
channel while the cold channel is low (e.g. yellow = green+red, no
blue). Skin/scalp keeps some cold channel, so it stays suppressed."""
if yp <= 0:
return capped
# "yellowness": high when spill & warm are both high but cold is low.
hue = f"max(0,min({spill_ch},{warm_ch})-{cold_ch})"
gate = f"clip(({hue}-40)/120,0,1)"
protect = f"({gate}*{yp:.3f})"
return f"({spill_ch}*{protect}+({capped})*(1-{protect}))"
if is_green_screen:
ref = f"((1-{t:.3f})*max(r(X,Y),b(X,Y))+{t:.3f}*(r(X,Y)+b(X,Y))/2)"
new_g = f"min(g(X,Y),{ref})"
capped = f"min(g(X,Y),{ref})"
new_g = _protected("g(X,Y)", capped, "r(X,Y)", "b(X,Y)")
parts.append(f"geq=r='r(X,Y)':g='{new_g}':b='b(X,Y)':a='alpha(X,Y)'")
else:
ref = f"((1-{t:.3f})*max(r(X,Y),g(X,Y))+{t:.3f}*(r(X,Y)+g(X,Y))/2)"
new_b = f"min(b(X,Y),{ref})"
capped = f"min(b(X,Y),{ref})"
new_b = _protected("b(X,Y)", capped, "r(X,Y)", "g(X,Y)")
parts.append(f"geq=r='r(X,Y)':g='g(X,Y)':b='{new_b}':a='alpha(X,Y)'")
# Edge-aware despill: aggressively suppress green at semi-transparent edges
@@ -1311,6 +1328,7 @@ def parse_gnommokey_config(config: dict) -> GnommoKeyConfig:
despill_bias=despill_bias,
despill_strength=float(config.get("despill_strength", 0.5)),
spill_suppress=float(config.get("spill_suppress", 0.0)),
yellow_protect=float(config.get("yellow_protect", 0.0)),
alpha_bias=alpha_bias,
protect_luma=int(config.get("protect_luma", -1)),
shadow_boost=float(config.get("shadow_boost", 0.0)),