17 Commits
Author SHA1 Message Date
facok 20455e339e Fix sharpness calibration vector count 2026-06-28 01:21:38 +08:00
facok e7a836db4d Fix color/brightness side effects in sharpness intervention
Two bugs caused sharpness edits to shift color even when calibrated
with lcs_data:

1. Calibration order was wrong: DC removal before LCS color removal
   operated in the wrong space (LCS basis has large DC components),
   producing incorrect color projection and incomplete removal.
   Fix: remove color first (in raw space), then DC.

2. Intervention DC removal broke orthogonality: subtracting
   mean(pc1_dir) introduced a color component because the all-ones
   vector projects onto LCS basis. Fix: re-orthogonalize against
   LCS basis after DC removal.

Also: store lcs_basis in SharpnessData/safetensors so intervention
can re-orthogonalize; bump cache name to _grating to force
re-calibration with the corrected pipeline.
2026-03-22 20:57:08 +08:00
facok 818ec85429 Replace Gaussian blur calibration with sinusoidal grating stimuli
Grating stimuli achieve higher linearity (R²=0.94 vs 0.88) because each
contains a single spatial frequency — a purer probe of the sharpness axis.
Both methods discover the same 1D subspace (|cos|=0.986, 9.7° apart).

- Replace _gaussian_kernel_1d, _apply_gaussian_blur with _generate_grating_batch
- Generate stimuli lazily per batch to avoid 1.3GB all-at-once allocation
- Accept deprecated blur_levels parameter with warning
- Update cache filename to sharpness_{fp}_freq{suffix}.safetensors
- Update node descriptions to reflect new calibration method
2026-03-22 20:17:27 +08:00
facok 0d82606fb0 Support 5D video VAE latents (Wan) in patchify/unpatchify
Video VAEs output [B, C, T, H, W]. Patchify now merges T into batch,
processes all frames, and unpatchify restores the 5D shape.
Updated all callers to pass extra_shape through.
2026-03-21 16:19:03 +08:00
facok a336d4ca95 Fall back to per-image VAE encoding for video VAEs (Wan)
Video VAEs like Wan don't support batch encoding — input B images
produces only 1 latent. Detect this by comparing output batch size
to input, and fall back to encoding images one by one.
Fixes both color calibration and sharpness calibration.
2026-03-21 16:10:13 +08:00
facok 53b2c4cadb Fix blur_labels count to match actual VAE output size 2026-03-21 15:41:14 +08:00
facok c9e7d608a8 Simplify sharpness: precompute edit vector, remove dead pc1_std, reduce memory
- Precompute edit_vec = (strength * sign) * dc_removed_pc1_dir once in
  closure instead of recomputing pc1_dir mean removal every denoising step
- Remove SharpnessData.pc1_std (stored but never used since dropping
  pc1_std multiplier)
- Blur single-channel gray noise, expand to 3-channel per-batch instead
  of allocating full 201 MB 3-channel copy upfront
- Remove unused LCSData import
2026-03-20 17:25:49 +08:00
facok 1d024be9da Revert to 512px default, remove diagnostic logging
256 vs 512 difference (97.5% vs 97.3%) is within random variation —
different image_size consumes different random numbers from the same
seed, producing different images rather than a controlled comparison.
Keep 512 consistent with LCS color calibration. Remove per-blur-level
mean logging that was used during development.
2026-03-20 17:06:17 +08:00
facok ce4007b916 Default to 256px calibration images, add image_size parameter
256px gives the best PC1 (97.5%) and fastest calibration.
Larger images average more patches, reducing per-sample variance
but not improving PCA purity. Added image_size input to
LCSSharpnessCalibrate for experimentation.
2026-03-20 17:02:21 +08:00
facok 71233801a2 Use uniform blur levels (0,0.5,1,2,4,8) for sharpness calibration
σ=16 nearly destroys all spatial content (≈solid color), adding little
information beyond σ=8 while distorting PCA with an extreme outlier.
Adding σ=0.5 better captures subtle sharpness differences.

Result: PC1 96.7% → 97.3%, correlation 0.748 → 0.893.
2026-03-20 16:51:57 +08:00
facok f1661ed4bf Use grayscale noise for sharpness calibration
RGB noise has independent per-channel variation that blur affects
differently, introducing inter-channel variance captured as PC2.
Grayscale noise (same value across RGB) ensures blur affects all
channels identically, raising PC1 from 95.8% to 96.7%.

Also added per-vector zero-mean to remove VAE brightness drift
and diagnostic logging for per-blur-level latent means.
2026-03-20 16:37:34 +08:00
facok 6fe4add02c Add lcs_data to LCSSharpnessIntervene for color-preserved sharpness
When calibration used LCS color removal, the intervention must also
remove the color component before projecting onto the sharpness basis,
then add it back after reconstruction. This ensures sharpness edits
don't affect color.

Workflow:
1. LCSSharpnessCalibrate(vae, lcs_data) → sharpness_data
2. LCSSharpnessIntervene(model, sharpness_data, strength, lcs_data=lcs_data)

Both nodes now have optional lcs_data input. When connected, color is
preserved during sharpness intervention.
2026-03-20 14:16:22 +08:00
facok cc0291352e Add lcs_data input to remove color component during calibration
When LCS color data is connected, the sharpness PC1 will be orthogonal
to the color subspace, preventing color shifts during intervention.

Changes:
- calibrate_sharpness() now accepts optional lcs_data parameter
- LCSSharpnessCalibrate node has optional lcs_data input
- Cache files use "_lcs" suffix when LCS is used (different subspace)
2026-03-20 14:00:57 +08:00
facok c6b746ff32 Fix conv2d groups parameter and add .contiguous() for expanded kernels 2026-03-20 13:37:50 +08:00
facok 531fb5fd1a Fix sharpness calibration: use same base images for all blur levels
- Generate base images once upfront, then apply all blur levels to the
  SAME images (was generating different random images per blur level)
- Add separable convolution for kernel_size > 15 (O(2k) vs O(k²))
- Add kernel caching to avoid recomputation
- Add per-blur-level progress logging

This matches SubspaceLab's methodology and ensures PCA receives
properly paired stimuli (same image × multiple blur levels).
2026-03-20 13:34:45 +08:00
facok 03fb376b8c Fix sharpness calibration and simplify intervention hook
- Use random noise images instead of solid-color (blur had no effect
  on spatially uniform images, making calibration a no-op)
- Generate images per-batch to avoid 200 MB upfront allocation
- Simplify hook algebraically: patches + delta * pc1_dir eliminates
  projection/reconstruction/residual intermediates (~24 MB per step)
- Move SCALE_FACTOR, SHIFT_FACTOR, find_step_index to core/sampling.py
- Rename sd → shd to avoid confusion with state_dict convention
- Remove redundant [:,:,:,:3] slice and unused imports
2026-03-20 13:12:46 +08:00
facok cf9edf0239 Add sharpness intervention via PCA subspace in FLUX VAE patch space
New calibration (core/sharpness.py) generates blur stimuli, VAE-encodes,
and extracts PC1 as the sharpness direction. New nodes (nodes/sharpen.py)
provide LCSSharpnessCalibrate (auto-cached per-VAE) and
LCSSharpnessIntervene (post-CFG hook with strength, step window, mask).
Positive strength = sharper, negative = blurrier.
2026-03-20 13:01:23 +08:00