LeafCat79 commited on
Commit
2e6e9aa
·
verified ·
1 Parent(s): ad45203

Generate assets directly from text prompts

Browse files

Remove procedural image guides from the production diffusion path. Generate sprites and backgrounds directly from written prompts, keep fail-closed neural enforcement, and make realistic style parsing negation-aware.

Files changed (1) hide show
  1. app.py +44 -50
app.py CHANGED
@@ -126,8 +126,6 @@ PRIMARY_LORA_MODEL = os.environ.get("PRIMARY_LORA_MODEL", "latent-consistency/lc
126
  PRIMARY_IMAGE_STEPS = int(os.environ.get("PRIMARY_IMAGE_STEPS", "4"))
127
  PRIMARY_SPRITE_STEPS = int(os.environ.get("PRIMARY_SPRITE_STEPS", "6"))
128
  PRIMARY_GUIDANCE_SCALE = float(os.environ.get("PRIMARY_GUIDANCE_SCALE", "1.5"))
129
- PRIMARY_BACKGROUND_STRENGTH = float(os.environ.get("PRIMARY_BACKGROUND_STRENGTH", "0.72"))
130
- PRIMARY_SPRITE_STRENGTH = float(os.environ.get("PRIMARY_SPRITE_STRENGTH", "0.82"))
131
  USE_PRIMARY_IMAGE_MODEL = os.environ.get("USE_PRIMARY_IMAGE_MODEL", "1") == "1"
132
  REQUIRE_PRIMARY_IMAGE_MODEL = os.environ.get(
133
  "REQUIRE_PRIMARY_IMAGE_MODEL",
@@ -154,7 +152,6 @@ FREE_DIFFUSION_ERROR = None
154
  CONTROLNET_PIPE = None
155
  CONTROLNET_ERROR = None
156
  PRIMARY_TEXT_PIPE = None
157
- PRIMARY_IMAGE_TO_IMAGE_PIPE = None
158
  PRIMARY_MODEL_ERROR = None
159
 
160
 
@@ -225,7 +222,18 @@ def interpret_style_hint(style_hint: str) -> StylePlan:
225
  tags: list[str] = []
226
 
227
  def has(*words: str) -> bool:
228
- return any(word in text for word in words)
 
 
 
 
 
 
 
 
 
 
 
229
 
230
  if has("watercolor", "ink wash", "gouache", "paper"):
231
  tags.append("watercolor")
@@ -255,6 +263,13 @@ def interpret_style_hint(style_hint: str) -> StylePlan:
255
  texture = "visible bristle strokes and canvas-like texture"
256
  lighting = "dramatic directional light"
257
  linework = "painted edges instead of hard outlines"
 
 
 
 
 
 
 
258
  elif has("pixel", "8-bit", "16-bit", "retro"):
259
  tags.append("pixel")
260
  medium = "retro pixel-art game asset"
@@ -1374,9 +1389,7 @@ def draw_sprite(spec: AssetSpec, rng: random.Random) -> bytes:
1374
  return out.getvalue()
1375
 
1376
  if spec.camera == "side_view":
1377
- # Profile silhouette used both by the procedural fallback and as the
1378
- # neural img2img camera guide. A single visible eye and forward nose
1379
- # prevent the model from reverting to its common front-facing pose.
1380
  draw.ellipse((cx - 28 * scale, cy + 31 * scale, cx + 31 * scale, cy + 46 * scale), fill=(0, 0, 0, 55))
1381
  draw.rounded_rectangle(
1382
  (cx - 15 * scale, cy - 16 * scale, cx + 17 * scale, cy + 32 * scale),
@@ -1544,7 +1557,7 @@ def diffusion_negative_prompt(spec: AssetSpec) -> str:
1544
  )
1545
  return (
1546
  common
1547
- + ", landscape, environment scene, room, horizon, opaque background, drop shadow, "
1548
  "trees, foliage, ground, floor, scenery, background objects, frame, border"
1549
  )
1550
 
@@ -1602,8 +1615,8 @@ def build_camera_control_image(spec: AssetSpec, width: int = 1024, height: int =
1602
 
1603
 
1604
  def initialize_primary_image_model() -> None:
1605
- """Load the production diffusion pipelines once under ZeroGPU CUDA emulation."""
1606
- global PRIMARY_TEXT_PIPE, PRIMARY_IMAGE_TO_IMAGE_PIPE, PRIMARY_MODEL_ERROR
1607
  if not USE_PRIMARY_IMAGE_MODEL or PRIMARY_TEXT_PIPE is not None or PRIMARY_MODEL_ERROR:
1608
  return
1609
  if not os.environ.get("SPACE_ID"):
@@ -1611,7 +1624,7 @@ def initialize_primary_image_model() -> None:
1611
 
1612
  try:
1613
  import torch
1614
- from diffusers import AutoPipelineForImage2Image, AutoPipelineForText2Image, LCMScheduler
1615
 
1616
  model_kwargs = {
1617
  "torch_dtype": torch.float16,
@@ -1631,13 +1644,7 @@ def initialize_primary_image_model() -> None:
1631
  if hasattr(text_pipe, "enable_vae_slicing"):
1632
  text_pipe.enable_vae_slicing()
1633
 
1634
- image_pipe = AutoPipelineForImage2Image.from_pipe(text_pipe)
1635
- image_pipe.scheduler = LCMScheduler.from_config(image_pipe.scheduler.config)
1636
- if hasattr(image_pipe, "set_progress_bar_config"):
1637
- image_pipe.set_progress_bar_config(disable=True)
1638
-
1639
  PRIMARY_TEXT_PIPE = text_pipe
1640
- PRIMARY_IMAGE_TO_IMAGE_PIPE = image_pipe
1641
  except Exception as exc:
1642
  PRIMARY_MODEL_ERROR = short_error(exc)
1643
 
@@ -1645,33 +1652,21 @@ def initialize_primary_image_model() -> None:
1645
  def primary_diffusion_prompt(spec: AssetSpec) -> str:
1646
  if is_background_spec(spec):
1647
  return (
1648
- f"{spec.prompt} Production-quality 2D game background only. Preserve the supplied camera layout "
1649
- "and readable gameplay space. Use connected environmental forms and clean terrain structure. "
1650
- "No characters and no decorative overlay."
1651
  )
1652
  return (
1653
  f"{spec.prompt} One complete isolated game sprite only, centered, fully visible, readable silhouette, "
1654
- "camera angle and pose exactly matching the supplied guide, on a plain uniform white studio background. "
 
1655
  "Add recognizable costume, equipment, materials, and character details. "
1656
  "No scenery, floor, trees, foliage, frame, or environmental props."
1657
  )
1658
 
1659
 
1660
- def primary_image_guide(spec: AssetSpec, index: int, run_id: int) -> Image.Image:
1661
- """Build a clean camera-and-palette guide for the neural image-to-image pass."""
1662
- guide_png = local_asset_png(spec, index, run_id)
1663
- guide = Image.open(io.BytesIO(guide_png)).convert("RGBA")
1664
- if is_background_spec(spec):
1665
- return guide.convert("RGB").resize((1344, 768), Image.LANCZOS)
1666
-
1667
- guide = guide.resize((1024, 1024), Image.LANCZOS)
1668
- white = Image.new("RGB", guide.size, (255, 255, 255))
1669
- white.paste(guide.convert("RGB"), mask=guide.getchannel("A"))
1670
- return white
1671
-
1672
-
1673
  def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
1674
- """Generate every production asset with SSD-1B; backgrounds use camera-guided img2img."""
1675
  if PRIMARY_MODEL_ERROR:
1676
  return None, PRIMARY_MODEL_ERROR
1677
  if PRIMARY_TEXT_PIPE is None:
@@ -1691,13 +1686,10 @@ def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[byt
1691
  "guidance_scale": PRIMARY_GUIDANCE_SCALE,
1692
  "generator": generator,
1693
  }
1694
- if PRIMARY_IMAGE_TO_IMAGE_PIPE is None:
1695
- return None, "primary camera-guided image-to-image pipeline is unavailable"
1696
- guide = primary_image_guide(spec, index, run_id)
1697
- strength = PRIMARY_BACKGROUND_STRENGTH if is_background_spec(spec) else PRIMARY_SPRITE_STRENGTH
1698
- image = PRIMARY_IMAGE_TO_IMAGE_PIPE(
1699
- image=guide,
1700
- strength=strength,
1701
  **common,
1702
  ).images[0]
1703
  return polish_diffusion_asset(image, spec), None
@@ -2115,7 +2107,7 @@ def model_source_note(image_model: str) -> str:
2115
  if "via fal-ai" in lowered or "via replicate" in lowered:
2116
  return "routed image-to-image refinement—preserves a layout guide but consumes account inference credit"
2117
  if "zerogpu" in lowered:
2118
- return "locally hosted neural diffusion—no per-image API credit; assets are camera-guided and still require visual review"
2119
  return "diffusion output—higher visual variety; verify subject, perspective, alpha edges, and prompt compliance"
2120
 
2121
 
@@ -2124,16 +2116,17 @@ def model_configuration_summary() -> str:
2124
  sprite_chain = []
2125
  primary_source = f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL} on ZeroGPU"
2126
  if USE_PRIMARY_IMAGE_MODEL:
2127
- sprite_chain.append(f"camera-guided {primary_source}")
2128
  if USE_DIFFUSION_FOR_SPRITES:
2129
  sprite_chain.append(FREE_IMAGE_MODEL)
2130
  if USE_HF_IMAGE_PROVIDER:
2131
  sprite_chain.append(HF_IMAGE_MODEL)
2132
- sprite_chain.append("shape-aware procedural renderer (no neural model)")
 
2133
  sprite_source = " → ".join(sprite_chain)
2134
  background_chain = []
2135
  if USE_PRIMARY_IMAGE_MODEL:
2136
- background_chain.append(f"camera-guided {primary_source}")
2137
  if USE_CONTROLNET_FOR_BACKGROUNDS:
2138
  background_chain.append(f"{CONTROLNET_BASE_MODEL} + {CONTROLNET_MODEL}")
2139
  if USE_HF_STRUCTURE_PROVIDER:
@@ -2142,7 +2135,8 @@ def model_configuration_summary() -> str:
2142
  background_chain.append(FREE_IMAGE_MODEL)
2143
  if USE_HF_IMAGE_PROVIDER:
2144
  background_chain.append(HF_IMAGE_MODEL)
2145
- background_chain.append("camera-safe procedural renderer (no neural model)")
 
2146
  background_source = " → ".join(background_chain)
2147
  remote_sources = []
2148
  if USE_HF_STRUCTURE_PROVIDER:
@@ -2171,9 +2165,9 @@ def model_configuration_summary() -> str:
2171
  f"prompts: `{prompt_source}` · sprites: `{sprite_source}` · backgrounds: `{background_source}` · "
2172
  f"remote image fallback: `{remote_fallback}` · neural image models: `{neural_status}` · "
2173
  f"neural enforcement: `{enforcement}` · primary readiness: `{readiness}`. "
2174
- "Every production image is generated by the primary neural model. Sprites and backgrounds start from deterministic "
2175
- "camera-and-palette guides and are refined with image-to-image diffusion; the procedural renderer remains an emergency "
2176
- "development fallback and is blocked in the deployed Space when the primary model fails."
2177
  )
2178
 
2179
 
 
126
  PRIMARY_IMAGE_STEPS = int(os.environ.get("PRIMARY_IMAGE_STEPS", "4"))
127
  PRIMARY_SPRITE_STEPS = int(os.environ.get("PRIMARY_SPRITE_STEPS", "6"))
128
  PRIMARY_GUIDANCE_SCALE = float(os.environ.get("PRIMARY_GUIDANCE_SCALE", "1.5"))
 
 
129
  USE_PRIMARY_IMAGE_MODEL = os.environ.get("USE_PRIMARY_IMAGE_MODEL", "1") == "1"
130
  REQUIRE_PRIMARY_IMAGE_MODEL = os.environ.get(
131
  "REQUIRE_PRIMARY_IMAGE_MODEL",
 
152
  CONTROLNET_PIPE = None
153
  CONTROLNET_ERROR = None
154
  PRIMARY_TEXT_PIPE = None
 
155
  PRIMARY_MODEL_ERROR = None
156
 
157
 
 
222
  tags: list[str] = []
223
 
224
  def has(*words: str) -> bool:
225
+ negation = re.compile(
226
+ r"(?:\bno\b|\bnot\b|\bwithout\b|\bavoid(?:ing)?\b|"
227
+ r"\bexclude(?:d|ing)?\b|\brather\s+than\b|\bnon[- ]?)\s+"
228
+ r"(?:[\w-]+\s+){0,3}$"
229
+ )
230
+ for word in words:
231
+ pattern = re.compile(rf"(?<![\w-]){re.escape(word)}(?![\w-])")
232
+ for match in pattern.finditer(text):
233
+ prefix = text[max(0, match.start() - 48) : match.start()]
234
+ if not negation.search(prefix):
235
+ return True
236
+ return False
237
 
238
  if has("watercolor", "ink wash", "gouache", "paper"):
239
  tags.append("watercolor")
 
263
  texture = "visible bristle strokes and canvas-like texture"
264
  lighting = "dramatic directional light"
265
  linework = "painted edges instead of hard outlines"
266
+ elif has("photorealistic", "photo-realistic", "realistic", "cinematic realism", "lifelike"):
267
+ tags.append("realistic")
268
+ medium = "realistic game concept art"
269
+ palette = "natural material colors with controlled cinematic contrast"
270
+ texture = "physically plausible fabric, skin, metal, and surface detail"
271
+ lighting = "cinematic directional light with realistic shadows"
272
+ linework = "natural painted edges without graphic outlines"
273
  elif has("pixel", "8-bit", "16-bit", "retro"):
274
  tags.append("pixel")
275
  medium = "retro pixel-art game asset"
 
1389
  return out.getvalue()
1390
 
1391
  if spec.camera == "side_view":
1392
+ # Profile silhouette for the optional local procedural fallback.
 
 
1393
  draw.ellipse((cx - 28 * scale, cy + 31 * scale, cx + 31 * scale, cy + 46 * scale), fill=(0, 0, 0, 55))
1394
  draw.rounded_rectangle(
1395
  (cx - 15 * scale, cy - 16 * scale, cx + 17 * scale, cy + 32 * scale),
 
1557
  )
1558
  return (
1559
  common
1560
+ + ", landscape, environment scene, room, horizon, complex background, drop shadow, "
1561
  "trees, foliage, ground, floor, scenery, background objects, frame, border"
1562
  )
1563
 
 
1615
 
1616
 
1617
  def initialize_primary_image_model() -> None:
1618
+ """Load the production text-to-image pipeline once under ZeroGPU CUDA emulation."""
1619
+ global PRIMARY_TEXT_PIPE, PRIMARY_MODEL_ERROR
1620
  if not USE_PRIMARY_IMAGE_MODEL or PRIMARY_TEXT_PIPE is not None or PRIMARY_MODEL_ERROR:
1621
  return
1622
  if not os.environ.get("SPACE_ID"):
 
1624
 
1625
  try:
1626
  import torch
1627
+ from diffusers import AutoPipelineForText2Image, LCMScheduler
1628
 
1629
  model_kwargs = {
1630
  "torch_dtype": torch.float16,
 
1644
  if hasattr(text_pipe, "enable_vae_slicing"):
1645
  text_pipe.enable_vae_slicing()
1646
 
 
 
 
 
 
1647
  PRIMARY_TEXT_PIPE = text_pipe
 
1648
  except Exception as exc:
1649
  PRIMARY_MODEL_ERROR = short_error(exc)
1650
 
 
1652
  def primary_diffusion_prompt(spec: AssetSpec) -> str:
1653
  if is_background_spec(spec):
1654
  return (
1655
+ f"{spec.prompt} Generate this scene directly from the written description as a production-quality "
1656
+ "2D game background. Follow the stated camera projection exactly and keep gameplay space readable. "
1657
+ "Use connected environmental forms and clean terrain structure. No characters and no decorative overlay."
1658
  )
1659
  return (
1660
  f"{spec.prompt} One complete isolated game sprite only, centered, fully visible, readable silhouette, "
1661
+ "with its anatomy, body shape, camera angle, and pose derived directly from the written description. "
1662
+ "Use a plain uniform white studio background for clean extraction. "
1663
  "Add recognizable costume, equipment, materials, and character details. "
1664
  "No scenery, floor, trees, foliage, frame, or environmental props."
1665
  )
1666
 
1667
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1668
  def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
1669
+ """Generate every production asset directly from text with the primary model."""
1670
  if PRIMARY_MODEL_ERROR:
1671
  return None, PRIMARY_MODEL_ERROR
1672
  if PRIMARY_TEXT_PIPE is None:
 
1686
  "guidance_scale": PRIMARY_GUIDANCE_SCALE,
1687
  "generator": generator,
1688
  }
1689
+ width, height = (1344, 768) if is_background_spec(spec) else (1024, 1024)
1690
+ image = PRIMARY_TEXT_PIPE(
1691
+ width=width,
1692
+ height=height,
 
 
 
1693
  **common,
1694
  ).images[0]
1695
  return polish_diffusion_asset(image, spec), None
 
2107
  if "via fal-ai" in lowered or "via replicate" in lowered:
2108
  return "routed image-to-image refinement—preserves a layout guide but consumes account inference credit"
2109
  if "zerogpu" in lowered:
2110
+ return "direct locally hosted text-to-image diffusion—no procedural guide or per-image API credit; visual review required"
2111
  return "diffusion output—higher visual variety; verify subject, perspective, alpha edges, and prompt compliance"
2112
 
2113
 
 
2116
  sprite_chain = []
2117
  primary_source = f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL} on ZeroGPU"
2118
  if USE_PRIMARY_IMAGE_MODEL:
2119
+ sprite_chain.append(f"direct text-to-image {primary_source}")
2120
  if USE_DIFFUSION_FOR_SPRITES:
2121
  sprite_chain.append(FREE_IMAGE_MODEL)
2122
  if USE_HF_IMAGE_PROVIDER:
2123
  sprite_chain.append(HF_IMAGE_MODEL)
2124
+ if not (USE_PRIMARY_IMAGE_MODEL and REQUIRE_PRIMARY_IMAGE_MODEL):
2125
+ sprite_chain.append("shape-aware procedural renderer (development fallback only)")
2126
  sprite_source = " → ".join(sprite_chain)
2127
  background_chain = []
2128
  if USE_PRIMARY_IMAGE_MODEL:
2129
+ background_chain.append(f"direct text-to-image {primary_source}")
2130
  if USE_CONTROLNET_FOR_BACKGROUNDS:
2131
  background_chain.append(f"{CONTROLNET_BASE_MODEL} + {CONTROLNET_MODEL}")
2132
  if USE_HF_STRUCTURE_PROVIDER:
 
2135
  background_chain.append(FREE_IMAGE_MODEL)
2136
  if USE_HF_IMAGE_PROVIDER:
2137
  background_chain.append(HF_IMAGE_MODEL)
2138
+ if not (USE_PRIMARY_IMAGE_MODEL and REQUIRE_PRIMARY_IMAGE_MODEL):
2139
+ background_chain.append("camera-safe procedural renderer (development fallback only)")
2140
  background_source = " → ".join(background_chain)
2141
  remote_sources = []
2142
  if USE_HF_STRUCTURE_PROVIDER:
 
2165
  f"prompts: `{prompt_source}` · sprites: `{sprite_source}` · backgrounds: `{background_source}` · "
2166
  f"remote image fallback: `{remote_fallback}` · neural image models: `{neural_status}` · "
2167
  f"neural enforcement: `{enforcement}` · primary readiness: `{readiness}`. "
2168
+ "Every production image is generated directly from its written prompt by the primary text-to-image model. "
2169
+ "No procedural guide is supplied to the model. The procedural renderer is development-only and is blocked in "
2170
+ "the deployed Space when the primary model fails."
2171
  )
2172
 
2173