Skip to content

feat(canvas): Clarity, Texture and Dehaze adjustments on layers and groups - #10001

Open
joshistoast wants to merge 25 commits into
mainfrom
feat/canvas-spatial-adjustments
Open

joshistoast wants to merge 25 commits into
mainfrom
feat/canvas-spatial-adjustments

Conversation

@joshistoast

@joshistoast joshistoast commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds Clarity, Texture and Dehaze as non-destructive adjustments on raster layers and raster groups, using the same kernels as the backend nodes from #9891.

  • Matches the nodes: results are byte-identical to the backend nodes run on the layer's alpha-trimmed pixels, so they don't depend on the bbox or crop.
  • Off the main thread: a background worker computes them. While a job runs, the canvas shows only the colour adjustments (an interim) and then installs the exact result. A layer row shows a delayed spinner while computing, or a warning when an adjustment can't run (over 16.8 MP, over the memory budget, or a worker error).
  • Exact or refused: generation, export, gallery save, Merge Down, rasterize, PSD export, extract, filter and Select Object inputs, crop, copy and painting all either use the exact adjusted pixels or refuse with a message naming the layer and the reason. A spatial adjustment is never silently dropped.
  • Settings: Amount runs from −200 to 200. Size uses a log scale: 4–1000 px for Clarity, 1–100 px for Texture.

Shares canvas schema v5 with the Noise adjustment (#9933).

Related Issues / Discussions

Closes #9813
Builds on #9891 (backend nodes) and #9933 (Noise adjustment, schema v5).

QA Instructions

Automated (Node 22.14, pnpm 10):

  • pnpm check:release passes: lint, 11,179 unit tests, 3,345 browser tests, fixtures, both performance gates, the project-file journey and accessibility journeys.
  • Python: pytest tests/backend/image_util passes, including the shared parity fixture that pins the TS kernels to the Python output.

Manual, on the mock backend:

  1. Add a raster layer and paint on it. Right-click → Add adjustment → Clarity, Texture or Dehaze.
  2. Scrub Amount: the canvas updates within about 0.1 s at 1024². For example, Dehaze +200 lifts a 127 grey to 136, which matches the worker output.
  3. Toggle the entry off and on: pixels return to the original values and back.
  4. Reload: the entries persist (the document is written as v5).

Not yet done: a real-backend comparison of a canvas result with the node on the same alpha-trimmed image.

Screenshots, on the mock backend:

Add adjustment menu on a raster layer:

Layer context menu with Add adjustment open, listing Texture, Clarity, Dehaze and Noise

Texture +80, Clarity +120 and Dehaze +60 on one layer, with Clarity selected:

Layer with Texture, Clarity and Dehaze rows; Clarity Amount and Size settings

Dehaze settings and hint:

Dehaze Amount setting with its approximation hint

Before and after, with the three adjustments off, then on:

The same photo without and with the three adjustments

Row status: the spinner while a job runs on a 3072² layer, and the warning with its tooltip on a 4100² layer:

Layer row with a spinner while the adjustment computes Layer row warning and tooltip for content over 16.8 megapixels

Review

Resolved findings:

  • Cooldown cancellations: cooldown no longer cancels the exact jobs that generation waits on.
  • Painting refusals: painting on a layer whose adjustment can't run now gets a specific message.
  • Size-limit retries: an oversized layer no longer retries on every slider tick.
  • Group status: a group's status no longer flips between the canvas and overview reads.
  • Stale warnings: a hidden layer's warning clears once its adjustment is removed.

Known limits:

  • Offscreen thumbnails: offscreen layers that have never been drawn show only the colour adjustments in their thumbnail.
  • PSD groups: PSD export approximates spatial group stacks per layer (documented).
  • Previews in groups: while a filter preview or floating selection sits inside a spatial group, the canvas and the overview each compute the group.
  • Large layers: layers over 16.8 MP are refused; tiled evaluation is a follow-up.

Compatibility / Rollout

  • Schema: documents holding any of the three adjustments, enabled or not, save as canvas schema v5. This is shared with Noise (feat(canvas): noise adjustment for film grain #9933), so older builds refuse those documents instead of silently dropping the adjustment stack. Documents without them keep their current version.

  • Bundle size: measured against feat(canvas): noise adjustment for film grain #9933's build:

    • about +22 KB raw startup script on editor routes and +30 KB on canvas;
    • +2.4 KB of locale strings;
    • +7.7 KB when the canvas widget opens;
    • no new requests.

    The browser baseline re-records only byte figures and startup-module lists; timings are unchanged. The kernels load lazily in a 9 KB worker chunk.

  • Memory: worker scratch (at most about 400 MB, one job at a time per engine) is outside the raster memory budget. The pixel buffer each job uses is reserved.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

joshistoast and others added 19 commits October 18, 2026 22:40
Adds a Noise raster/group adjustment with Gaussian or Uniform distribution, monochromatic or color grain, amount, and a re-rollable seed.
Noise is a deterministic function of position, so display, generation, export, merges and PSD share the same grain.
Layer bakes and PSD now apply a layer's own adjustments before resampling, matching the display.
Noise gains a 1–8 px grain size (coordinate-pure Gaussian-correlated samples), a midtones response that fades
grain to none at black and white, and a chroma grain on OKLab a/b that keeps lightness.
Re-records the editor source-owner set and canvas route bytes for the larger kernel.
Any noise adjustment, enabled or not, raises the canvas and the project floor to 5 and never lowers it,
so older builds refuse the project instead of dropping the adjustment stack and saving the loss.
Merge Down bakes both layers' stacks layer-local before compositing; Rasterize bakes a noise stack before the
transform and drops it. Composites reserve measured text scratch past its estimate or refuse as over budget.
Covers grain size, tonal response and chroma, how Merge Down and Rasterize bake adjustments, the noise kernel's
cost and region-independence invariants, and that older versions refuse projects with noise.
…ustment

# Conflicts:
#	invokeai/frontend/webv2/performance/browser-baseline.json
#	invokeai/frontend/webv2/src/workbench/projects/projectFile.test.ts
A legacy project kept whole because its workflow repeats an item id now sends the raised canvas floor like split
saves do, so saving Noise into it stores floor 5 and older clients refuse it.
Export reuses the display's adjusted copy only when it is current and never builds a layer-sized one, so a cache miss adjusts just the reserved sampled region.
Add Clarity, Texture, and Contrast Dehaze approximation invocations with shared numerical primitives, schemas, tests, and docs.
Allow Clarity, Texture, and Contrast Dehaze amounts from -200 to 200.
…ab restore

Restore Oklab lightness in float64 per tile and gamut-clip only pixels more than 1e-5 out of range, so float32 round-trip noise no longer shifts colours with a 0 or 255 channel by up to 45 levels.
…backend nodes

Port the nodes' numerics into the canvas engine (alpha-bounds domain, OpenCV chamfer fill, guided-filter bands, Oklab with gamut clip) and replace the unused Generation primitives.
A Python-generated parity fixture pins both sides byte for byte; provenance records the OpenCV and Oklab sources.
Add a lazily created module worker and a job service with priorities, same-slot replacement, abort, memory reservations and crash recovery. Nothing renders through it yet.
…schema v5

Adds the three adjustment entries to raster layer and group stacks. Documents holding any of them, enabled or not, write schema v5 so older builds refuse them instead of dropping the stack.
…he worker

Layers and groups show a per-pixel interim while a job runs and install only exact results. A keyed status store reports computing, too-large, over-budget and error per owner.
…djustments

Every pixel-writing operation evaluates spatial stacks exactly in the worker or refuses with a message naming the layer and the cause. PSD export approximates spatial group stacks per layer.
Adds the menu entries, Amount and Size controls, and a row indicator for pending or failed adjustments, with user docs.
@github-actions github-actions Bot added CI-CD Continuous integration / Continuous delivery python PRs that change python files invocations PRs that change invocations backend PRs that change backend files frontend PRs that change frontend files python-tests PRs that change python tests docs PRs that change docs labels Oct 23, 2026
Main's growth and the Noise adjustment together exceed the 1% byte allowance on the editor build and on canvas activation.
Base automatically changed from feat/canvas-noise-adjustment to main October 23, 2026 17:13

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend PRs that change backend files CI-CD Continuous integration / Continuous delivery docs PRs that change docs frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files python-tests PRs that change python tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[enhancement]: Adjustment layers: Vibrance/Saturation and Clarity/Dehaze

2 participants