Skip to content

Feat/inpaint improve#411

Merged
marcinz606 merged 6 commits into
mainfrom
feat/inpaint-improve
Jul 7, 2026
Merged

Feat/inpaint improve#411
marcinz606 merged 6 commits into
mainfrom
feat/inpaint-improve

Conversation

@marcinz606

Copy link
Copy Markdown
Owner
  • improve dust removal inpainting quality
  • add scratch removal polyline
  • fix some bugs related to manual dust removal brush overlay not exactly matching area that was touched up

…stroke tool

Replace the quality-limited fills with two patch-copy primitives (identical
math on CPU/numba and GPU/WGSL, parity by construction):

- Manual heals: Georgiev-style membrane clone — copy a real source patch
  (grain exactly matched) and blend the seam with a mean-value-coordinates
  membrane on the boundary difference (solver-free, single compute pass).
  Clone source is auto-picked at click time by rim-band SSD scoring and
  stored in the config. The Telea uint8 + synthetic-grain path is deleted.
- Auto/IR heals: guarded reflection copy — sample outward from the defect
  centroid for grain continuity, rotate away from other defects, fall back
  to the old trimmed mean so the worst case is unchanged. Detection untouched.

New Scratch Tool heals elongated defects: click a polyline along the
hair/scratch, double-click to commit, Esc cancels (mirrors the lasso).
Strokes live in RetouchConfig.manual_heal_strokes; legacy manual_dust_spots
still load and convert at pipeline time with deterministic fallback offsets.
Placed heals draw as outlines while a retouch tool is active.

Tiled export halo now grows with heal radius + source offset (capped 512px),
fixing the pre-existing bug where large spots sampled past the fixed 32px
halo across tile boundaries.
Membrane clone sampled the source patch and boundary points raw, so a speck
inside the source band got copied straight into the healed area.

Two-layer fix, mirrored on CPU (numba) and GPU (WGSL):
- Clone-sample guard: every cloned sample (source patch, both boundary
  sides) whose luma pops above its 3x3 luma-median neighbour by 0.06 is a
  speck — the median-luma pixel is used instead (a real pixel, grain kept).
  Ceiling: specks wider than ~2px fill the window and pass through.
- Source scoring now probes the candidate patch interior (chain + inner
  ring) and heavily penalizes lumas above the candidate band's median, so
  dusty patches lose to clean ones upfront — rim-band SSD alone could not
  see interior dust.
…apsule

The manual heal replaced the entire brushed capsule with shifted source
texture, which read as cloning a bigger area than selected and could still
drag a large speck along. The brush is now a search area:

- Destination dust gate (CPU + WGSL): a brushed pixel is healed only when
  its luma exceeds the membrane-predicted clean value (smoothstep 0.04-0.12,
  encoded domain). Clean pixels inside the brush stay untouched.
- The directly-cloned source sample upgrades from a 3x3 to a 5x5 luma-median
  guard, catching specks up to ~4px that slipped through before; boundary
  samples keep the cheaper 3x3.
The pipeline treated brush size as a radius while the overlay cursor draws
size/(2*preview_render_size) — half of it — so the healed area was 2x the
visible circle. Brush size is now a diameter everywhere: pipeline radius is
size/2 * scale_factor, matching overlay._brush_screen_radius 1:1 at any
zoom and resolution (the source-offset scoring radius in the controller
follows the same convention).

Locked in by tests: a dust strip crossing the brush is healed strictly
inside the brush circle (max changed-pixel distance <= radius, strip
untouched outside), and the pipeline radius fraction equals the cursor
fraction exactly.
Pure formatting (AST-identical): line joins/splits and blank-line removal
from a ruff format pass; no behavior change.
Same behaviour as double-click: Enter commits an in-progress scratch stroke
or closes a lasso polygon (only when it has >= 3 vertices — fewer keeps the
draw alive instead of wiping it). The overlay now takes click focus so its
widget-context shortcuts (Esc cancel, Enter finish) actually fire after the
user clicks the canvas; without a focus policy they never could.
@marcinz606 marcinz606 merged commit b9c3537 into main Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant