Fix color temperature having no visible effect

The warm_dir was a unit vector (norm=1) but LCS chroma vectors have
norms ~40-46, so color_temperature=0.5 only shifted chroma by 1.2%.
Scale warm_dir by mean anchor chroma magnitude so the shift is
proportional. Slider range narrowed from ±2.0 to ±1.0 and preset
values adjusted accordingly (Warm=0.15, Cool=-0.15, Cinematic=0.05).
This commit is contained in:
facok
2026-03-17 05:05:21 +08:00
parent 78be03db46
commit 7aa68375f9
2 changed files with 14 additions and 7 deletions
+11 -4
View File
@@ -302,6 +302,8 @@ def _build_tone_fn(lcs_data, contrast, brightness, saturation, color_temperature
onto achromatic axis) and chroma (perpendicular residual). No HSL round-trip.
"""
# Precompute warm/cool direction vector (depends only on immutable calibration data)
# Scaled so that color_temperature=1.0 shifts by the mean anchor chroma magnitude,
# giving a visually significant warm↔cool shift.
_warm_dir = None
if abs(color_temperature) > _EPS:
anc = lcs_data.anchor_lcs # [8, 3]
@@ -314,10 +316,15 @@ def _build_tone_fn(lcs_data, contrast, brightness, saturation, color_temperature
l_a = ((pt - black_anc) * a_pre).sum() / a_sq_pre
return pt - (black_anc + l_a * a_pre)
warm_center = (_anchor_chroma_pre(0) + _anchor_chroma_pre(5)) / 2 # Red + Yellow
cool_center = (_anchor_chroma_pre(1) + _anchor_chroma_pre(4)) / 2 # Blue + Cyan
chromas = [_anchor_chroma_pre(i) for i in range(6)]
warm_center = (chromas[0] + chromas[5]) / 2 # Red + Yellow
cool_center = (chromas[1] + chromas[4]) / 2 # Blue + Cyan
wd = warm_center - cool_center
_warm_dir = wd / wd.norm() # unit vector
wd_unit = wd / wd.norm() # unit vector
# Scale: color_temperature=1.0 → shift by mean chroma norm
mean_chroma_norm = sum(c.norm() for c in chromas) / 6.0
_warm_dir = wd_unit * mean_chroma_norm
def post_cfg_fn(args):
"""Post-CFG hook: project to LCS, adjust contrast/brightness/saturation, reconstruct."""
@@ -478,7 +485,7 @@ class LCSToneAdjust(io.ComfyNode):
tooltip="Lightness shift (>0 = brighter, <0 = darker)"),
io.Float.Input("saturation", default=1.0, min=0.0, max=3.0, step=0.05,
tooltip="Saturation multiplier (>1 = more vivid, <1 = more muted, 0 = grayscale)"),
io.Float.Input("color_temperature", default=0.0, min=-2.0, max=2.0, step=0.05,
io.Float.Input("color_temperature", default=0.0, min=-1.0, max=1.0, step=0.05,
tooltip="Color temperature shift (>0 = warmer/amber, <0 = cooler/blue)"),
io.Int.Input("start_step", default=5, min=0, max=50,
tooltip="First step to apply adjustment"),
+3 -3
View File
@@ -3,14 +3,14 @@ import { app } from "../../../scripts/app.js";
// Preset values — the single source of truth for preset parameters.
// TONE_PRESET_NAMES in nodes/intervene.py must list matching keys.
const TONE_PRESETS = {
"Cinematic": { contrast: 1.20, brightness: -0.05, saturation: 0.90, color_temperature: 0.15 },
"Cinematic": { contrast: 1.20, brightness: -0.05, saturation: 0.90, color_temperature: 0.05 },
"HDR": { contrast: 1.40, brightness: 0.0, saturation: 1.20, color_temperature: 0.0 },
"Vivid": { contrast: 1.10, brightness: 0.0, saturation: 1.50, color_temperature: 0.0 },
"Dramatic": { contrast: 1.50, brightness: -0.10, saturation: 0.85, color_temperature: 0.0 },
"Low Key": { contrast: 1.30, brightness: -0.20, saturation: 0.80, color_temperature: 0.0 },
"High Key": { contrast: 0.80, brightness: 0.20, saturation: 0.90, color_temperature: 0.0 },
"Warm": { contrast: 1.0, brightness: 0.0, saturation: 1.0, color_temperature: 0.50 },
"Cool": { contrast: 1.0, brightness: 0.0, saturation: 1.0, color_temperature: -0.50 },
"Warm": { contrast: 1.0, brightness: 0.0, saturation: 1.0, color_temperature: 0.15 },
"Cool": { contrast: 1.0, brightness: 0.0, saturation: 1.0, color_temperature: -0.15 },
"Desaturated": { contrast: 1.0, brightness: 0.0, saturation: 0.40, color_temperature: 0.0 },
};