Spaces:
Running on Zero
Running on Zero
Reject scene-contaminated transparent assets
Browse files
README.md
CHANGED
|
@@ -67,7 +67,7 @@ If a requested asset has no deterministic hook, images are still generated for r
|
|
| 67 |
|
| 68 |
## Contract-driven generation and validation
|
| 69 |
|
| 70 |
-
Filenames and IDs do not decide whether an image is a scene or sprite. Dimensions, transparency, composition, camera, quantity, subject count, and silhouette requirements come from the manifest. Transparent single-subject outputs receive alpha extraction, occupancy normalization,
|
| 71 |
|
| 72 |
The image model can still violate these instructions, so every result requires visual review. Single-subject outputs that fail automated checks are regenerated with a fresh seed rather than cropped into a guessed subject. The review UI can regenerate one output or a complete multi-output asset set.
|
| 73 |
|
|
@@ -89,7 +89,7 @@ segmind/SSD-1B + latent-consistency/lcm-lora-ssd-1b
|
|
| 89 |
|
| 90 |
SSD-1B is an Apache-2.0 distilled SDXL model. The LCM adapter enables low-step generation. The weights run inside the Space's ZeroGPU allocation, so the app does not consume Hugging Face Inference Provider credits and does not require a paid image API.
|
| 91 |
|
| 92 |
-
Sprites and backgrounds are generated directly from their written prompts. No procedurally drawn sprite, palette, silhouette, camera guide, or background layout is passed into the production model. Sprite prompts use a portrait canvas and positive single-subject composition language; the negative prompt separately rejects sheets, lineups, repeated subjects,
|
| 93 |
|
| 94 |
SSD-1B's CLIP text encoder has a 77-token context window. Production prompts therefore use a conservative word budget, place the camera and single-subject contract first, and compact the free-form description. This prevents those critical constraints from being silently truncated behind verbose style text.
|
| 95 |
|
|
|
|
| 67 |
|
| 68 |
## Contract-driven generation and validation
|
| 69 |
|
| 70 |
+
Filenames and IDs do not decide whether an image is a scene or sprite. Dimensions, transparency, composition, camera, quantity, subject count, and silhouette requirements come from the manifest. Transparent single-subject outputs receive alpha extraction, occupancy normalization, subject-count checks, and a retained-scene check that rejects environmental foreground spread around an isolated subject. Opaque outputs are checked for unwanted transparency. Seamless requests receive an opposite-edge similarity check. Other compositions are not incorrectly rejected for containing multiple subjects.
|
| 71 |
|
| 72 |
The image model can still violate these instructions, so every result requires visual review. Single-subject outputs that fail automated checks are regenerated with a fresh seed rather than cropped into a guessed subject. The review UI can regenerate one output or a complete multi-output asset set.
|
| 73 |
|
|
|
|
| 89 |
|
| 90 |
SSD-1B is an Apache-2.0 distilled SDXL model. The LCM adapter enables low-step generation. The weights run inside the Space's ZeroGPU allocation, so the app does not consume Hugging Face Inference Provider credits and does not require a paid image API.
|
| 91 |
|
| 92 |
+
Sprites and backgrounds are generated directly from their written prompts. No procedurally drawn sprite, palette, silhouette, camera guide, or background layout is passed into the production model. Sprite prompts use a portrait canvas and positive single-subject composition language; the negative prompt separately rejects sheets, lineups, repeated subjects, multiple views, and surrounding environments. After transparency extraction, sprites with zero or multiple significant foreground components, retained scene-like alpha, implausible silhouettes, or inadequate scale are regenerated with a new seed up to `PRIMARY_SPRITE_ATTEMPTS` times. If every attempt fails, the deployed Space returns an explicit model-generation error rather than returning a contact sheet or environment-contaminated cutout. Background negative prompts reject characters and generic particle overlays.
|
| 93 |
|
| 94 |
SSD-1B's CLIP text encoder has a 77-token context window. Production prompts therefore use a conservative word budget, place the camera and single-subject contract first, and compact the free-form description. This prevents those critical constraints from being silently truncated behind verbose style text.
|
| 95 |
|
app.py
CHANGED
|
@@ -1851,7 +1851,8 @@ def diffusion_negative_prompt(spec: AssetSpec) -> str:
|
|
| 1851 |
return (
|
| 1852 |
"multiple subjects, duplicate character, duplicate subject, repeated subject, character sheet, model sheet, turnaround, "
|
| 1853 |
"lineup, alternate views, multiple poses, multiple views, pair, group, cropped subject, scenery, landscape, "
|
| 1854 |
-
"
|
|
|
|
| 1855 |
+ camera_negative
|
| 1856 |
)
|
| 1857 |
if spec.composition == "seamless":
|
|
@@ -1954,11 +1955,13 @@ def primary_diffusion_prompt(spec: AssetSpec) -> str:
|
|
| 1954 |
camera = f"User-defined camera: {compact_prompt_words(spec.camera_instruction, 10)}."
|
| 1955 |
variation = f"Distinct variation: {compact_prompt_words(spec.variation, 8)}. " if spec.variation else ""
|
| 1956 |
if spec.composition == "single_subject":
|
| 1957 |
-
description = compact_prompt_words(spec.prompt,
|
|
|
|
| 1958 |
subject = (spec.group or spec.role).replace("_", " ")
|
| 1959 |
return (
|
| 1960 |
f"{camera} Single isolated {subject} subject. One complete body, one pose, centered and fully visible. "
|
| 1961 |
-
|
|
|
|
| 1962 |
f"{variation}{description}"
|
| 1963 |
)
|
| 1964 |
description = compact_prompt_words(spec.prompt, 26 if variation else 34)
|
|
@@ -2024,6 +2027,9 @@ def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[byt
|
|
| 2024 |
else:
|
| 2025 |
last_failure_detail = "the last output did not contain one significant foreground subject"
|
| 2026 |
continue
|
|
|
|
|
|
|
|
|
|
| 2027 |
if has_implausibly_thin_foreground_subject(content, spec):
|
| 2028 |
last_failure_detail = "the last output contained an implausibly thin foreground silhouette"
|
| 2029 |
continue
|
|
@@ -2339,6 +2345,46 @@ def has_undersized_foreground_subject(content: bytes) -> bool:
|
|
| 2339 |
return max(width_ratio, height_ratio) < 0.68
|
| 2340 |
|
| 2341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2342 |
def free_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
|
| 2343 |
global FREE_DIFFUSION_PIPE, FREE_DIFFUSION_ERROR
|
| 2344 |
if FREE_DIFFUSION_ERROR:
|
|
@@ -2589,6 +2635,8 @@ def validate_asset_png(content: bytes, spec: AssetSpec) -> list[str]:
|
|
| 2589 |
)
|
| 2590 |
if any(value > 16 for value in corners):
|
| 2591 |
warnings.append("transparent output has opaque corner pixels")
|
|
|
|
|
|
|
| 2592 |
if spec.expected_subjects is not None:
|
| 2593 |
subject_count = estimated_foreground_subject_count(content)
|
| 2594 |
if subject_count != spec.expected_subjects:
|
|
@@ -2763,7 +2811,7 @@ def model_configuration_summary() -> str:
|
|
| 2763 |
"Every production image is generated directly from its written prompt by the primary text-to-image model. "
|
| 2764 |
"The user-defined composition and camera contract is placed first inside a conservative CLIP prompt budget. "
|
| 2765 |
"Transparent single-subject foregrounds are normalized to the requested canvas, then pass configured subject-count, "
|
| 2766 |
-
"silhouette, and scale validation; failures are regenerated with a new seed up to "
|
| 2767 |
f"{PRIMARY_SPRITE_ATTEMPTS} times. No procedural guide is supplied to the model. The procedural renderer is "
|
| 2768 |
"development-only and is blocked in the deployed Space when the primary model fails."
|
| 2769 |
)
|
|
|
|
| 1851 |
return (
|
| 1852 |
"multiple subjects, duplicate character, duplicate subject, repeated subject, character sheet, model sheet, turnaround, "
|
| 1853 |
"lineup, alternate views, multiple poses, multiple views, pair, group, cropped subject, scenery, landscape, "
|
| 1854 |
+
"environment, environmental framing, scenic background, surrounding landscape, ground plane, floor plane, "
|
| 1855 |
+
"background border, background props, drop shadow, text, watermark"
|
| 1856 |
+ camera_negative
|
| 1857 |
)
|
| 1858 |
if spec.composition == "seamless":
|
|
|
|
| 1955 |
camera = f"User-defined camera: {compact_prompt_words(spec.camera_instruction, 10)}."
|
| 1956 |
variation = f"Distinct variation: {compact_prompt_words(spec.variation, 8)}. " if spec.variation else ""
|
| 1957 |
if spec.composition == "single_subject":
|
| 1958 |
+
description = compact_prompt_words(spec.prompt, 6 if variation else 10)
|
| 1959 |
+
variation = f"Variation: {compact_prompt_words(spec.variation, 4)}. " if spec.variation else ""
|
| 1960 |
subject = (spec.group or spec.role).replace("_", " ")
|
| 1961 |
return (
|
| 1962 |
f"{camera} Single isolated {subject} subject. One complete body, one pose, centered and fully visible. "
|
| 1963 |
+
"Fill about eighty percent of the frame with modest even margins. "
|
| 1964 |
+
"Plain uniform white field only; no environment, scenery, ground plane, or surrounding border. "
|
| 1965 |
f"{variation}{description}"
|
| 1966 |
)
|
| 1967 |
description = compact_prompt_words(spec.prompt, 26 if variation else 34)
|
|
|
|
| 2027 |
else:
|
| 2028 |
last_failure_detail = "the last output did not contain one significant foreground subject"
|
| 2029 |
continue
|
| 2030 |
+
if has_scene_like_foreground_frame(content):
|
| 2031 |
+
last_failure_detail = "the last output retained a surrounding scene instead of an isolated subject"
|
| 2032 |
+
continue
|
| 2033 |
if has_implausibly_thin_foreground_subject(content, spec):
|
| 2034 |
last_failure_detail = "the last output contained an implausibly thin foreground silhouette"
|
| 2035 |
continue
|
|
|
|
| 2345 |
return max(width_ratio, height_ratio) < 0.68
|
| 2346 |
|
| 2347 |
|
| 2348 |
+
def has_scene_like_foreground_frame(content: bytes) -> bool:
|
| 2349 |
+
"""Detect retained scenery spread around a nominally isolated transparent subject.
|
| 2350 |
+
|
| 2351 |
+
Alpha extraction can turn a model-generated environment into one connected
|
| 2352 |
+
foreground component, so subject counting alone cannot identify it. A clean
|
| 2353 |
+
cutout may reach one or two sides of its canvas, but retained scenery fills
|
| 2354 |
+
nearly every outer region around the center. The conservative 3x3 test avoids
|
| 2355 |
+
rejecting ordinary tall, wide, or circular silhouettes.
|
| 2356 |
+
"""
|
| 2357 |
+
image = Image.open(io.BytesIO(content)).convert("RGBA")
|
| 2358 |
+
alpha = image.getchannel("A")
|
| 2359 |
+
width, height = image.size
|
| 2360 |
+
if width < 9 or height < 9:
|
| 2361 |
+
return False
|
| 2362 |
+
|
| 2363 |
+
occupied_regions = 0
|
| 2364 |
+
total_foreground = 0
|
| 2365 |
+
perimeter_regions = 0
|
| 2366 |
+
for row in range(3):
|
| 2367 |
+
y0 = round(row * height / 3)
|
| 2368 |
+
y1 = round((row + 1) * height / 3)
|
| 2369 |
+
for column in range(3):
|
| 2370 |
+
x0 = round(column * width / 3)
|
| 2371 |
+
x1 = round((column + 1) * width / 3)
|
| 2372 |
+
area = max(1, (x1 - x0) * (y1 - y0))
|
| 2373 |
+
foreground = sum(
|
| 2374 |
+
alpha.getpixel((x, y)) >= 64
|
| 2375 |
+
for y in range(y0, y1)
|
| 2376 |
+
for x in range(x0, x1)
|
| 2377 |
+
)
|
| 2378 |
+
total_foreground += foreground
|
| 2379 |
+
if row != 1 or column != 1:
|
| 2380 |
+
perimeter_regions += 1
|
| 2381 |
+
if foreground / area >= 0.28:
|
| 2382 |
+
occupied_regions += 1
|
| 2383 |
+
|
| 2384 |
+
foreground_ratio = total_foreground / max(1, width * height)
|
| 2385 |
+
return perimeter_regions == 8 and foreground_ratio >= 0.38 and occupied_regions >= 7
|
| 2386 |
+
|
| 2387 |
+
|
| 2388 |
def free_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
|
| 2389 |
global FREE_DIFFUSION_PIPE, FREE_DIFFUSION_ERROR
|
| 2390 |
if FREE_DIFFUSION_ERROR:
|
|
|
|
| 2635 |
)
|
| 2636 |
if any(value > 16 for value in corners):
|
| 2637 |
warnings.append("transparent output has opaque corner pixels")
|
| 2638 |
+
if spec.composition in {"single_subject", "icon", "animation_frame"} and has_scene_like_foreground_frame(content):
|
| 2639 |
+
warnings.append("transparent cutout retains a surrounding scene instead of an isolated subject")
|
| 2640 |
if spec.expected_subjects is not None:
|
| 2641 |
subject_count = estimated_foreground_subject_count(content)
|
| 2642 |
if subject_count != spec.expected_subjects:
|
|
|
|
| 2811 |
"Every production image is generated directly from its written prompt by the primary text-to-image model. "
|
| 2812 |
"The user-defined composition and camera contract is placed first inside a conservative CLIP prompt budget. "
|
| 2813 |
"Transparent single-subject foregrounds are normalized to the requested canvas, then pass configured subject-count, "
|
| 2814 |
+
"retained-scene, silhouette, and scale validation; failures are regenerated with a new seed up to "
|
| 2815 |
f"{PRIMARY_SPRITE_ATTEMPTS} times. No procedural guide is supplied to the model. The procedural renderer is "
|
| 2816 |
"development-only and is blocked in the deployed Space when the primary model fails."
|
| 2817 |
)
|