SD WebUI extension that applies multi-scale pyramid guidance during denoising. Two pyramid modes are supported:
- Gaussian Blur (default): Each pyramid level applies progressively stronger Gaussian blur (σ_k = max_σ × k/(K-1))
- Down-Up Sampling: Each pyramid level downsamples to factor^k of original size (area averaging) then upsamples back (bicubic interpolation)
At each sampling step, the denoised latent is VAE-decoded to pixel space, degraded at multiple scales, VAE-encoded back, and each scale gets modified CLIP text conditioning and per-level CFG. The UNet runs once per pyramid level per step; outputs are weighted-averaged.
Per Denoising Step:
Denoised z ──┬── [Level 0: original] ── Standard UNet output ─────┐
│ │
├── VAE decode ──┬── Degrade(1) ── VAE encode ── UNet ┤
│ ├── Degrade(2) ── VAE encode ── UNet ┤
│ └── Degrade(K) ── VAE encode ── UNet ┤
│ │
└──────────── Weighted Average ────────────────── Output
Degrade = Blur(σ_k) [Gaussian Blur mode]
| Downsample(f^k) → Upsample [Down-Up Sampling mode]
Hooks: on_cfg_denoiser (store refs) → cfg_after_cfg (run pyramid levels, weighted average into params.x)
pyramid_scale_guidance/— Core library (types, blur, downsample, VAE, conditioning, denoiser, scheduling, tagger, frequency bands)scripts/pyramid_scale_guidance.py— WebUI script (UI + hook registration)tests/— Unit tests (pytest), golden test runner (real SD model), parameter sweepdocs/— UI screenshotscripts/generate_readme_tables.py— Regenerates the image comparison section of README.md (rows=seeds, columns=configs, grouped by technique). Auto-run byserve_readme.sh.
./scripts/lint.sh # auto-fix safe issues, then check
./scripts/lint.sh --check # check only (no fixes)Configured via ruff.toml (line-length=120, E402 ignored for files with sys.path manipulation).
Convention: Always use zip(..., strict=True) to catch length mismatches early. Ruff enforces this via B905.
# Unit tests (no GPU needed, ~60s)
# golden_test_runner.py and parameter_sweep.py are auto-excluded via conftest.py collect_ignore
./scripts/run_unit_tests.sh
# or equivalently:
python -m pytest tests/ -v
# Golden tests with real SD model (offline by default, run directly as scripts)
python tests/golden_test_runner.py --sd-steps 10
python tests/golden_test_runner.py --level 9 10 11 # specific levels
python tests/golden_test_runner.py --all-prompts # all 12 prompts
python tests/golden_test_runner.py --online # allow HF Hub access
# Multi-seed golden tests (Wilcoxon signed-rank test for statistical significance)
python tests/golden_test_runner.py --seeds 42,123,7,2024,555,8888,31415
python tests/golden_test_runner.py --n-seeds 7 # use first 7 from DEFAULT_SEEDS
python tests/golden_test_runner.py --n-seeds 7 --all-prompts # full matrix
# Mock tests (math-only, no SD model)
python tests/golden_test_runner.py --mock
# Parameter sweep (offline by default, run directly as script)
python tests/parameter_sweep.py --seeds 42,123,7
python tests/parameter_sweep.py --mode downsample # downsample factor sweep
python tests/parameter_sweep.py --mode both # blur + downsample
python tests/parameter_sweep.py --online # allow HF Hub accessAll tests and the main extension default to offline mode — no HF Hub network requests. Models must be pre-cached locally.
- Tests: Pass
--onlineflag togolden_test_runner.pyorparameter_sweep.pyto allow downloads - Extension UI: "Allow tagger model download (HF Hub)" checkbox (unchecked by default) controls Florence-2 model download
- Env vars:
HF_HUB_OFFLINE=1,TRANSFORMERS_OFFLINE=1,HF_DATASETS_OFFLINE=1are set by default in test scripts
Based on 81-config × 3-seed sweep (Gaussian blur mode): K=2, σ=2.0, threshold=0.5, decode_every=3, enable_frequency_bands=True
Lower sigma, fewer pyramid levels, higher activation threshold, and less frequent VAE decoding consistently perform best. Down-up sampling mode defaults: K=2, factor=0.5, threshold=0.5, decode_every=3 (not yet sweep-validated).
Prompt-type suitability (from 28-level × 12-prompt × 8-seed analysis): Pyramid guidance benefits atmospheric/mood scenes (89% positive), portraits (64%), and structured text (64%), but generally degrades CLIP alignment for detailed scenes, abstract patterns, macro photography, and minimalist compositions (~70% of configs show negative delta overall).
Florence-2 tags are split into content (→ positive prompt) and quality/blur (→ negative prompt for ALL levels). Three-stage pipeline:
- Filter redundant tags (word stems already in prompt)
- Hardcoded blur/quality tag list (word-boundary matching)
- CLIP embedding distance fallback for unknown tags
Florence-2 runs in a separate venv (.venv-florence/) due to dependency conflicts with SD WebUI. The tests/florence2_tagger.py subprocess handles model loading and inference.
- Frequency band logic added to
run_pyramid_eulerin golden_test_runner.py - Golden test configs 14-18 cover blur+FB, downsample+FB, hi/lo emphasis, control
- Results: FB modulation helps blur mode (reduces CLIP loss from -0.0131 to -0.0068), downsample+FB works well (+0.0023)
- Band energies computed and logged in golden test results
- Algorithm: Adaptive freq-band weights from energy, step-dependent band weights, sigma-adaptive depth, pixel-space frequency recomposition
- Quality metrics: LPIPS, FID/KID, per-band energy ratios (no MS-SSIM)
- Performance: Latent-space blur (skip VAE), cached VAE decode, shared coarse UNet cache, half-precision VAE
- New features: Anisotropic blur, wavelet (DWT) decomposition, self-similarity guidance (DINO), SDXL/SD2.x support, ControlNet-aware blur/downsample
- PROMPTS expanded from 3 to 12 (abstract, text-heavy, macro, fog, lightning, architecture, multi-subject, watercolor, minimalist)
- Multi-seed support via
--seedsor--n-seedsflags; DEFAULT_SEEDS = [42, 123, 7, 2024, 555, 8888, 31415] - Wilcoxon signed-rank test for statistical significance (requires scipy, >= 6 non-zero diffs)
_aggregate_seed_results()computes mean/std CLIP delta, mean LPIPS, Wilcoxon p-value- Downsample parameter sweep via
--mode downsample(factor grid: 0.3, 0.5, 0.7)
- Manual WebUI verification: Live end-to-end test with hooks, UI, and actual image generation
- Live CLIP classification test: Verify
_sd_clip_encodeagainst real WebUI model hijack layer - Tagger backend alternatives: Test WD14 and CLIP Interrogator backends