LeafCat79 commited on
Commit
09186f2
路
verified 路
1 Parent(s): 20e49a4

Make validation aware of image model behavior

Browse files
Files changed (2) hide show
  1. README.md +2 -0
  2. app.py +48 -12
README.md CHANGED
@@ -31,6 +31,8 @@ If a requested role has no deterministic hook, assets are still generated for re
31
 
32
  Generated sprites receive automated dimension, alpha-channel, and corner-transparency checks. These checks do not establish semantic or artistic correctness, so every role also has a manual approval control and per-role regeneration.
33
 
 
 
34
  The app combines the game type, perspective, theme, and pasted code into explicit per-role image prompts, then generates PNG assets and embeds contract-compatible assets as base64 data URIs.
35
 
36
  If the Space has an `HF_TOKEN` secret, it first uses the text model in `HF_PROMPT_MODEL` to interpret the code, roles, and theme into image prompts. The default prompt model is `Qwen/Qwen2.5-Coder-7B-Instruct`.
 
31
 
32
  Generated sprites receive automated dimension, alpha-channel, and corner-transparency checks. These checks do not establish semantic or artistic correctness, so every role also has a manual approval control and per-role regeneration.
33
 
34
+ The UI reports the configured prompt and image pipeline and labels every role with its actual source. Procedural generators provide predictable structure and real transparency but have limited open-ended semantic fidelity. Diffusion models provide more visual variety but are more likely to miss perspective, subject, background, or edge constraints; sprite outputs therefore receive alpha post-processing and still require visual approval. Background and sprite diffusion runs use different negative prompts so valid characters and vehicles are not accidentally excluded from sprite generation.
35
+
36
  The app combines the game type, perspective, theme, and pasted code into explicit per-role image prompts, then generates PNG assets and embeds contract-compatible assets as base64 data URIs.
37
 
38
  If the Space has an `HF_TOKEN` secret, it first uses the text model in `HF_PROMPT_MODEL` to interpret the code, roles, and theme into image prompts. The default prompt model is `Qwen/Qwen2.5-Coder-7B-Instruct`.
app.py CHANGED
@@ -1046,7 +1046,15 @@ def local_asset_png(spec: AssetSpec, index: int, run_id: int) -> bytes:
1046
  content = draw_background(spec, rng)
1047
  else:
1048
  content = draw_sprite(spec, rng)
1049
- return apply_style_finish(content, spec, rng)
 
 
 
 
 
 
 
 
1050
 
1051
 
1052
  def placeholder_png_bytes(role: str, width: int, height: int) -> bytes:
@@ -1070,6 +1078,17 @@ def diffusion_dimensions(spec: AssetSpec) -> tuple[int, int]:
1070
  return 256, 256
1071
 
1072
 
 
 
 
 
 
 
 
 
 
 
 
1073
  def polish_diffusion_asset(image: Image.Image, spec: AssetSpec) -> bytes:
1074
  image = image.convert("RGBA")
1075
  if not is_background_spec(spec):
@@ -1123,15 +1142,9 @@ def free_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes
1123
  width, height = diffusion_dimensions(spec)
1124
  seed = abs(hash(f"{spec.role}|{spec.prompt}|{index}|{run_id}")) % 2147483647
1125
  generator = torch.Generator(device="cpu").manual_seed(seed)
1126
- negative_prompt = (
1127
- "texture map, material texture, seamless texture, tiled pattern, uv map, uv unwrap, "
1128
- "skin texture, 3d model texture, normal map, roughness map, diffuse map, sprite sheet, "
1129
- "atlas, multiple objects, cropped subject, close-up surface, fabric swatch, text, watermark, "
1130
- "person, character, player, hero, creature, monster, vehicle, mascot, foreground subject"
1131
- )
1132
  image = FREE_DIFFUSION_PIPE(
1133
  spec.prompt,
1134
- negative_prompt=negative_prompt,
1135
  width=width,
1136
  height=height,
1137
  num_inference_steps=FREE_IMAGE_STEPS,
@@ -1163,9 +1176,7 @@ def hf_image_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None
1163
  guidance_scale=0.0,
1164
  seed=abs(hash(f"{spec.role}|{spec.prompt}|{index}|{run_id}")) % 2147483647,
1165
  )
1166
- out = io.BytesIO()
1167
- image.save(out, format="PNG")
1168
- return image_to_png_bytes(out.getvalue(), spec.width, spec.height), None
1169
  except Exception as exc:
1170
  return None, short_error(exc)
1171
 
@@ -1348,7 +1359,31 @@ def build_prompt_preview(specs: list[AssetSpec]) -> str:
1348
 
1349
 
1350
  def build_model_report(rows: list[tuple[str, str, str]]) -> str:
1351
- return "\n".join(f"{role}: prompt={prompt_model}; image={image_model}" for role, prompt_model, image_model in rows)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1352
 
1353
 
1354
  def summarize_model_sources(rows: list[tuple[str, str, str]]) -> str:
@@ -1553,6 +1588,7 @@ with gr.Blocks(title="Image Generator for HTML Games") as demo:
1553
  "`GAME_ASSETS.<role>` hooks or exact role filenames such as `sprite_player.png`. "
1554
  "Primitive canvas geometry is preserved and never silently guessed."
1555
  )
 
1556
 
1557
  generation_state = gr.State({})
1558
 
 
1046
  content = draw_background(spec, rng)
1047
  else:
1048
  content = draw_sprite(spec, rng)
1049
+ content = apply_style_finish(content, spec, rng)
1050
+ if not is_background_spec(spec):
1051
+ return content
1052
+
1053
+ image = Image.open(io.BytesIO(content)).convert("RGBA")
1054
+ image.putalpha(Image.new("L", image.size, 255))
1055
+ out = io.BytesIO()
1056
+ image.save(out, format="PNG")
1057
+ return out.getvalue()
1058
 
1059
 
1060
  def placeholder_png_bytes(role: str, width: int, height: int) -> bytes:
 
1078
  return 256, 256
1079
 
1080
 
1081
+ def diffusion_negative_prompt(spec: AssetSpec) -> str:
1082
+ common = (
1083
+ "texture map, material texture, seamless texture, tiled pattern, uv map, uv unwrap, "
1084
+ "3d model texture, normal map, roughness map, diffuse map, sprite sheet, atlas, "
1085
+ "multiple objects, cropped subject, close-up surface, fabric swatch, text, watermark"
1086
+ )
1087
+ if is_background_spec(spec):
1088
+ return common + ", person, character, player, hero, creature, monster, vehicle, mascot, foreground subject"
1089
+ return common + ", landscape, environment scene, room, horizon, opaque background, drop shadow"
1090
+
1091
+
1092
  def polish_diffusion_asset(image: Image.Image, spec: AssetSpec) -> bytes:
1093
  image = image.convert("RGBA")
1094
  if not is_background_spec(spec):
 
1142
  width, height = diffusion_dimensions(spec)
1143
  seed = abs(hash(f"{spec.role}|{spec.prompt}|{index}|{run_id}")) % 2147483647
1144
  generator = torch.Generator(device="cpu").manual_seed(seed)
 
 
 
 
 
 
1145
  image = FREE_DIFFUSION_PIPE(
1146
  spec.prompt,
1147
+ negative_prompt=diffusion_negative_prompt(spec),
1148
  width=width,
1149
  height=height,
1150
  num_inference_steps=FREE_IMAGE_STEPS,
 
1176
  guidance_scale=0.0,
1177
  seed=abs(hash(f"{spec.role}|{spec.prompt}|{index}|{run_id}")) % 2147483647,
1178
  )
1179
+ return polish_diffusion_asset(image, spec), None
 
 
1180
  except Exception as exc:
1181
  return None, short_error(exc)
1182
 
 
1359
 
1360
 
1361
  def build_model_report(rows: list[tuple[str, str, str]]) -> str:
1362
+ return "\n".join(
1363
+ f"{role}: prompt={prompt_model}; image={image_model}; {model_source_note(image_model)}"
1364
+ for role, prompt_model, image_model in rows
1365
+ )
1366
+
1367
+
1368
+ def model_source_note(image_model: str) -> str:
1369
+ lowered = image_model.lower()
1370
+ if "shape-aware" in lowered or "scene-safe" in lowered or "procedural" in lowered:
1371
+ return "procedural/heuristic output鈥攑redictable structure, limited open-ended semantic fidelity"
1372
+ return "diffusion output鈥攈igher visual variety; verify subject, perspective, alpha edges, and prompt compliance"
1373
+
1374
+
1375
+ def model_configuration_summary() -> str:
1376
+ prompt_source = HF_PROMPT_MODEL if USE_HF_PROMPT_PROVIDER else "local prompt interpreter"
1377
+ sprite_source = FREE_IMAGE_MODEL if USE_DIFFUSION_FOR_SPRITES else "shape-aware procedural generator"
1378
+ background_source = FREE_IMAGE_MODEL if USE_DIFFUSION_FOR_BACKGROUNDS else "scene-safe procedural generator"
1379
+ remote_fallback = HF_IMAGE_MODEL if USE_HF_IMAGE_PROVIDER else "disabled"
1380
+ return (
1381
+ "**Configured model pipeline:** "
1382
+ f"prompts: `{prompt_source}` 路 sprites: `{sprite_source}` 路 backgrounds: `{background_source}` 路 "
1383
+ f"remote image fallback: `{remote_fallback}`. "
1384
+ "Procedural output is structurally predictable but semantically limited; diffusion output is more varied "
1385
+ "and therefore requires stricter visual review."
1386
+ )
1387
 
1388
 
1389
  def summarize_model_sources(rows: list[tuple[str, str, str]]) -> str:
 
1588
  "`GAME_ASSETS.<role>` hooks or exact role filenames such as `sprite_player.png`. "
1589
  "Primitive canvas geometry is preserved and never silently guessed."
1590
  )
1591
+ gr.Markdown(model_configuration_summary())
1592
 
1593
  generation_state = gr.State({})
1594