LeafCat79 commited on
Commit
75b10ac
·
verified ·
1 Parent(s): 0fe8502

Extract transparent assets with BiRefNet

Browse files
Files changed (3) hide show
  1. README.md +6 -3
  2. app.py +101 -12
  3. requirements.txt +5 -0
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 must first arrive from the model on a plain light studio field; scene-like model frames are rejected before alpha extraction. Passing outputs then 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
 
@@ -81,15 +81,16 @@ If the Space has an `HF_TOKEN` secret and `USE_HF_PROMPT_PROVIDER=1`, it first u
81
 
82
  ## Default neural image pipeline
83
 
84
- The deployed Space is designed for Hugging Face ZeroGPU and uses a real diffusion model for both sprites and backgrounds:
85
 
86
  ```text
87
  segmind/SSD-1B + latent-consistency/lcm-lora-ssd-1b
 
88
  ```
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. Before alpha extraction, cutout attempts whose boundary is not predominantly light and neutral are rejected because they did not honor the requested studio field. 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
 
@@ -106,6 +107,8 @@ USE_PRIMARY_IMAGE_MODEL=1
106
  REQUIRE_PRIMARY_IMAGE_MODEL=1
107
  PRIMARY_IMAGE_MODEL=segmind/SSD-1B
108
  PRIMARY_LORA_MODEL=latent-consistency/lcm-lora-ssd-1b
 
 
109
  PRIMARY_IMAGE_STEPS=4
110
  PRIMARY_SPRITE_STEPS=6
111
  PRIMARY_SPRITE_ATTEMPTS=3
 
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 neural alpha extraction from the pinned MIT-licensed [`ZhengPeng7/BiRefNet`](https://huggingface.co/ZhengPeng7/BiRefNet) model, 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
 
 
81
 
82
  ## Default neural image pipeline
83
 
84
+ The deployed Space is designed for Hugging Face ZeroGPU and uses real neural models for generation and transparent cutout extraction:
85
 
86
  ```text
87
  segmind/SSD-1B + latent-consistency/lcm-lora-ssd-1b
88
+ ZhengPeng7/BiRefNet (transparent-cutout alpha only)
89
  ```
90
 
91
  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.
92
 
93
+ 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 image generator. 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. BiRefNet predicts only the alpha mask for transparent cutouts while preserving SSD-1B's generated RGB artwork. After neural 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.
94
 
95
  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.
96
 
 
107
  REQUIRE_PRIMARY_IMAGE_MODEL=1
108
  PRIMARY_IMAGE_MODEL=segmind/SSD-1B
109
  PRIMARY_LORA_MODEL=latent-consistency/lcm-lora-ssd-1b
110
+ FOREGROUND_MODEL_ID=ZhengPeng7/BiRefNet
111
+ FOREGROUND_MODEL_REVISION=e2bf8e4460fc8fa32bba5ea4d94b3233d367b0e4
112
  PRIMARY_IMAGE_STEPS=4
113
  PRIMARY_SPRITE_STEPS=6
114
  PRIMARY_SPRITE_ATTEMPTS=3
app.py CHANGED
@@ -167,6 +167,11 @@ class IntegrationReport:
167
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
168
  PRIMARY_IMAGE_MODEL = os.environ.get("PRIMARY_IMAGE_MODEL", "segmind/SSD-1B")
169
  PRIMARY_LORA_MODEL = os.environ.get("PRIMARY_LORA_MODEL", "latent-consistency/lcm-lora-ssd-1b")
 
 
 
 
 
170
  PRIMARY_IMAGE_STEPS = int(os.environ.get("PRIMARY_IMAGE_STEPS", "4"))
171
  PRIMARY_SPRITE_STEPS = int(os.environ.get("PRIMARY_SPRITE_STEPS", "6"))
172
  PRIMARY_SPRITE_ATTEMPTS = max(1, int(os.environ.get("PRIMARY_SPRITE_ATTEMPTS", "3")))
@@ -198,6 +203,8 @@ CONTROLNET_PIPE = None
198
  CONTROLNET_ERROR = None
199
  PRIMARY_TEXT_PIPE = None
200
  PRIMARY_MODEL_ERROR = None
 
 
201
 
202
 
203
  def gpu_task(duration):
@@ -1949,6 +1956,56 @@ def initialize_primary_image_model() -> None:
1949
  PRIMARY_MODEL_ERROR = short_error(exc)
1950
 
1951
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1952
  def primary_diffusion_prompt(spec: AssetSpec) -> str:
1953
  camera = primary_camera_phrase(spec.camera, is_background_spec(spec))
1954
  if spec.camera == "auto" and spec.camera_instruction and spec.camera_instruction.lower() != "auto":
@@ -1996,10 +2053,18 @@ def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[byt
1996
  if PRIMARY_TEXT_PIPE is None:
1997
  return None, "primary ZeroGPU diffusion model is not initialized on this runtime"
1998
 
 
 
 
 
 
 
 
 
 
1999
  try:
2000
  import torch
2001
 
2002
- is_background = is_background_spec(spec)
2003
  attempts = 1 if is_background else PRIMARY_SPRITE_ATTEMPTS
2004
  width, height = primary_generation_dimensions(spec)
2005
  last_failure_detail = "the last output did not contain one valid foreground subject"
@@ -2016,12 +2081,18 @@ def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[byt
2016
  width=width,
2017
  height=height,
2018
  ).images[0]
2019
- if not is_background and has_invalid_cutout_studio_field(image):
2020
- last_failure_detail = "the last output did not use the required plain light studio field"
2021
- continue
2022
- content = polish_diffusion_asset(image, spec)
2023
  if is_background:
 
2024
  return content, None
 
 
 
 
 
 
 
 
 
2025
 
2026
  subject_count = estimated_foreground_subject_count(content)
2027
  if subject_count != 1:
@@ -2050,6 +2121,7 @@ def primary_diffusion_png(spec: AssetSpec, index: int, run_id: int) -> tuple[byt
2050
  # here keeps the weights resident between decorated requests instead of paying the
2051
  # model-load cost on every generation.
2052
  initialize_primary_image_model()
 
2053
 
2054
 
2055
  def controlnet_background_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
@@ -2148,6 +2220,20 @@ def polish_diffusion_asset(image: Image.Image, spec: AssetSpec) -> bytes:
2148
  return out.getvalue()
2149
 
2150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2151
  def has_invalid_cutout_studio_field(image: Image.Image) -> bool:
2152
  """Reject model scenes before alpha extraction when their outer field is not light and neutral.
2153
 
@@ -2535,11 +2621,13 @@ def generate_asset(spec: AssetSpec, index: int, run_id: int) -> tuple[str, str,
2535
  if USE_PRIMARY_IMAGE_MODEL:
2536
  png_content, error = primary_diffusion_png(spec, index, run_id)
2537
  source = f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL} on ZeroGPU"
 
 
2538
  if error:
2539
  errors.append(f"primary ZeroGPU diffusion: {error}")
2540
  if REQUIRE_PRIMARY_IMAGE_MODEL:
2541
  raise RuntimeError(
2542
- "Neural image generation is required, but the primary model failed. "
2543
  f"No procedural image was returned. Details: {error}"
2544
  )
2545
 
@@ -2785,7 +2873,7 @@ def model_configuration_summary() -> str:
2785
  sprite_chain = []
2786
  primary_source = f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL} on ZeroGPU"
2787
  if USE_PRIMARY_IMAGE_MODEL:
2788
- sprite_chain.append(f"direct text-to-image {primary_source}")
2789
  if USE_DIFFUSION_FOR_SPRITES:
2790
  sprite_chain.append(FREE_IMAGE_MODEL)
2791
  if USE_HF_IMAGE_PROVIDER:
@@ -2816,6 +2904,7 @@ def model_configuration_summary() -> str:
2816
  neural_models = []
2817
  if USE_PRIMARY_IMAGE_MODEL:
2818
  neural_models.append(f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL}")
 
2819
  if USE_DIFFUSION_FOR_SPRITES or USE_DIFFUSION_FOR_BACKGROUNDS:
2820
  neural_models.append(FREE_IMAGE_MODEL)
2821
  if USE_CONTROLNET_FOR_BACKGROUNDS:
@@ -2823,10 +2912,10 @@ def model_configuration_summary() -> str:
2823
  neural_models.extend(remote_sources)
2824
  neural_status = ", ".join(dict.fromkeys(neural_models)) if neural_models else "none active—procedural code only"
2825
  enforcement = "required; generation stops on model failure" if REQUIRE_PRIMARY_IMAGE_MODEL else "development fallback allowed"
2826
- if PRIMARY_MODEL_ERROR:
2827
- readiness = f"startup error: {PRIMARY_MODEL_ERROR}"
2828
- elif PRIMARY_TEXT_PIPE is not None:
2829
- readiness = "loaded on ZeroGPU"
2830
  else:
2831
  readiness = "loads in the deployed ZeroGPU runtime"
2832
  return (
@@ -2837,7 +2926,7 @@ def model_configuration_summary() -> str:
2837
  f"neural enforcement: `{enforcement}` · primary readiness: `{readiness}`. "
2838
  "Every production image is generated directly from its written prompt by the primary text-to-image model. "
2839
  "The user-defined composition and camera contract is placed first inside a conservative CLIP prompt budget. "
2840
- "Transparent single-subject attempts must first honor the light studio-field contract, are normalized to the requested canvas, then pass configured subject-count, "
2841
  "retained-scene, silhouette, and scale validation; failures are regenerated with a new seed up to "
2842
  f"{PRIMARY_SPRITE_ATTEMPTS} times. No procedural guide is supplied to the model. The procedural renderer is "
2843
  "development-only and is blocked in the deployed Space when the primary model fails."
 
167
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
168
  PRIMARY_IMAGE_MODEL = os.environ.get("PRIMARY_IMAGE_MODEL", "segmind/SSD-1B")
169
  PRIMARY_LORA_MODEL = os.environ.get("PRIMARY_LORA_MODEL", "latent-consistency/lcm-lora-ssd-1b")
170
+ FOREGROUND_MODEL_ID = os.environ.get("FOREGROUND_MODEL_ID", "ZhengPeng7/BiRefNet")
171
+ FOREGROUND_MODEL_REVISION = os.environ.get(
172
+ "FOREGROUND_MODEL_REVISION",
173
+ "e2bf8e4460fc8fa32bba5ea4d94b3233d367b0e4",
174
+ )
175
  PRIMARY_IMAGE_STEPS = int(os.environ.get("PRIMARY_IMAGE_STEPS", "4"))
176
  PRIMARY_SPRITE_STEPS = int(os.environ.get("PRIMARY_SPRITE_STEPS", "6"))
177
  PRIMARY_SPRITE_ATTEMPTS = max(1, int(os.environ.get("PRIMARY_SPRITE_ATTEMPTS", "3")))
 
203
  CONTROLNET_ERROR = None
204
  PRIMARY_TEXT_PIPE = None
205
  PRIMARY_MODEL_ERROR = None
206
+ FOREGROUND_MODEL = None
207
+ FOREGROUND_MODEL_ERROR = None
208
 
209
 
210
  def gpu_task(duration):
 
1956
  PRIMARY_MODEL_ERROR = short_error(exc)
1957
 
1958
 
1959
+ def initialize_foreground_model() -> None:
1960
+ """Load the pinned neural foreground extractor used by transparent cutouts."""
1961
+ global FOREGROUND_MODEL, FOREGROUND_MODEL_ERROR
1962
+ if FOREGROUND_MODEL is not None or FOREGROUND_MODEL_ERROR:
1963
+ return
1964
+ if not USE_PRIMARY_IMAGE_MODEL or not os.environ.get("SPACE_ID"):
1965
+ return
1966
+
1967
+ try:
1968
+ import torch
1969
+ from transformers import AutoModelForImageSegmentation
1970
+
1971
+ model = AutoModelForImageSegmentation.from_pretrained(
1972
+ FOREGROUND_MODEL_ID,
1973
+ revision=FOREGROUND_MODEL_REVISION,
1974
+ trust_remote_code=True,
1975
+ torch_dtype=torch.float16,
1976
+ )
1977
+ FOREGROUND_MODEL = model.eval().to("cuda")
1978
+ except Exception as exc:
1979
+ FOREGROUND_MODEL_ERROR = short_error(exc)
1980
+
1981
+
1982
+ def neural_foreground_cutout(image: Image.Image) -> Image.Image:
1983
+ """Use BiRefNet to derive alpha while preserving the generator's RGB artwork."""
1984
+ if FOREGROUND_MODEL is None:
1985
+ raise RuntimeError("neural foreground model is not initialized")
1986
+
1987
+ import numpy as np
1988
+ import torch
1989
+
1990
+ source = image.convert("RGB")
1991
+ sample = source.resize((1024, 1024), Image.LANCZOS)
1992
+ array = np.asarray(sample, dtype=np.float32) / 255.0
1993
+ tensor = torch.from_numpy(array).permute(2, 0, 1).unsqueeze(0)
1994
+ mean = torch.tensor((0.485, 0.456, 0.406)).view(1, 3, 1, 1)
1995
+ std = torch.tensor((0.229, 0.224, 0.225)).view(1, 3, 1, 1)
1996
+ tensor = ((tensor - mean) / std).to(device="cuda", dtype=torch.float16)
1997
+
1998
+ with torch.no_grad():
1999
+ prediction = FOREGROUND_MODEL(tensor)[-1].sigmoid()[0].squeeze().float().cpu().numpy()
2000
+ mask = Image.fromarray(np.clip(prediction * 255.0, 0, 255).astype(np.uint8)).resize(
2001
+ source.size,
2002
+ Image.LANCZOS,
2003
+ )
2004
+ cutout = source.convert("RGBA")
2005
+ cutout.putalpha(mask)
2006
+ return cutout
2007
+
2008
+
2009
  def primary_diffusion_prompt(spec: AssetSpec) -> str:
2010
  camera = primary_camera_phrase(spec.camera, is_background_spec(spec))
2011
  if spec.camera == "auto" and spec.camera_instruction and spec.camera_instruction.lower() != "auto":
 
2053
  if PRIMARY_TEXT_PIPE is None:
2054
  return None, "primary ZeroGPU diffusion model is not initialized on this runtime"
2055
 
2056
+ is_background = is_background_spec(spec)
2057
+ if not is_background:
2058
+ if FOREGROUND_MODEL is None and not FOREGROUND_MODEL_ERROR:
2059
+ initialize_foreground_model()
2060
+ if FOREGROUND_MODEL_ERROR:
2061
+ return None, f"neural foreground model failed: {FOREGROUND_MODEL_ERROR}"
2062
+ if FOREGROUND_MODEL is None and REQUIRE_PRIMARY_IMAGE_MODEL:
2063
+ return None, "neural foreground model is not initialized on this runtime"
2064
+
2065
  try:
2066
  import torch
2067
 
 
2068
  attempts = 1 if is_background else PRIMARY_SPRITE_ATTEMPTS
2069
  width, height = primary_generation_dimensions(spec)
2070
  last_failure_detail = "the last output did not contain one valid foreground subject"
 
2081
  width=width,
2082
  height=height,
2083
  ).images[0]
 
 
 
 
2084
  if is_background:
2085
+ content = polish_diffusion_asset(image, spec)
2086
  return content, None
2087
+ if FOREGROUND_MODEL is not None:
2088
+ content = polish_neural_cutout(neural_foreground_cutout(image), spec)
2089
+ else:
2090
+ # Local-development compatibility only. The public fail-closed
2091
+ # runtime requires BiRefNet and cannot enter this branch.
2092
+ if has_invalid_cutout_studio_field(image):
2093
+ last_failure_detail = "the last output did not use the required plain light studio field"
2094
+ continue
2095
+ content = polish_diffusion_asset(image, spec)
2096
 
2097
  subject_count = estimated_foreground_subject_count(content)
2098
  if subject_count != 1:
 
2121
  # here keeps the weights resident between decorated requests instead of paying the
2122
  # model-load cost on every generation.
2123
  initialize_primary_image_model()
2124
+ initialize_foreground_model()
2125
 
2126
 
2127
  def controlnet_background_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
 
2220
  return out.getvalue()
2221
 
2222
 
2223
+ def polish_neural_cutout(image: Image.Image, spec: AssetSpec) -> bytes:
2224
+ """Resize and normalize a neural alpha cutout without changing its generated RGB art."""
2225
+ contained = image.convert("RGBA")
2226
+ contained.thumbnail((spec.width, spec.height), Image.LANCZOS)
2227
+ canvas = Image.new("RGBA", (spec.width, spec.height), (0, 0, 0, 0))
2228
+ offset = ((spec.width - contained.width) // 2, (spec.height - contained.height) // 2)
2229
+ canvas.alpha_composite(contained, dest=offset)
2230
+ if spec.composition in {"single_subject", "icon", "animation_frame"}:
2231
+ canvas = normalize_sprite_foreground(canvas, spec)
2232
+ out = io.BytesIO()
2233
+ canvas.save(out, format="PNG")
2234
+ return out.getvalue()
2235
+
2236
+
2237
  def has_invalid_cutout_studio_field(image: Image.Image) -> bool:
2238
  """Reject model scenes before alpha extraction when their outer field is not light and neutral.
2239
 
 
2621
  if USE_PRIMARY_IMAGE_MODEL:
2622
  png_content, error = primary_diffusion_png(spec, index, run_id)
2623
  source = f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL} on ZeroGPU"
2624
+ if not is_background:
2625
+ source += f" + {FOREGROUND_MODEL_ID} neural alpha"
2626
  if error:
2627
  errors.append(f"primary ZeroGPU diffusion: {error}")
2628
  if REQUIRE_PRIMARY_IMAGE_MODEL:
2629
  raise RuntimeError(
2630
+ "The neural image pipeline is required, but diffusion generation or foreground extraction failed. "
2631
  f"No procedural image was returned. Details: {error}"
2632
  )
2633
 
 
2873
  sprite_chain = []
2874
  primary_source = f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL} on ZeroGPU"
2875
  if USE_PRIMARY_IMAGE_MODEL:
2876
+ sprite_chain.append(f"direct text-to-image {primary_source} → neural alpha {FOREGROUND_MODEL_ID}")
2877
  if USE_DIFFUSION_FOR_SPRITES:
2878
  sprite_chain.append(FREE_IMAGE_MODEL)
2879
  if USE_HF_IMAGE_PROVIDER:
 
2904
  neural_models = []
2905
  if USE_PRIMARY_IMAGE_MODEL:
2906
  neural_models.append(f"{PRIMARY_IMAGE_MODEL} + {PRIMARY_LORA_MODEL}")
2907
+ neural_models.append(FOREGROUND_MODEL_ID)
2908
  if USE_DIFFUSION_FOR_SPRITES or USE_DIFFUSION_FOR_BACKGROUNDS:
2909
  neural_models.append(FREE_IMAGE_MODEL)
2910
  if USE_CONTROLNET_FOR_BACKGROUNDS:
 
2912
  neural_models.extend(remote_sources)
2913
  neural_status = ", ".join(dict.fromkeys(neural_models)) if neural_models else "none active—procedural code only"
2914
  enforcement = "required; generation stops on model failure" if REQUIRE_PRIMARY_IMAGE_MODEL else "development fallback allowed"
2915
+ if PRIMARY_MODEL_ERROR or FOREGROUND_MODEL_ERROR:
2916
+ readiness = f"startup error: {PRIMARY_MODEL_ERROR or FOREGROUND_MODEL_ERROR}"
2917
+ elif PRIMARY_TEXT_PIPE is not None and FOREGROUND_MODEL is not None:
2918
+ readiness = "diffusion and neural foreground models loaded on ZeroGPU"
2919
  else:
2920
  readiness = "loads in the deployed ZeroGPU runtime"
2921
  return (
 
2926
  f"neural enforcement: `{enforcement}` · primary readiness: `{readiness}`. "
2927
  "Every production image is generated directly from its written prompt by the primary text-to-image model. "
2928
  "The user-defined composition and camera contract is placed first inside a conservative CLIP prompt budget. "
2929
+ f"Transparent single-subject attempts use `{FOREGROUND_MODEL_ID}` for neural alpha extraction, are normalized to the requested canvas, then pass configured subject-count, "
2930
  "retained-scene, silhouette, and scale validation; failures are regenerated with a new seed up to "
2931
  f"{PRIMARY_SPRITE_ATTEMPTS} times. No procedural guide is supplied to the model. The procedural renderer is "
2932
  "development-only and is blocked in the deployed Space when the primary model fails."
requirements.txt CHANGED
@@ -1,8 +1,13 @@
1
  Pillow
2
  huggingface_hub
3
  torch==2.8.0
 
4
  diffusers>=0.35.1,<0.38
5
  transformers>=4.48,<6
6
  accelerate>=1.2,<2
7
  peft>=0.14,<1
8
  safetensors>=0.4
 
 
 
 
 
1
  Pillow
2
  huggingface_hub
3
  torch==2.8.0
4
+ torchvision==0.23.0
5
  diffusers>=0.35.1,<0.38
6
  transformers>=4.48,<6
7
  accelerate>=1.2,<2
8
  peft>=0.14,<1
9
  safetensors>=0.4
10
+ numpy<3
11
+ timm>=1.0,<2
12
+ kornia>=0.8,<1
13
+ einops>=0.8,<1