Prompt-driven roles: drop imageN_role widgets, styleN/characterN decide per-slot processing

This commit is contained in:
KonokoAz
2026-07-14 20:03:34 -06:00
parent 5b62d874a1
commit 04f26bdf4f
2 changed files with 18 additions and 21 deletions
+7 -4
View File
@@ -63,14 +63,17 @@ Civitai / Hugging Face and put it in `models/loras/`.
### Krea 2 Reference Encode (`TextEncodeKrea2Reference`)
Prompt + up to 4 reference images. Refer to them as `Picture 1..4` (or
`image1..4` — auto-normalized). Images connected but not mentioned get a role
clause appended automatically.
Prompt + up to 4 reference images. **Roles live in the prompt**: write
`character1..4` for identity references and `style1..4` for style-only
references (their patches are shuffled so the subject can't leak; `characterN`
wins if both appear). Both expand to the exact clauses the LoRA trains on —
`"The character1 driving a car in the style2"` is a complete prompt. Plain
`image1..4` / `Picture 1..4` also work; connected images never mentioned get a
character clause appended automatically.
| Input | Description |
|---|---|
| `imageN_detail` | vision tokens per ref: low/normal/high/max = 256/512/1024/1280px. High for characters, low for style/pose hints |
| `imageN_role` | `content` (identity) or `style` (32px grid patch-shuffle: destroys the subject, keeps the style) |
| `imageN_mask` | crop to mask bbox + white-out background before encoding |
| `bg_removal` | wire `LoadBackgroundRemovalModel`: auto-cleans content refs |
| `layout_split` | first fraction of steps conditioned on text only (0.25 kills collage layouts zero-shot; unnecessary with the LoRA) |
+11 -17
View File
@@ -21,10 +21,6 @@ DETAIL = {"low": 256, "normal": 512, "high": 1024, "max": 1280}
DETAIL_TIP = ("Longest side this reference is scaled to before the vision encoder. Higher = more "
"vision tokens = stronger, more literal reference. Use high for the character/subject "
"and low/normal for pose or style hints to avoid side-by-side collages.")
ROLE_TIP = ("content: the image is used literally (character, subject, pose). "
"style: its patches are shuffled before the vision encoder, destroying the subject's "
"shape so only palette/linework/texture transfer — prevents the style image's subject "
"from appearing in the output.")
SPLIT_TIP = ("Fraction of the denoise steps that see TEXT-ONLY conditioning before the references "
"kick in. The first steps fix the layout, so 0.25 strongly prevents side-by-side "
"collages of the references. 0 disables.")
@@ -89,8 +85,8 @@ EDIT_TEMPLATE = (
_IMG_WORD = re.compile(r"\b(?:image|img|imagen|picture)[ _]?([1-4])\b", re.IGNORECASE)
# Role-typed shorthand: "character1" / "style2" expand to the exact clauses the
# reference LoRA trains on, and "styleN" additionally forces that slot's role to
# style (patch shuffle) — the prompt wins over the widget.
# reference LoRA trains on. Roles are prompt-driven: styleN shuffles that slot's
# patches (style-only transfer); characterN keeps it literal and wins over styleN.
_CHAR_WORD = re.compile(r"\b(?:character|char|personaje)[ _]?([1-4])\b", re.IGNORECASE)
_STYLE_WORD = re.compile(r"\b(?:style|estilo)[ _]?([1-4])\b", re.IGNORECASE)
@@ -98,11 +94,16 @@ _STYLE_WORD = re.compile(r"\b(?:style|estilo)[ _]?([1-4])\b", re.IGNORECASE)
def expand_role_tokens(prompt):
"""'The character1 in a car in the style2' ->
'The character from Picture 1 in a car in the exact art style of Picture 2'.
Returns (text, slots_forced_to_style)."""
Returns (text, roles): roles are prompt-driven — styleN marks the slot as
style (patch shuffle); characterN marks it content and wins if both appear
for the same slot (identity needs the unshuffled image)."""
style_slots = {int(m.group(1)) - 1 for m in _STYLE_WORD.finditer(prompt)}
char_slots = {int(m.group(1)) - 1 for m in _CHAR_WORD.finditer(prompt)}
text = _CHAR_WORD.sub(lambda m: "character from Picture " + m.group(1), prompt)
text = _STYLE_WORD.sub(lambda m: "exact art style of Picture " + m.group(1), text)
return text, style_slots
roles = tuple("style" if i in style_slots and i not in char_slots else "content"
for i in range(4))
return text, roles
# Auto-added when the prompt doesn't mention a connected image. Mirrors the
@@ -142,26 +143,22 @@ class TextEncodeKrea2Reference:
"prompt": ("STRING", {
"multiline": True,
"dynamicPrompts": True,
"tooltip": "Shorthand: character1..4 / style1..4 expand to the trained clauses (styleN also sets that slot to style role). Or refer to refs as image1..4 / Picture 1..4, "
"tooltip": "Roles come from the prompt: character1..4 = identity reference, style1..4 = style-only (patches shuffled). Both expand to the clauses the LoRA trains on. Plain image1..4 / Picture 1..4 also work, "
"e.g. 'Redraw the character from image1 in the pose of image2'.",
}),
},
"optional": {
"image1": ("IMAGE",),
"image1_detail": (list(DETAIL), {"default": "high", "tooltip": DETAIL_TIP}),
"image1_role": (["content", "style"], {"default": "content", "tooltip": ROLE_TIP}),
"image1_mask": ("MASK", {"tooltip": MASK_TIP}),
"image2": ("IMAGE",),
"image2_detail": (list(DETAIL), {"default": "high", "tooltip": DETAIL_TIP}),
"image2_role": (["content", "style"], {"default": "content", "tooltip": ROLE_TIP}),
"image2_mask": ("MASK", {"tooltip": MASK_TIP}),
"image3": ("IMAGE",),
"image3_detail": (list(DETAIL), {"default": "high", "tooltip": DETAIL_TIP}),
"image3_role": (["content", "style"], {"default": "content", "tooltip": ROLE_TIP}),
"image3_mask": ("MASK", {"tooltip": MASK_TIP}),
"image4": ("IMAGE",),
"image4_detail": (list(DETAIL), {"default": "high", "tooltip": DETAIL_TIP}),
"image4_role": (["content", "style"], {"default": "content", "tooltip": ROLE_TIP}),
"image4_mask": ("MASK", {"tooltip": MASK_TIP}),
"bg_removal": ("BACKGROUND_REMOVAL", {"tooltip": BG_TIP}),
"layout_split": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.9, "step": 0.05, "tooltip": SPLIT_TIP}),
@@ -177,15 +174,12 @@ class TextEncodeKrea2Reference:
def encode(self, clip, prompt, image1=None, image2=None, image3=None, image4=None,
image1_detail="high", image2_detail="high", image3_detail="high", image4_detail="high",
image1_role="content", image2_role="content", image3_role="content", image4_role="content",
image1_mask=None, image2_mask=None, image3_mask=None, image4_mask=None,
bg_removal=None, layout_split=0.0):
slots = (image1, image2, image3, image4)
details = (image1_detail, image2_detail, image3_detail, image4_detail)
masks = (image1_mask, image2_mask, image3_mask, image4_mask)
prompt, style_slots = expand_role_tokens(prompt)
roles = tuple("style" if i in style_slots else r
for i, r in enumerate((image1_role, image2_role, image3_role, image4_role)))
prompt, roles = expand_role_tokens(prompt)
full_prompt, connected = build_image_prompt(prompt, slots, roles)
images_vl = []