Commit Graph
16 Commits
Author SHA1 Message Date
ausboss 36a0b5ca6a Merge branch 'fix/mask-refine-fill-limit' into rc/2026-09-30
# Conflicts:
#	CHANGELOG.md
2026-09-29 22:39:30 -05:00
ausboss 11b5a6d56a Mask Refine: Max hole size, so fill holes leaves the part you kept
Paint everything except a person or a pet, turn on fill holes, and it
filled the unpainted subject too, so the part meant to stay was repainted.
The new optional max_hole_size input (a percent of the picture, 0 to 100)
sets the biggest hole fill holes fills. The default, 0, fills every hole
exactly as before, so saved workflows and API prompts run the same; at 2
the gaps a quick brush leaves still fill and a subject left unpainted
stays. Areas touching the picture's edge are never holes, as before.

- Hole sizes come from scipy's labels, the same 4-connected holes its
  fill finds; the torch fallback labels the same 8-connected holes as its
  own fill. Each picture in a batch is measured on its own, after expand.
- The card gets a Max hole size row with a % unit, dimmed while Fill holes
  is off, and a resetUnknown fallback of 0 for 2.x saves that hold the
  card's empty value in its slot.
- Tests: a hole above the limit stays, holes up to it fill, 0 matches the
  old fill, edge-touching areas never fill, the torch fallback agrees,
  batches, and older saves opening with 0.
- Node API snapshot, help page and changelog.
2026-09-29 22:38:36 -05:00
ausboss d14b717090 Stitch Inpaint: Tone match works with Seam set to blend in
Blend in kept the model's colour as painted, so a model that paints
lighter, darker or warmer left the new area off-tone. Classic's Tone
match is no answer on turned pictures: it reads the grey rim a turn
leaves on the picture's edge as drift, and its flat offset lifts the
black of a dark new area.

With Tone match on, blend in now compares the model's version of the
strip it redrew with the picture at the same pixels (tone layers over
the same clean picture pixels, clipped highlights left out), fits the
drift as curves over brightness with a pinned and a free part, checks
the fit on held-out tiles, adds a local gain along the edge only when
that holds out too, and takes the result back off the model's picture
before the blend. Blacks stay black; nothing changes when the check
fails, the model kept the colours, or there is no feather to read. A
clip is measured once.

Classic is unchanged (its pins), and blend in at Tone match 0 is pinned
to the 2.4.0 stitch. The card no longer dims Tone match under blend in.
2026-09-29 16:56:12 -05:00
ausboss 8d03e59d2b Open 1.x saves with the preview and Stop on missing LoRA on
Workflows saved by the 1.x releases also end in a panel's empty value: the
preview panel on LaMa Inpaint and Mask Refine (Select Frame from 1.2.0) and
LoRA Loader's rows. The inputs added in 2.0.0 sit in those slots, so preview
opened as "" (the switch showed on, but the prompt ran without a preview)
and on_missing opened as "" (the menu showed Stop on missing LoRA off while
the node still stopped the run). Each now opens on its default, which is
also what an API prompt without the input runs with: the three cards reset
preview through resetUnknown, and LoRA Loader, which has no card, applies a
RESET_UNKNOWN when a workflow opens. On a real 1.x save (pack 1.3.0 saved by
frontend 1.47.12) only those four values change.

The guard's history now reaches back to 1.0.0. Mask Refine's preview was
inserted ahead of an existing socket rather than appended, so the table now
lists each node's last input as of this guard and APPENDED_SINCE declares
everything added since the node first saved an empty value. The fallback
check also reads RESET_UNKNOWN, and tests/test_card_fallbacks.py becomes
tests/test_saved_fallbacks.py, which checks both kinds.
2026-09-29 03:20:45 -05:00
ausboss 577d69c1bf Crop For Inpaint: open older saves with Stay in picture on
A workflow saved before 2.4.0 holds the widget card's own empty value in
the slot keep_inside took when it was appended. The frontend restores
widget values by position, so keep_inside loaded as "", the card showed it
off and the queued prompt sent "", which ComfyUI reads as False: the old
padded crop. An API prompt saved before 2.4.0 has no keep_inside and runs
with the node's default, True. The same workflow gave two different crops.

The card now puts True, the node's default, back on a keep_inside that is
neither true nor false before it draws, the way it already resets an
unknown Seam on Stitch Inpaint. The resetUnknown check moves into
widget_card_math.mjs (holdsUnknownValue, resetUnknownValues) and learns
switches: the old check only knew a choice's options, so it would have
reset every saved switch. A saved true or false is left alone.

The backend is unchanged. ComfyUI converts a BOOLEAN "" to False before
VALIDATE_INPUTS or the node sees it, so an API prompt that carries "" (an
API export made on 2.4.0) still runs with it off.
2026-09-29 02:06:24 -05:00
ausboss 20afbf2536 Stitch Inpaint: move Seam into a gear menu
The card keeps its old look and height. Seam has no row and no socket
now: a small gear in the card's top-right corner opens a menu with the
two choices, each with one plain line. With blend in on, Tone match and
Fix edge halo stay in place but dim and stop taking input, with a hover
hint, and a "blend in" chip beside the gear shows the mode.

The seam widget is a socketless combo, so the frontend makes no input
slot for it in either renderer; a stale unlinked slot from an earlier
save is dropped on load. The backend input is unchanged.

The widget card gains two generic options for this: rows can be muted
by the current values, and a card can carry small corner tools that
every row's control keeps clear of. A gear-menu choice can list one
line per option.
2026-09-29 00:19:01 -05:00
ausboss 65784651d1 Stitch Inpaint: add a Seam option for outpaint canvases
Seam is a new optional choice, appended after color_match. "classic"
is the default and the old stitch, bit for bit. "blend in" hands the
model's picture over to the source by depth into the picture: colour
and tone change over slowly, fine detail changes over in a short ramp
where the two line up, the new area keeps the model's pixels, and the
deep picture keeps its own. There is no global tone shift, so
color_match and fix_edge_halo do not apply and the card hides them.

The depth map comes from the generated-area mask of a Crop + Rotate
+ Pad stitcher, or from the source rectangle of a Load Image + Pad
stitcher, with sides flush with the frame left out. It is computed
once per stitcher and shared by every frame, with scipy when present
and an exact torch fallback otherwise. Crop For Inpaint stitchers
keep the classic stitch. With blend in, blend_mask is the hand-over
weight.

An older workflow can carry the card's empty value in the slot Seam
now takes; the card resets it to classic on load and the node accepts
it as classic. Classic output is pinned against the previous code for
Load Image + Pad, Crop + Rotate + Pad, Crop For Inpaint and clip
stitchers.
2026-09-29 00:19:01 -05:00
ausboss b8c9dff562 Crop For Inpaint: keep the crop inside the picture (on by default)
When the grown context box was bigger than the picture on an axis (a wide
or tall mask, or a small picture), fit_rect centred it and the overflow was
replicate-padded: the sampler got stretched copies of the edge pixels, and
a megapixel target spent its budget on them. On a 1184x2112 portrait with
the coat painted, only 42% of the canvas was the picture and the new part
was drawn at 0.52x.

New optional input keep_inside (card row "Stay in picture", on by
default) clamps the box to the picture on the overflowing axis before it
is fitted, so the crop holds only real pixels (0.80x on that portrait).
The helper keeps the old padding by default for existing callers; off on
the node is the old crop. Appended as the last optional input, so saved
workflows keep their values; ones whose box ran past the picture now
render a little differently.
2026-09-28 18:07:50 -05:00
ausboss 9e33f62ba6 Merge main into feat/load-image-pad-input 2026-09-27 18:04:30 -05:00
ausboss fdbc674d8a Add Realign to Source (experimental): undo an edit's zoom and shift
Qwen Image 2.1 draws broad edits (watercolor, anime, oil) a few percent
taller than the source, up to about 12%, differently for every seed, and
snapping sizes does not change it. Realign to Source measures that zoom
and shift against the source and warps the edit back onto the source's
frame, at the source's size. empty_mask marks the strip the model pushed
out of view; report gives the measured zoom, shift and worst corner, or
why a frame was left as it is. The display name carries EXPERIMENTAL.

Promoted from the lab node. The measurement is unchanged (the same
matrices on 166 real Qwen 2.1 renders). The warp now samples the edit
with its edge pixels extended for every fill, so the gray and source
fills no longer draw a dark line beside the strip.

New example: Qwen Image 2.1 Edit + Realign, the Qwen 2.1 Edit graph with
the realign step after decode, its report, and before and after compares.
2026-09-27 13:53:59 -05:00
ausboss f32917321e Load Image + Pad: pad a wired image
An optional source_image socket pads an image from another node instead
of the chosen file, so a step before the pad (flattening a transparent
picture onto white, say) no longer has to be saved and loaded back.

- The wire wins over the file; validation skips the file check while it
  is connected and runs core's link-type check itself, since asking for
  input_types turns core's off for the node.
- A wired run hands the canvas a small temp preview (one file per node,
  overwritten each run) and the true source size, so the stage draws the
  last image it padded; before the first run it shows an outline.
- Card rows gain linkedBy, so the Source row dims while a wire replaces it.
- Appended and optional with no widget: saved workflows and API prompts
  load unchanged. Node API snapshot refreshed.
2026-09-27 01:45:51 -05:00
ausboss 153eb3a25d Preview the file under the pointer in every Source list
A native select draws its option list itself, so hovering a name gave
nothing to go on: you picked by filename alone. Load Image + Pad, Load
Video and the crop / rotate / pad editors now open a list that shows the
hovered picture, or plays the hovered clip muted with its size and
length, and filters as you type; arrow keys, Enter and Escape work.

The picker (js/shared/media_picker.mjs) only reads the combo widget's
options and writes the chosen value back, so saved workflows, undo and
the API are unchanged. Pure parts live in media_list.mjs with tests.
2026-09-25 09:02:20 -05:00
ausboss aef487375a Save to the output folder itself by default
Save Image and Save Video defaulted to an AusBoss subfolder, and every
example workflow saved into one. New nodes now save to ComfyUI's output
folder as image_... and video_..., and the examples keep their file
names without the folder. Saved workflows keep whatever prefix they
already have.
2026-09-24 00:00:43 -05:00
ausboss 44024352ce Show Save Video's CRF and Metadata rows only for formats that read them
When Save Video moved to a widget card, its rows lost the per-format
hiding: CRF showed for gif, ProRes and FFV1, and Metadata for gif and
WebP. The old code that toggled the classic widgets was still running
under the card and could bring them back. The card rows now follow the
format directly.
2026-09-23 23:56:58 -05:00
ausboss 4cb2af204a Release 2.1.0: Qwen workflows and polished image/video controls 2026-09-22 15:23:07 -05:00
ausboss 699208dadc Prepare 2.0.0 release candidate 2026-09-07 19:56:23 -05:00