diff --git a/custom_recipes/README.md b/custom_recipes/README.md index 0322767..f4a3fd2 100644 --- a/custom_recipes/README.md +++ b/custom_recipes/README.md @@ -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 diff --git a/docs/krea-v10-technical-paper.md b/docs/krea-v10-technical-paper.md index 69979e1..e5fb051 100644 --- a/docs/krea-v10-technical-paper.md +++ b/docs/krea-v10-technical-paper.md @@ -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). diff --git a/docs/krea-v10-user-guide.md b/docs/krea-v10-user-guide.md index d06f4e3..2589762 100644 --- a/docs/krea-v10-user-guide.md +++ b/docs/krea-v10-user-guide.md @@ -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. | diff --git a/kg_krea_v10/guide_card.py b/kg_krea_v10/guide_card.py index 902fcfe..035a024 100644 --- a/kg_krea_v10/guide_card.py +++ b/kg_krea_v10/guide_card.py @@ -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: """ diff --git a/kg_krea_v10/recipes.py b/kg_krea_v10/recipes.py index d555319..75f0688 100644 --- a/kg_krea_v10/recipes.py +++ b/kg_krea_v10/recipes.py @@ -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", diff --git a/tests/test_krea_v10.py b/tests/test_krea_v10.py index 1e1b002..7f9a44d 100644 --- a/tests/test_krea_v10.py +++ b/tests/test_krea_v10.py @@ -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": " "}))