Fix visual-style blur with an anti-blur focus

The style recipe's strong-blur prep is its structure-safety guarantee, but the encoder was also studying the blur itself - on soft-medium references renders came out pervasively soft. The built-in style bundle now ships a focus that de-selects softness and scene layout; prep and pulls are unchanged. A/B render-verified across five references, two strengths, three seeds, single-card and in the two-card method. Crisper prep alternatives re-opened source-scene takeover and were rejected.
This commit is contained in:
Kevin Gilper
2026-07-06 18:15:19 -04:00
parent 0663ccf15b
commit 53db3aad0c
6 changed files with 73 additions and 9 deletions
+7
View File
@@ -155,6 +155,13 @@ changed):
guarantees. Keep `subject: avoid` and the treatment appropriate to the job,
and render-validate like everything else.
- **De-selection also fixes prep artifacts:** the built-in `suggest the
visual style` recipe ships a focus ending in "*not the image's blurriness
or soft focus, and not its subject or scene layout*" - its strong-blur
prep is the safety guarantee, and the focus stops that deliberate blur
from being studied as if it were the style. Copy the pattern if your own
heavy-treatment recipe starts echoing its treatment in renders.
## The `layers` Array, Exactly
### What the 12 positions are
+16 -4
View File
@@ -365,8 +365,8 @@ Caveat: reuse assumes the connected CLIP's behavior is unchanged — pick
### 8.1 Built-in quick recipes (the shipped values)
All fourteen keys per bundle; `layers` refers to the family tables in §8.2.
Values are the 2026-07 render-retuned set.
All fourteen core keys per bundle; `layers` refers to the family tables in
§8.2. Values are the 2026-07 render-retuned set.
| Recipe (dropdown label) | role | treatment | color | detail | study | framing | subject | early | late | cap | shape | global* | layers |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
@@ -386,6 +386,17 @@ Values are the 2026-07 render-retuned set.
\* `global` is inert on Krea 2 (§2.1); values are kept for pooled-emitting
encoders.
`suggest the visual style` additionally ships a `focus` (§9): *"the artistic
style: palette, medium, brushwork, art direction, and rendering finish - not
the image's blurriness or soft focus, and not its subject or scene layout"*.
The strong-blur prep is the recipe's structure-safety guarantee, but without
the focus the encoder also studied the blur itself - on soft-medium
references (watercolor, gouache, aged photos) renders came out pervasively
soft. De-selecting softness and layout at the instruction channel keeps the
prompt subject crisp and primary while the medium still arrives
(A/B render-verified 2026-07-06; crisper prep instead re-opened source-scene
takeover).
Role pull baselines (used by manual mode and as defaults for minimal custom
recipes): balanced 1.0/1.0, identity 1.0/1.0, style 0.8/1.35, palette
0.7/1.75, composition 1.25/0.35, framing 0.9/0.25, environment 0.7/0.8,
@@ -547,8 +558,9 @@ of a fox" at strength 1.0.*
2. **Feel curve** (§3): σ = 0.65^1.6 = 0.5014.
3. **Preparation** (§4): ≈384² px, full color, strong blur, and reduced
small-detail study. The encoder sees softened medium and finish cues.
4. **Prompt** (§5): system prompt gains the style role line and the avoid
subject rule; user turn is one image-pad line plus the prompt.
4. **Prompt** (§5): system prompt gains the style role line, the recipe's
anti-blur focus line (§8.1), and the avoid subject rule; user turn is one
image-pad line plus the prompt.
5. **Targets** (§3), both phases (m = 0.85): base = 0.5014 × 0.85 =
0.4262; token t = 0.4262 × 0.85 = **0.3623**; pooled g = 0.4262 × 1.85 =
0.7885 (inert on Krea 2).
+9
View File
@@ -396,6 +396,14 @@ guarantees - keep `subject: avoid` and a job-appropriate treatment, and
render-check like everything else. The stack report prints each card's
focus so you can confirm it reached the encoder.
One built-in uses it too: `suggest the visual style` ships an anti-blur
focus ("...not the image's blurriness or soft focus, and not its subject or
scene layout"). The recipe softens the reference before study on purpose -
that is its safety guarantee - and the focus keeps that deliberate blur from
being read back as the style, so subjects stay crisp while the palette,
medium, and brushwork arrive. You will see it on the card's stack-report
line; that is expected.
A complete example (the shipped template - render-validated values):
```yaml
@@ -782,6 +790,7 @@ If a loaded demo reports missing images, copy
| Away card makes the image muddy or empty. | Lower its strength below `0.30`, and make sure something positive (prompt or toward card) says what you *do* want. |
| Many cards fight each other. | Turn on `gentle balance`, or lower the two strongest cards. The report shows the applied scale. |
| Style card drags its subject in. | Check the prepared-references preview; if the subject is visible there, lower detail or strengthen the treatment. In manual mode, lower `Structure layers pull`. |
| Style results look soft or out of focus. | Make sure you are on the current tuning: `suggest the visual style` ships an anti-blur focus (printed on its stack-report line) that stops the recipe's deliberate reference-softening from being copied as the style. If soft-medium references (watercolor, hazy photos) still read soft, that is the medium arriving - lower the card below `0.6` or add crispness words to the prompt. |
| Re-runs are slow while tuning. | Set `Reuse image studies` to `reuse between runs` and keep the prompt fixed while you tune strengths. |
| Behaves oddly after swapping CLIP patches. | Use `always re-study` while experimenting with CLIP-side changes. |
+8
View File
@@ -24,6 +24,14 @@ class _V9Resolution(_V9Card):
QUICK_RECIPES = recipes.QUICK_RECIPES
@classmethod
def _recipe_settings(cls, recipe_key):
# V9's resolver predates the focus field and drops unknown bundle
# keys, so a V10 built-in recipe's focus is forwarded here.
settings = super()._recipe_settings(recipe_key)
settings["focus"] = cls.QUICK_RECIPES[recipe_key].get("focus", "")
return settings
class KGKrea2ImageGuideCardV10:
"""
+17
View File
@@ -22,6 +22,22 @@ LIGHTING_LAYER_PULL = _v9_recipes.LIGHTING_LAYER_PULL
STYLE_TRANSFER_LAYER_PULL = [0.062, 0.087, 0.113, 0.15, 0.2, 0.25, 1.25, 3.438, 6.875, 1.375, 5.5, 1.5]
MATERIAL_FINISH_LAYER_PULL = [0.05, 0.075, 0.113, 0.163, 0.213, 0.25, 1.25, 2.75, 5.5, 1.5, 4.125, 1.375]
# Render-tuned 2026-07-06: the strong-blur prep is the style recipe's
# structure-safety guarantee, but the encoder was also *studying the blur* -
# on soft-medium references (watercolor, gouache, aged photos) renders came
# out pervasively soft, with the subject small and hazy. Crisper prep
# (higher detail, larger study) re-opened source-scene takeover instead, so
# the correction rides the instruction channel: de-selecting softness and
# scene layout keeps the prompt subject crisp and primary while palette,
# medium, and brushwork still arrive. A/B render-verified across five
# references, two strengths, and three seeds, single-card and in the
# two-card style-transfer method.
STYLE_TRANSFER_FOCUS = (
"the artistic style: palette, medium, brushwork, art direction, and "
"rendering finish - not the image's blurriness or soft focus, and not "
"its subject or scene layout"
)
role_pull_defaults = _v9_recipes.role_pull_defaults
role_layer_pull_defaults = _v9_recipes.role_layer_pull_defaults
effective_image_strength = _v9_recipes.effective_image_strength
@@ -52,6 +68,7 @@ QUICK_RECIPES.update({
"shape": 0.85,
"global": 1.85,
"layers": STYLE_TRANSFER_LAYER_PULL,
"focus": STYLE_TRANSFER_FOCUS,
},
"texture gentle": {
"role": "material",
+16 -5
View File
@@ -252,9 +252,13 @@ class KreaV10LabelContractTests(unittest.TestCase):
):
self.assertIn(key, recipes)
self.assertEqual(recipes[key]["role"], role)
# Every recipe (V9 and V10) carries the full settings bundle.
# Every recipe (V9 and V10) carries the full settings bundle;
# `focus` is the only optional extra a bundle may add.
for key, recipe in recipes.items():
self.assertEqual(set(recipe.keys()), RECIPE_KEYS, "recipe {} is incomplete".format(key))
self.assertEqual(
set(recipe.keys()) - {"focus"}, RECIPE_KEYS,
"recipe {} is incomplete".format(key),
)
def test_v10_max_layer_scale_constant(self):
self.assertEqual(self.nodes.KGTextEncodeKreaImageReferencesV10.MAX_LAYER_SCALE, 6.0)
@@ -303,7 +307,11 @@ class KreaV10CardBehaviorTests(unittest.TestCase):
self.assertGreater(card["resolved_shape_pull"], 0.4)
self.assertAlmostEqual(card["resolved_global_pull"], 1.85)
self.assertEqual(card["resolved_layer_pull"], self.nodes.recipes.STYLE_TRANSFER_LAYER_PULL)
self.assertEqual(card["resolved_focus"], "")
# Anti-blur focus (2026-07-06): the strong-blur prep must not itself
# be studied as the style.
self.assertEqual(card["resolved_focus"], self.nodes.recipes.STYLE_TRANSFER_FOCUS)
self.assertIn("not the image's blurriness", card["resolved_focus"])
self.assertEqual(card["focus"], self.nodes.recipes.STYLE_TRANSFER_FOCUS)
self.assertEqual(card["resolved_direction"], "toward")
self.assertEqual(card["resolved_timing"], "recipe")
@@ -766,9 +774,12 @@ class KreaV10CustomRecipeTests(unittest.TestCase):
card = self._build("clothing borrower")
self.assertEqual(card["resolved_focus"], "the clothing and garment style, not the person")
self.assertEqual(card["focus"], "the clothing and garment style, not the person")
# Built-in recipes carry an empty focus (key present, no text).
builtin = self._build("suggest the visual style")
# Built-in recipes without a focus carry an empty one (key present,
# no text); the V10 style recipe ships its anti-blur focus.
builtin = self._build("suggest the color palette")
self.assertEqual(builtin["resolved_focus"], "")
style = self._build("suggest the visual style")
self.assertEqual(style["resolved_focus"], self.nodes.recipes.STYLE_TRANSFER_FOCUS)
def test_focus_without_text_is_omitted_from_the_bundle(self):
self._write("nofocus.json", json.dumps({"label": "plain style", "role": "style", "focus": " "}))