Commit Graph
187 Commits
Author SHA1 Message Date
shootthesound d1416121bc chore: update Krea + Klein example workflow 2026-07-07 16:17:22 +01:00
shootthesound 48df2240d3 feat: gen bundle — generate with one model, edit with another (2.6.0)
The Angelo — Overrides node now carries an optional second model stack
(gen_model / gen_positive / gen_negative / gen_vae connection inputs
plus gen_steps / gen_cfg / gen_sampler_name / gen_scheduler settings).
When wired, Sampler Mode generates the base with the gen stack, decodes
with the gen VAE, and re-encodes the pixels with the main edit VAE so
the whole editing session runs on the edit model — generate with FLUX
Krea / SDXL for the look, click-to-edit with Klein / Qwen-Image-Edit.

Behaviour:
- Edit Mode never reads the gen bundle; unwired = byte-identical to 2.5
- Seed + Smpl Denoise stay on the toolbar (seed controls keep working;
  denoise < 1.0 = img2img with the gen model, incl. off a loaded image)
- gen_negative optional: falls back to a zeroed gen_positive
  (ConditioningZeroOut maths)
- Preview/history pixels are the gen VAE decode (not the edit-VAE
  round-trip); base pixels cropped to /16 for any edit VAE ratio
- Cached-base / loaded-image Sampler runs derive the gen latent by
  encoding the base pixels with the gen VAE
- guider/sampler/sigmas trio deliberately not applied to the gen model
  (a wired GUIDER wraps the edit model's conds)

Hardening from the bug-check pass (2 inline + 3 agent-review findings):
- gen widgets live in optional inputs, not required: ComfyUI validates
  required inputs server-side BEFORE Python defaults apply, which would
  have rejected every pre-2.6 API-format export of the Overrides node
- explicit None checks in overrides parsing (gen_cfg 0.0 is falsy and
  an `or` fallback silently sampled at the default)
- partial gen-bundle wiring (any subset incl. gen_negative-only) raises
  a clear ValueError in Sampler Mode instead of silently falling back
- wired-latent guard: wrong-channel latents raise a clear not-in-the-
  gen-model's-latent-space error; 5D video latents into a 4D gen model
  get their singleton frame axis squeezed instead of crashing deep in
  the UNet
- console prints the gen resolution each run (an empty latent authored
  for a 16x edit VAE silently means half-size through an 8x gen VAE —
  undetectable from the latent alone, so make it visible)

Fingerprint stays edit-model-fixed so Edit Mode never spuriously
resets; _STATE untouched until the whole gen chain succeeds
(cancel/OOM safe).

Also: example_workflows/Krea_plus_Klein-workflow.json (FLUX Krea 2 +
turbo LoRA at 1504x1504 through the Wan 2.1 VAE, Klein 9B editing) and
full README coverage (Quick start section, Overrides wiring rules,
Krea 2 + Wan 2.1 VAE pairing note).
v2.6.0
2026-07-07 16:15:55 +01:00
shootthesound 0ac2e8a46d feat: in-node Queue button + full-flow bug-hunt hardening (2.5.0)
New:
- ▶ Queue button beside the Mode dropdown — the in-node twin of
  ComfyUI's Queue button (same code path, so Persistent Mask re-runs
  work identically) usable from both modes and fullscreen.
- Re-roll after ✨ Quick Photo Refine now re-runs the ✨ pass (same as
  pressing ✨ again) instead of silently corrupting the session.
- Outpaint Accept shows the approved canvas instantly (optimistic swap
  from the already-loaded review image; commit lands in the background,
  and image_w/h update immediately so edits in the gap map correctly).
- One-shot migration for v2.0.0–v2.1.0 workflow saves: the lir_seq
  widget removed in 2.2.0 shifted positional widgets_values, corrupting
  quick_prompt_mode/shrink_seq and failing Queue validation. Detected
  semantically (a number on a combo), repaired on load, self-deactivates
  on the next save.

Fixed (three-agent audit of backend flows, frontend flows, and the
JS↔Python contract):
- Reset was a silent no-op with Persistent Mask ON (queue hook cleared
  the reset flag pre-serialization; hook now skips reset-pending nodes).
- Internal action queues bumped EVERY persistent-mask node — editing
  node A re-ran node B's held mask. Initiator-scoped now.
- Re-roll / repeat-✨ popped history before sampling: a Cancel/OOM
  permanently lost the last result. Both are now sample-first,
  replace-on-success.
- Re-roll/Vary after Undo/Redo/✨ re-ran stale mask widgets (worst
  case: a bogus corner-circle edit from click_x=-1). History entries
  now carry provenance; Re-roll/Vary gate on it.
- Reset after _HISTORY_CAP eviction rebased onto a mid-session image
  and overwrote the session base. Base fallbacks prefer source_latent.
- FLUX 2 large-canvas decodes missed the anti-NaN tiled path (threshold
  assumed an 8x VAE); ratio now queried from the VAE.
- Vary chooser surviving an Undo could commit onto the wrong history
  entry (stash cleared on undo/redo; JS closes the chooser).
- No-op edits (Remove without CLIP, empty mask) pushed junk history and
  killed Redo.
- Esc was dead over the Vary chooser / Outpaint review (hover lost to
  the overlay); Esc in a fullscreen popup exited fullscreen and
  destroyed the popup (defer + rescue on exit + listener leak fixed).
- Remove's Xtra-Fine restore state now survives workflow reloads
  (node.properties).
- Pan speed under graph zoom, context menu behind fullscreen,
  Smpl Denoise 0.0 failing server validation, mode fallback order.
v2.5.0
2026-07-04 04:22:43 +01:00
shootthesound f787704c71 fix: show body-level popups over the fullscreen overlay (2.4.1)
Smart Phrasing / Load Image / Shrink popups attach to document.body at
z-index 10000, but the fullscreen overlay is z-index 99990, so they
rendered behind it and were invisible until you exited fullscreen. Parent
these modals into the fullscreen overlay when it's active (it owns the
stacking context, so a fixed backdrop inside it still covers the viewport
and renders above the editor); unchanged outside fullscreen.
v2.4.1
2026-07-03 02:42:45 +01:00
shootthesound 023f89227e feat: Remove brush — erase objects and rebuild the background (2.4.0)
Paint over an object (or Detect / click it) and the edit model rebuilds
the background behind it. Avoids the usual 'ghost' by cutting the masked
region OUT of the reference the model anchors to (a real hole in the
reference latent), so it continues the surroundings instead of redrawing
the object — same idea as the Outpaint protect brush.

- Single full-denoise pass on the Xtra-Fine crop path (forced on;
  respects Ctx Pad / MP / Method) so it rebuilds fine background detail.
- Fixed background-fill instruction; main/Area prompt and Denoise box
  are ignored. Feather forced 0, mask auto-grows ~10%.
- Toggling Remove auto-enables Xtra-Fine and restores it on the way out.
- Edit models only; Refine mode only; mutually exclusive with Restore.

README: new 'Remove brush' section + feature bullets + toolbar row;
elevator pitch and registry description updated.
v2.4.0
2026-07-01 15:22:42 +01:00
shootthesound 40b8ebf484 feat: Fullscreen editing mode (2.3.0)
Pop the whole editor (toolbar + area-prompt box + canvas) into a
viewport-filling overlay for a much bigger canvas. The container is
reparented intact, so every click/paint/zoom/pan/detect/outpaint
handler keeps working — mapping via getBoundingClientRect at the new
size — and the ResizeObserver auto-refits the image.

- New '⛶ Fullscreen' toggle pinned top-left of the Mode row.
- Best-effort true browser fullscreen; falls back to a viewport overlay.
- Exit via the button, a floating '✕ Exit Fullscreen' button, or Esc.
- Layered Esc: first dismisses any open Outpaint/Vary/Detect review,
  second leaves fullscreen.
- Tears down cleanly on node removal; restores container to its exact
  DOM slot on exit.

README: new Fullscreen section + screenshot, feature bullet, and
keyboard-shortcut row. pyproject description refreshed (also drops the
stale 'Large Image Refine' mention).
v2.3.0
2026-07-01 11:34:21 +01:00
shootthesound 0c70813e65 examples: update Klein 9B example workflow 2026-06-30 17:17:56 +01:00
shootthesound d823a6223e release: v2.2.1 — clearer Reference tooltip (drop stale Quick Photo Refine line) v2.2.1 2026-06-30 17:10:05 +01:00
shootthesound 0595f486e0 docs(ui): rewrite the Reference tooltip — clearer, and drop the stale line claiming it affects Quick Photo Refine (QPR ignores it now) 2026-06-30 17:08:16 +01:00
shootthesound bfa2448d9e release: v2.2.0 — Lite mode + Shrink button; VAE/load/seam fixes; Large Image Refine removed v2.2.0 2026-06-30 13:42:40 +01:00
shootthesound f4ea545d12 tweak: Lite mode denoise 0.49 -> 0.8 2026-06-30 13:36:44 +01:00
shootthesound 73564ea84e docs: README — replace Large Image Refine with Lite toggle + document ⬇ Shrink 2026-06-30 13:15:09 +01:00
shootthesound 598c53b10d feat: replace Large Image Refine with a Lite toggle on Quick Photo Refine
Removed the ▦ Large Image Refine button entirely (constants, lir_seq widget,
handler, JS button + trigger + hide-list entry). In its place, a "Lite"
toggle sits beside the prompt selector in the Quick Actions bar.

Lite mode is the EXACT same Quick Photo Refine recipe (reference 1.0, 1.3MP
target, prompt selector, >1.6MP tiling with dual-grid seam-erase, re-roll-
from-original) with one change: denoise 0.49 instead of 1.0 — a lighter
restore that re-renders less and stays closer to the input. New quick_lite
BOOLEAN widget drives it; qr_denoise picks 0.49 vs 1.0 and feeds both the
tiled and whole-image paths.

Note: lir_seq was a released (v2.1.0) widget sitting before quick_prompt_mode;
removing it shifts quick_prompt_mode's positional slot, so a v2.1.0 saved
workflow with a NON-default prompt mode may reset it to "Identity + Quality"
on load. Clean cut per project convention; flagged for the release notes.

README still documents Large Image Refine (8 mentions) — needs updating
before release. Both files parse.
2026-06-30 13:07:49 +01:00
shootthesound a01d8f8110 fix(load): large loaded images looked "edited" on load — preview the real file, not its VAE round-trip
Dragging/loading a big image (e.g. 7680x2160) showed it visibly modified the
moment it loaded, while small images just loaded cleanly. Cause: a loaded
image is only VAE-ENCODED to a base latent (_encode_loaded_image returned
just the latent), so history[0] was (latent, None). With no stored pixels,
the preview path decodes the latent — i.e. shows the VAE ROUND-TRIP of the
file, not the file. That round-trip is near-lossless on small images (reads
as a plain load) but visibly lossy on large ones (and now tiled), so it
looked like Angelo edited the image on load.

Fix: _encode_loaded_image now also returns the original pixels; the load
reset stores them in history[0] and source_pixels. The preview path already
prefers current_pixels when present, so a freshly loaded image now shows
exactly as the file. Non-load resets still pass None (decode as before).

Bonus: the first edit after a load now builds on the real pixels rather than
the round-trip, and hold-to-compare / the source_image output show the true
file. No extra memory — the pixels tensor is shared across history[0],
source_pixels and current_pixels.
2026-06-30 12:43:03 +01:00
shootthesound c3d483410e fix(vae): black/NaN output on very large canvases — auto-tile VAE encode/decode above 4MP
Large Image Refine (and any path that VAE-encodes/decodes a whole large
canvas) produced a fully black image with "invalid value encountered in
cast" on a 7744x2176 (16.8MP) input. Cause: _vae_encode/_vae_decode always
used the plain whole-canvas vae.encode/vae.decode. A single VAE pass at that
size OOMs or numerically overflows to NaN — silently (no exception) — so the
whole-canvas big_latent came back NaN, every tile cropped from it sampled to
NaN, and the blended pixels were NaN → np.clip leaves NaN → astype(uint8) =
black.

The tiling engine only ever tiled the SAMPLING; the whole-canvas VAE passes
(initial big_latent encode, final re-encode, the optional input decode) were
still single huge ops. Fix at the chokepoint: above _VAE_TILE_MP (4MP),
route through vae.encode_tiled / vae.decode_tiled (which also handle 5D
video VAEs). Below it, the plain exact/faster path as before — ~1.4k-px
sampling tiles and normal canvases are unaffected.

This also fixes the same latent bug for 2× Pixel and Xtra-Fine when their
output canvas is very large.
2026-06-30 12:19:17 +01:00
shootthesound 929a88a49e feat: ⬇ Shrink Image button — pick a scale factor (with live new-dimensions readout), area-resample downscale
New Quick Actions button after Large Image Refine. Opens a popup showing the
current dimensions, a percent input with 75/50/33/25% quick presets, and a
LIVE "New: W × H" readout (snapped to a multiple of 16, computed with the
same formula the backend uses so it matches exactly). On confirm it sets
shrink_scale + bumps shrink_seq.

Backend downscales with AREA resampling (comfy common_upscale "area") — box
averaging, the right method for shrinking: it averages the discarded pixels
so it anti-aliases, unlike lanczos (great for UPscaling) which can ring on
heavy reduction. No AI; committed straight as a fresh session base
(dimension change = Load-Image semantics, history resets), mirroring 2× Pixel.

New widgets shrink_seq / shrink_scale declared LAST (positional-compat for
old workflows) and added to all wholesale state-reset dicts + the hide list.

Also refreshed the now-stale Large Image Refine tooltip/comment/toast (still
said "chess-pattern boxes" from before the seam fix → "overlapping tiled").

Python + JS parse. Untested on a live model — needs a click in ComfyUI.
2026-06-30 11:36:33 +01:00
shootthesound 264640fea1 ux: separator after the Quick Photo Refine prompt dropdown — groups the dropdown with its button, apart from the upscale buttons 2026-06-30 11:24:18 +01:00
shootthesound 35166a87c3 release: v2.1.0 — tile-seam fix (overlapping engine for LIR + dual offset-grid seam-erase on both tilers) v2.1.0 2026-06-30 11:14:45 +01:00
shootthesound f98351c7da feat(tiling): kill tile seams — LIR uses the overlapping engine + dual offset-grid seam-erase on both tilers
Two seam fixes for the tiling paths:

1. Large Image Refine now routes through the shared overlapping tiled
   engine (_tiled_restore_pass: feathered ramp blend + one shared noise
   field) instead of the old exact-tile, hard-mask, chess-ordered boxes
   with bit-exact latent compositing. The old design left seams because
   the box boundaries were hard AND each box used a decorrelated seed
   (lir_seed + k*9973), so texture statistics jumped at every edge. The
   overlapping engine removes both causes. (option 1)

2. The engine gains a dual_grid mode: after the normal grid, a SECOND tile
   grid offset by ~half a tile (starts at the midpoints of the first grid's
   starts) is accumulated into the same weighted-blend buffers. The offset
   tiles are centred on the first grid's seams, where the first grid's
   tiles taper to low ramp weight — so the offset tiles dominate exactly
   there, overwriting any residual seam with continuous tile interior, and
   vice versa. Both grids crop the same absolute global noise field, so
   their renders agree in shared regions and the blend stays sharp. Enabled
   for BOTH Quick Photo Refine's >1.6MP path and Large Image Refine.
   (option 4) Costs ~2× tiles.

Removed the now-unused _LIR_CTX_PAD / _LIR_BOX_MP constants and the box/
chess machinery; updated the comments. AST parses clean. Untested on a live
model — needs an A/B in ComfyUI.
2026-06-30 11:05:14 +01:00
shootthesound 207060b758 release: v2.0.1 — refresh registry description for the 2.0 feature set v2.0.1 2026-06-29 22:29:02 +01:00
shootthesound 3d66abb767 release: v2.0.0 — Quick Actions redesign (Quick Photo Refine + 2× Pixel + Large Image Refine), Outpaint, Reference anchoring, SAM3 installer fix v2.0.0 2026-06-29 22:23:31 +01:00
shootthesound 74dac5f8b5 docs: README rewrite for the redesigned Quick Actions; destar casual prose only
README was stale on the upscale redesign — it still documented the deleted
2× Restore / 2× Upscale / face-polish buttons. Rewritten to match what
ships:

- Opening one-liner now mentions one-click photo refine + the 2× Pixel /
  Large Image Refine upscale pair (the elevator pitch was missing them).
- Quick Photo Refine: the v2 recipe (whole image through Xtra-Fine, 1.3MP
  target, ref 1.0 / denoise 1.0), the prompt selector (Identity + Quality /
  Restore Photo / Identity + Colours / Use Area Prompt), and re-roll-from-
  original semantics.
- The upscale pair: ⬆ 2× Pixel (pure lanczos, no AI, history resets) +
  ▦ Large Image Refine (chess-pattern Xtra-Fine boxes over the canvas).
- The load-small-at-high-MP -> Quick Photo Refine super-resolution
  workflow (degrade-then-reconstruct on the edit model's own reference
  branch, no extra models).
- Fine-print note that a whole-image semantic change runs as one global
  pass, not tiled.

Also removed the ✨/✦ star glyphs from CASUAL PROSE sentences in the README
only. Button labels keep them (toolbar diagram, feature-table name,
restoration heading + bold lead) to match the UI. No code/UI changes —
angelo.js and angelo_nodes.py are untouched.
2026-06-29 22:16:34 +01:00
shootthesound 26a9fff838 fix(sam3): stop SAM3 installer breaking ComfyUI's numpy + auto-fix SAM3.1 dtype crash (#31)
Two users reported the optional SAM 3 (Detect) installer wrecking their
ComfyUI install and then crashing at detect time. Root causes:

- sam3_requirements.txt pinned `numpy>=1.26,<2`, and the installer ran
  pip WITHOUT a constraint, so pip downgraded ComfyUI's numpy 2.x to
  1.26 to satisfy it -> broken ComfyUI. opencv-python (full) dragged it
  down too. Fix: drop the numpy pin entirely and have the installer write
  a constraints file pinning numpy to the version already present, passed
  to every pip call, so a downgrade is impossible (fails loud if a dep
  truly needs <2, instead of silently breaking the env).

- OpenCV: only install opencv-python-headless when cv2 isn't already
  importable, so we never clobber a full opencv another node provides.

- pycocotools is on SAM 3.1's image import path now; added to requirements.

- SAM 3.1's perflib/fused.py::addmm_act returns bfloat16 unconditionally,
  which collides with the fp32 weights Angelo builds SAM3 with
  ("mat1 and mat2 must have the same dtype, BFloat16 and Float"). New
  sam3_postinstall.py idempotently patches addmm_act to cast its result
  back to the caller's dtype. vitdet.py binds the symbol at import time so
  a runtime monkey-patch can't reach it - the file on disk must be edited.
  The installer runs the patcher on both the fresh-install and the
  already-installed paths, so existing installs get fixed on a re-run.
2026-06-29 21:47:52 +01:00
shootthesound b64e9414a9 ux: load-dialog tip range 2.5-3MP -> 1.5-3MP 2026-06-11 14:01:17 +01:00
shootthesound 92a7f63ed1 ux: Load Image popup teaches the restoration workflow
Gold tip box in the load dialog: low-quality/small photos -> load at
2.5-3MP via Resize-to, then press ✨ Quick Photo Refine. The dialog
is the natural moment of discoverability — it's where every
restoration session starts.
2026-06-11 13:56:02 +01:00
shootthesound 35789f20b4 feat: ✨ presses re-roll instead of chaining
If the latest history entry is itself a ✨ result, a new press POPS
it and re-draws from the same source — replace semantics, exactly
like Re-roll. Mashing on randomize now gives independent variations
of the same image instead of a converged fixed-point chain. Any
other operation (manual edit, LIR, undo/redo, vary pick — and the
base-swap ops implicitly) clears the quick_last flag, so manual work
between presses is never discarded.
2026-06-11 09:53:46 +01:00
shootthesound b374efc4ec fix: ✨ 'Use Area Prompt' mode never tiles — semantic edits need one global pass
The broken half-changed-person failure: above 1.6MP the auto-tile
branch gave each ~1.4MP tile the semantic instruction independently,
so each tile changed the person its own way. The manual brush worked
because its whole-canvas bbox exceeds the 1.3MP target -> scale=1.0
shortcut -> ONE global sample (the '[Angelo fine-upscale] scale=1.0'
console line). Area-Prompt mode now always takes that same global
path at any canvas size; the restoration presets keep the tiled
fallback, where per-tile locality is correct.
2026-06-11 02:52:07 +01:00
shootthesound 150e32c98c feat: prompt selector for Quick Photo Refine
Dropdown beside the ✨ button with four modes (persisted, default
unchanged):
- Identity + Quality (default; Qwen keeps its tuned variant on 5D)
- Restore Photo — identity clause + the proven restore instruction
- Identity + Colours — adds a colour hold for palette-critical work
- Use Area Prompt — the user's own text drives the pass (falls back
  to the default preset when the box is empty, with a console note)
Console logs the active mode per press. New hidden widget
quick_prompt_mode declared LAST.
2026-06-11 02:24:11 +01:00
shootthesound 0565ec5de9 feat: Quick Photo Refine v2 — whole-image Xtra-Fine pass with identity instruction
New recipe: the whole canvas (mask = all ones, feather 0) runs through
the Xtra-Fine pipeline at a 1.3MP working target — sub-1.3MP images
get internally supersampled to 1.3MP, refined there, and composited
back (free supersampling); larger ones take the in-place path. Ref
1.0, denoise 1.0, ctx pad 128 (no-op at full coverage). Prompt is now
multi-ref aware: 'Keep the identity from image 1. make the image high
quality.' ('identity' spelling corrected from the spec). The >1.6MP
auto-tile branch is left as-is (model-tuned restore prompts) — big
canvases have the dedicated Large Image Refine button now.
2026-06-11 01:22:29 +01:00
shootthesound beb0e11f74 docs: LIR tooltip — settled numbers (0.2/0.5/128) 2026-06-11 01:06:24 +01:00
shootthesound 74028525a9 feat: Large Image Refine recipe settled — ref 0.2 / pad 128 / denoise 0.5; TEMP knobs removed
The tuning knobs did their job: winners folded into the _LIR_*
constants (box 1.3MP, ref 0.2, ctx pad 128, denoise 0.5, feather 0,
chess order). TEMP widgets, inputs, and sync removed from both files;
tooltip updated to the settled numbers.
2026-06-11 01:06:09 +01:00
shootthesound fcb0e9748c temp: GUI tuning knobs for Large Image Refine — R / Pad / Dn beside the button
Three small inputs on the Quick Actions bar drive lir_ref /
lir_ctx_pad / lir_denoise widgets (defaults 0.4 / 128 / 0.55),
overriding the _LIR_* constants per run. The Pad knob is separate
from Xtra-Fine's regular Ctx Pad. Everything is marked TEMP in both
files — remove once the recipe is settled and fold the winners back
into the constants.
2026-06-11 00:20:41 +01:00
shootthesound 22ac86f69e tweak: Large Image Refine — boxes to 1.3MP, ctx pad 176 -> 128 2026-06-11 00:15:19 +01:00
shootthesound d9327e8632 feat: Large Image Refine — chess-pattern Xtra-Fine boxes over the canvas
New ▦ button beside ⬆ 2x Pixel. The canvas divides into ~1MP boxes
that exact-tile it (no overlap); each box runs the Xtra-Fine pipeline
(ref 0.4, denoise 0.55, 176px ctx pad, hard feather-0 masks) under
'restore the image. make it clear and sharp.' — in CHESS order, so
the second colour's boxes refine with already-refined neighbours
visible in their context pad and match them. Compositing is
Xtra-Fine's bit-exact latent blend per box — no pixel feathering
anywhere in the path. Whole pass = ONE history entry; per-box seeds
derive from the Seed widget. The MP-target nudge forces the crop
path so the whole-canvas shortcut can never fire. Box size is one
constant (_LIR_BOX_MP) for later tweaking.
2026-06-11 00:01:30 +01:00
shootthesound 86fb125891 fix: int(None) crash after 2x Pixel on a fresh session
The pixel-2x and outpaint-accept state dicts carried over
refine_seed_at_run from the prior state — None when no refine had
run yet — and the ui message's int() choked on it. Producers now
fall back to the current seed widgets; the consumer is None-safe
regardless.
2026-06-10 23:45:57 +01:00
shootthesound abb4f64faa feat: 2x Pixel replaces the AI upscale buttons — pure lanczos, no AI, direct commit
The fused AI-upscale pipelines (2x Restore, 2x Upscale, the tiled
upscale pass tuning, face polish + Faces toggle) are removed. One
button remains: ⬆ 2x Pixel — decode, lanczos 2x, re-encode, committed
IMMEDIATELY as a fresh session base (deterministic, so no review;
dimension change = Load-Image semantics). AI enhancement becomes the
user's next, separate move — ✨ Quick Photo Refine auto-tiles on the
resulting large canvas, Xtra-Fine spot-edits at any size.

✨ Quick Photo Refine and its tiled big-canvas path are untouched;
the tiled engine remains as its machinery. Outpaint's review overlay
returns to single-purpose. upscale_mode/face_polish widgets deleted
(never publicly released — no workflow compatibility impact).
The week of fused-recipe tuning is preserved on branch
tiling-experiments-abandoned and in history if ever wanted.
2026-06-10 23:41:52 +01:00
shootthesound 8f4c939155 experiment: upscale tile ref back to 0.1 (0.05 tested worse) 2026-06-10 23:19:53 +01:00
shootthesound c9e6c1179b experiment: upscale tile ref 0.1 -> 0.05 2026-06-10 23:13:17 +01:00
shootthesound 5657f624cd experiment: upscale tiles at ref 0.1 / denoise 0.35 (working config tagged upscale-config-working-1) 2026-06-10 23:05:40 +01:00
shootthesound ec6b99e005 tweak: upscale tile denoise 1.0 -> 0.8 (settled by testing) 2026-06-10 22:59:29 +01:00
shootthesound 7b13e43492 tweak: upscale tiles — prompt 'keep the colours, keep the style and restore the image', denoise 1.0
Full recipe per 1400px tile: ref 1.0 anchor, denoise 1.0 re-render,
colour/style-preserving restore instruction. Tooltips + README synced
(incl. stale ~1MP tile wording -> ~1400px).
2026-06-10 22:52:44 +01:00
shootthesound e5643aab5f tweak: upscale tile pass — ref 1.0 / denoise 0.85
Full anchor + high denoise on the 1400px tiles (the Quick Photo
Refine recipe shape, applied to upscaling). At ref 1.0 the blend
collapses to a single cond — faster than the fractional 0.3 it
replaces. Applies to the upscale tile pass on both buttons (2x
Restore stage 2 + 2x Upscale).
2026-06-10 22:44:44 +01:00
shootthesound fb57c2291b tweak: tile size 1024 -> 1400 (max tile ~1400x1400, the model's quality ceiling)
Back on the proven baseline (single self-ref per tile at 0.3/0.55,
proportional min overlap, uniform spacing) — bigger square tiles are
now the context lever: ~1.9x the scene per tile vs 1024, while every
dimension stays at or under 1400px. The abandoned strips/neighbour-
refs/colour-match line is parked on branch tiling-experiments-
abandoned for reference.
2026-06-10 22:36:52 +01:00
shootthesound dd7ea0b064 feat(tiled engine): canvas-proportional minimum overlap + uniform tile spacing
Minimum overlap now scales with the canvas long edge (88 + L/46,
floor 128): calibrated to the tested points — a 3872px canvas gets
min 172, a 7744px canvas min 256. And tiles are now UNIFORMLY spaced
per axis instead of flush-to-end: every seam in an image shares the
same overlap (>= the minimum), replacing the old lopsided layout
where one boundary got 128px and the flush pair got 900px.
2026-06-10 20:46:40 +01:00
shootthesound 0ea291f100 revert: tone lock + overlap widening — broke the working case
The low-pass swap's cutoff (tile/16 ~= 64px) traded structural
frequencies for the BLURRY lanczos base's, haze-washing every tile —
the 1936x544 2x went from great to broken. Reverted along with the
256px overlap; back to the proven 128px blend. KEPT: the proportional
face-polish context pad (max(64, 25% of bbox edge)) — the part that
was actually asked for.
2026-06-10 20:31:32 +01:00
shootthesound 9e30637904 fix(upscale tiles): visible banding on big canvases — tone lock + wider overlap + proportional face pad
The bands (e.g. 3872x1088 -> 2x showed them; 1936x544 -> 2x didn't):
at big canvases each ~1MP tile sees only a sliver of the scene, so
neighbouring renders drift slightly in tone — and a 128px crossfade
can't hide a tone STEP between tile centres, especially in smooth
regions (sky, gradients).

Three-part fix:
1. TONE LOCK (the cure): each upscale tile's low-frequency component
   is replaced with the base's — the single lanczos upscale has
   perfectly consistent global tone, so every tile inherits it while
   keeping its own refined high-frequency detail. Implemented as a
   cheap bilinear down/up low-pass swap. Applied to the upscale tile
   pass only (restore passes may legitimately change tone globally).
2. Upscale tile overlap widened 128 -> 256px (25% of tile) for a
   gentler crossfade; engine takes overlap_px per call.
3. Face-polish ctx pad now proportional: max(64, 25% of bbox edge) —
   fixed 64px was real context for a 300px face, almost none for an
   800px one.
2026-06-10 20:25:40 +01:00
shootthesound 2021ac0eb8 feat: Faces toggle on the Quick Actions bar — opt out of the face-polish stage
Small lit toggle (default ON) beside the upscale buttons. OFF skips
stage 3 entirely (console notes it) — for non-portrait work or users
who'd rather polish by hand with Detect + Fix All. New hidden widget
face_polish; tooltip carries the per-flavour numbers and the SAM 3
requirement.
2026-06-10 18:42:11 +01:00
shootthesound 9e611c7a2a docs: 2x Restore tooltip — face polish numbers corrected (0.75/0.7) 2026-06-10 18:40:44 +01:00
shootthesound c076f3e893 tweak: face polish per flavour — 2x Restore at ref 0.75 / denoise 0.7
A restoration must not change who the person is: restore-mode face
stamps anchor much harder (ref 0.75) while re-rendering more texture
(denoise 0.7). Plain 2x Upscale keeps the freer 0.3/0.55. README
updated; tooltip-level numbers left generic.
2026-06-10 18:40:05 +01:00
shootthesound b4f3d31ced tweak: upscaler prompts say 'image' not 'photo' (own constants, ✨ keeps photo wording) 2026-06-10 18:20:56 +01:00