Refactor node loading, add WASLUT

This commit is contained in:
Jordan Thompson
2025-09-27 23:42:56 -07:00
parent 1e8ad53855
commit cb1688cd1c
2 changed files with 40 additions and 16 deletions
+14
View File
@@ -43,3 +43,17 @@ A VAE Encode that can store the latent in the workflow for easy sharing, potenti
Workflow Example:
![ComfyUI_02554_](https://github.com/WASasquatch/WAS_Extras/assets/1151589/1a4f33e2-fb7a-49c7-90eb-727ecbef8ff7)
## WAS LUT
Load and combine LUTs, and visualize RGB waveform with RGB parade view.
### Provides
- WASLoadLUT
- Create a custom LUT, load a preset, or load *.cube files from your `ComfyUI/models/LUT` folder.
- WASCombineLUT
- Combine multiple LUTs into a single LUT.
- WASApplyLUT
- Apply a LUT to an image.
- WASChannelWaveform
- Visualize the RGB waveform with RGB parade view.
+26 -16
View File
@@ -47,14 +47,28 @@ class LUTLoader:
@staticmethod
def get_lut_dirs() -> list[Path]:
dirs: list[Path] = []
seen: set[Path] = set()
try:
for base in MODEL_ROOTS:
p = Path(base) / "LUT"
if p.exists() and p.is_dir():
dirs.append(p)
return dirs
for key, (paths, _exts) in folder_names_and_paths.items():
for p in paths:
pp = Path(p)
models_dir = pp.parent
if models_dir.name != "models":
for ancestor in pp.parents:
if ancestor.name == "models":
models_dir = ancestor
break
if models_dir.name != "models":
continue
lut_dir = models_dir / "LUT"
if lut_dir.exists() and lut_dir.is_dir() and lut_dir not in seen:
seen.add(lut_dir)
dirs.append(lut_dir)
except Exception:
raise Exception("Failed to load LUTs")
print("[WASLUT] Unable to load LUT directory.", flush=True)
pass
return dirs
@staticmethod
def discover_cube_files() -> list[Path]:
@@ -448,7 +462,6 @@ class WaveformScope:
def get_lut_choice_list() -> list[str]:
cubes = LUTLoader.discover_cube_files()
# Put Custom first so it becomes the default; exclude legacy "No Change"
names = ["Custom"]
names += [name for name, _ in LUTLoader.BUILTIN_PRESETS]
names += [f"LUT: {p.name}" for p in cubes]
@@ -543,13 +556,11 @@ class LUTBlender:
@staticmethod
def blend_cosine(a: np.ndarray, b: np.ndarray, t: float) -> np.ndarray:
# Cosine interpolation adjusts t to ease-in/ease-out
tt = (1.0 - np.cos(np.pi * float(t))) * 0.5
return (a * (1.0 - tt) + b * tt).astype(np.float32)
@staticmethod
def blend_smoothstep(a: np.ndarray, b: np.ndarray, t: float) -> np.ndarray:
# Smoothstep interpolation for t
tt = float(t)
tt = tt * tt * (3.0 - 2.0 * tt)
return (a * (1.0 - tt) + b * tt).astype(np.float32)
@@ -564,26 +575,25 @@ class LUTBlender:
eps = 1e-8
ta = a.astype(np.float32)
tb = b.astype(np.float32)
# Magnitudes
na = np.linalg.norm(ta, axis=-1, keepdims=True)
nb = np.linalg.norm(tb, axis=-1, keepdims=True)
ua = ta / np.clip(na, eps, None)
ub = tb / np.clip(nb, eps, None)
# Cosine of angle between directions
dot = np.clip(np.sum(ua * ub, axis=-1, keepdims=True), -1.0, 1.0)
omega = np.arccos(dot)
sin_omega = np.sin(omega)
tt = float(t)
# Avoid division by zero
mask_small = (sin_omega < 1e-4).astype(np.float32)
# Slerp direction
coeff_a = np.where(mask_small == 1.0, 1.0 - tt, np.sin((1.0 - tt) * omega) / np.clip(sin_omega, eps, None))
coeff_b = np.where(mask_small == 1.0, tt, np.sin(tt * omega) / np.clip(sin_omega, eps, None))
u = coeff_a * ua + coeff_b * ub
# Normalize direction after blend to be safe
nu = np.linalg.norm(u, axis=-1, keepdims=True)
u = u / np.clip(nu, eps, None)
# Lerp magnitudes
mag = (1.0 - tt) * na + tt * nb
out = u * mag
return np.clip(out, 0.0, 1.0).astype(np.float32)
@@ -615,7 +625,7 @@ class LUTBlender:
t = v * (1.0 - s * (1.0 - f))
i_mod = i % 6
rgb = np.zeros((*h.shape, 3), dtype=np.float32)
# Assign based on sector
mask = (i_mod == 0)
rgb[..., 0] = np.where(mask, v, rgb[..., 0])
rgb[..., 1] = np.where(mask, t, rgb[..., 1])