anvilinteractiv commited on
Commit
803c1a2
·
verified ·
1 Parent(s): 23aa6df

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -33
app.py CHANGED
@@ -8,11 +8,14 @@ import trimesh
8
  import random
9
  from transformers import AutoModelForImageSegmentation
10
  from torchvision import transforms
11
- from huggingface_hub import hf_hub_download, snapshot_download
12
  import subprocess
13
  import shutil
14
  import base64
15
  import logging
 
 
 
16
 
17
  # Set up logging
18
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -25,12 +28,15 @@ except Exception as e:
25
  logger.error(f"Failed to install spandrel: {str(e)}")
26
  raise
27
 
 
 
 
28
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
29
  DTYPE = torch.float16
30
 
31
- logger.info(f"Using device: {DEVICE}")
32
 
33
- DEFAULT_FACE_NUMBER = 100000
34
  MAX_SEED = np.iinfo(np.int32).max
35
  TRIPOSG_REPO_URL = "https://github.com/VAST-AI-Research/TripoSG.git"
36
  MV_ADAPTER_REPO_URL = "https://github.com/huanngzh/MV-Adapter.git"
@@ -93,7 +99,7 @@ try:
93
  ).to(DEVICE)
94
  transform_image = transforms.Compose(
95
  [
96
- transforms.Resize((1024, 1024)),
97
  transforms.ToTensor(),
98
  transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
99
  ]
@@ -117,10 +123,57 @@ def get_random_hex():
117
  random_hex = random_bytes.hex()
118
  return random_hex
119
 
120
- @spaces.GPU(duration=3)
121
- def run_full(image: str, seed: int = 0, num_inference_steps: int = 50, guidance_scale: float = 7.5, simplify: bool = True, target_face_num: int = DEFAULT_FACE_NUMBER, req=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  try:
 
 
 
123
  image_seg = prepare_image(image, bg_color=np.array([1.0, 1.0, 1.0]), rmbg_net=rmbg_net)
 
 
124
 
125
  outputs = triposg_pipe(
126
  image=image_seg,
@@ -141,10 +194,11 @@ def run_full(image: str, seed: int = 0, num_inference_steps: int = 50, guidance_
141
  mesh_path = os.path.join(save_dir, f"polygenixai_{get_random_hex()}.glb")
142
  mesh.export(mesh_path)
143
  logger.info(f"Saved mesh to {mesh_path}")
 
144
 
145
  torch.cuda.empty_cache()
146
 
147
- height, width = 768, 768
148
  cameras = get_orthogonal_camera(
149
  elevation_deg=[0, 0, 0, 0, 89.99, -89.99],
150
  distance=[1.8] * NUM_VIEWS,
@@ -187,11 +241,12 @@ def run_full(image: str, seed: int = 0, num_inference_steps: int = 50, guidance_
187
  if seed != -1 and isinstance(seed, int):
188
  pipe_kwargs["generator"] = torch.Generator(device=DEVICE).manual_seed(seed)
189
 
 
190
  images = mv_adapter_pipe(
191
- "high quality",
192
  height=height,
193
  width=width,
194
- num_inference_steps=15,
195
  guidance_scale=3.0,
196
  num_images_per_prompt=NUM_VIEWS,
197
  control_image=control_images,
@@ -204,6 +259,7 @@ def run_full(image: str, seed: int = 0, num_inference_steps: int = 50, guidance_
204
  ).images
205
 
206
  torch.cuda.empty_cache()
 
207
  os.makedirs(save_dir, exist_ok=True)
208
  mv_image_path = os.path.join(save_dir, f"mv_adapter_{get_random_hex()}.png")
209
  make_image_grid(images, rows=1).save(mv_image_path)
@@ -220,18 +276,19 @@ def run_full(image: str, seed: int = 0, num_inference_steps: int = 50, guidance_
220
  save_dir=save_dir,
221
  save_name=f"polygenixai_texture_mesh_{get_random_hex()}.glb",
222
  uv_unwarp=True,
223
- uv_size=4096,
224
  rgb_path=mv_image_path,
225
  rgb_process_config=ModProcessConfig(view_upscale=True, inpaint_mode="view"),
226
  camera_azimuth_deg=[x - 90 for x in [0, 90, 180, 270, 180, 180]],
227
  )
228
 
 
229
  return image_seg, mesh_path, textured_glb_path
230
  except Exception as e:
231
  logger.error(f"Error in run_full: {str(e)}")
232
  raise
233
 
234
- def gradio_generate(image: str, seed: int = 0, num_inference_steps: int = 50, guidance_scale: float = 7.5, simplify: bool = True, target_face_num: int = DEFAULT_FACE_NUMBER):
235
  try:
236
  logger.info("Starting gradio_generate")
237
  # Verify API key
@@ -255,7 +312,7 @@ def gradio_generate(image: str, seed: int = 0, num_inference_steps: int = 50, gu
255
  logger.error(f"Image file not found: {temp_image_path}")
256
  raise ValueError("Invalid or missing image file")
257
 
258
- image_seg, mesh_path, textured_glb_path = run_full(temp_image_path, seed, num_inference_steps, guidance_scale, simplify, target_face_num, req=None)
259
  session_hash = os.path.basename(os.path.dirname(textured_glb_path))
260
  logger.info(f"Generated model at /files/{session_hash}/{os.path.basename(textured_glb_path)}")
261
  return {"file_url": f"/files/{session_hash}/{os.path.basename(textured_glb_path)}"}
@@ -291,7 +348,6 @@ def get_random_seed(randomize_seed, seed):
291
  logger.error(f"Error in get_random_seed: {str(e)}")
292
  raise
293
 
294
-
295
  def download_image(url: str, save_path: str) -> str:
296
  """Download an image from a URL and save it locally."""
297
  try:
@@ -307,7 +363,7 @@ def download_image(url: str, save_path: str) -> str:
307
  logger.error(f"Failed to download image from {url}: {str(e)}")
308
  raise
309
 
310
- @spaces.GPU()
311
  @torch.no_grad()
312
  def run_segmentation(image):
313
  try:
@@ -332,15 +388,17 @@ def run_segmentation(image):
332
 
333
  image = prepare_image(image_path, bg_color=np.array([1.0, 1.0, 1.0]), rmbg_net=rmbg_net)
334
  logger.info("Segmentation complete")
 
335
  return image
336
  except Exception as e:
337
  logger.error(f"Error in run_segmentation: {str(e)}")
338
  raise
339
 
340
- @spaces.GPU(duration=3)
 
341
  @torch.no_grad()
342
  def image_to_3d(
343
- image, # Changed to accept FileData dict or PIL Image
344
  seed: int,
345
  num_inference_steps: int,
346
  guidance_scale: float,
@@ -350,6 +408,8 @@ def image_to_3d(
350
  ):
351
  try:
352
  logger.info("Running image_to_3d")
 
 
353
  # Handle FileData dict from gradio_client
354
  if isinstance(image, dict):
355
  image_path = image.get("path") or image.get("url")
@@ -384,6 +444,7 @@ def image_to_3d(
384
  mesh_path = os.path.join(save_dir, f"polygenixai_{get_random_hex()}.glb")
385
  mesh.export(mesh_path)
386
  logger.info(f"Saved mesh to {mesh_path}")
 
387
 
388
  torch.cuda.empty_cache()
389
  return mesh_path
@@ -391,12 +452,15 @@ def image_to_3d(
391
  logger.error(f"Error in image_to_3d: {str(e)}")
392
  raise
393
 
394
- @spaces.GPU(duration=3)
 
395
  @torch.no_grad()
396
- def run_texture(image: Image, mesh_path: str, seed: int, req: gr.Request):
397
  try:
398
- logger.info("Running texture generation")
399
- height, width = 768, 768
 
 
400
  cameras = get_orthogonal_camera(
401
  elevation_deg=[0, 0, 0, 0, 89.99, -89.99],
402
  distance=[1.8] * NUM_VIEWS,
@@ -431,7 +495,16 @@ def run_texture(image: Image, mesh_path: str, seed: int, req: gr.Request):
431
  .to(DEVICE)
432
  )
433
 
434
- image = Image.open(image)
 
 
 
 
 
 
 
 
 
435
  image = remove_bg_fn(image)
436
  image = preprocess_image(image, height, width)
437
 
@@ -439,11 +512,12 @@ def run_texture(image: Image, mesh_path: str, seed: int, req: gr.Request):
439
  if seed != -1 and isinstance(seed, int):
440
  pipe_kwargs["generator"] = torch.Generator(device=DEVICE).manual_seed(seed)
441
 
 
442
  images = mv_adapter_pipe(
443
- "high quality",
444
  height=height,
445
  width=width,
446
- num_inference_steps=15,
447
  guidance_scale=3.0,
448
  num_images_per_prompt=NUM_VIEWS,
449
  control_image=control_images,
@@ -456,6 +530,7 @@ def run_texture(image: Image, mesh_path: str, seed: int, req: gr.Request):
456
  ).images
457
 
458
  torch.cuda.empty_cache()
 
459
  save_dir = os.path.join(TMP_DIR, str(req.session_hash))
460
  os.makedirs(save_dir, exist_ok=True)
461
  mv_image_path = os.path.join(save_dir, f"mv_adapter_{get_random_hex()}.png")
@@ -473,7 +548,7 @@ def run_texture(image: Image, mesh_path: str, seed: int, req: gr.Request):
473
  save_dir=save_dir,
474
  save_name=f"polygenixai_texture_mesh_{get_random_hex()}.glb",
475
  uv_unwarp=True,
476
- uv_size=4096,
477
  rgb_path=mv_image_path,
478
  rgb_process_config=ModProcessConfig(view_upscale=True, inpaint_mode="view"),
479
  camera_azimuth_deg=[x - 90 for x in [0, 90, 180, 270, 180, 180]],
@@ -485,11 +560,14 @@ def run_texture(image: Image, mesh_path: str, seed: int, req: gr.Request):
485
  logger.error(f"Error in run_texture: {str(e)}")
486
  raise
487
 
488
- @spaces.GPU(duration=3)
 
489
  @torch.no_grad()
490
- def run_full_api(image, seed: int = 0, num_inference_steps: int = 50, guidance_scale: float = 7.5, simplify: bool = True, target_face_num: int = DEFAULT_FACE_NUMBER, req: gr.Request = None):
491
  try:
492
  logger.info("Running run_full_api")
 
 
493
  # Handle FileData dict or URL
494
  if isinstance(image, dict):
495
  image_path = image.get("path") or image.get("url")
@@ -508,10 +586,10 @@ def run_full_api(image, seed: int = 0, num_inference_steps: int = 50, guidance_s
508
  logger.error(f"Invalid image path: {image_path}")
509
  raise ValueError(f"Invalid image path: {image_path}")
510
 
511
- image_seg, mesh_path, textured_glb_path = run_full(image_path, seed, num_inference_steps, guidance_scale, simplify, target_face_num, req)
512
  session_hash = os.path.basename(os.path.dirname(textured_glb_path))
513
  logger.info(f"Generated textured model at /files/{session_hash}/{os.path.basename(textured_glb_path)}")
514
- return {"file_url": f"/files/{session_hash}/{os.path.basename(textured_glb_path)}"}
515
  except Exception as e:
516
  logger.error(f"Error in run_full_api: {str(e)}")
517
  raise
@@ -524,10 +602,15 @@ try:
524
  inputs=[
525
  gr.Image(type="filepath", label="Image"),
526
  gr.Number(label="Seed", value=0, precision=0),
527
- gr.Number(label="Inference Steps", value=50, precision=0),
528
  gr.Number(label="Guidance Scale", value=7.5),
529
  gr.Checkbox(label="Simplify Mesh", value=True),
530
- gr.Number(label="Target Face Number", value=DEFAULT_FACE_NUMBER, precision=0)
 
 
 
 
 
531
  ],
532
  outputs="json",
533
  api_name="/api/generate"
@@ -653,7 +736,7 @@ try:
653
  minimum=8,
654
  maximum=50,
655
  step=1,
656
- value=50,
657
  info="Higher steps enhance detail but increase processing time",
658
  elem_classes="gr-slider"
659
  )
@@ -689,7 +772,7 @@ try:
689
  for image in os.listdir(f"{TRIPOSG_CODE_DIR}/assets/example_data")
690
  ],
691
  fn=run_full,
692
- inputs=[image_prompts],
693
  outputs=[seg_image, model_output, textured_model_output],
694
  cache_examples=True,
695
  )
@@ -716,7 +799,7 @@ try:
716
  ).then(lambda: gr.Button(interactive=True), outputs=[gen_texture_button])
717
  gen_texture_button.click(
718
  run_texture,
719
- inputs=[image_prompts, model_output, seed],
720
  outputs=[textured_model_output]
721
  )
722
  demo.load(start_session)
 
8
  import random
9
  from transformers import AutoModelForImageSegmentation
10
  from torchvision import transforms
11
+ from huggingface_hub import hf_hub_download, snapshot_download, HfApi
12
  import subprocess
13
  import shutil
14
  import base64
15
  import logging
16
+ import requests
17
+ from functools import wraps
18
+ import time
19
 
20
  # Set up logging
21
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
28
  logger.error(f"Failed to install spandrel: {str(e)}")
29
  raise
30
 
31
+ # Check if running in ZeroGPU environment
32
+ IS_ZEROGPU = os.getenv("HF_ZERO_SPACE", "0") == "1"
33
+
34
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
35
  DTYPE = torch.float16
36
 
37
+ logger.info(f"Using device: {DEVICE}, ZeroGPU: {IS_ZEROGPU}")
38
 
39
+ DEFAULT_FACE_NUMBER = 50000 # Reduced for L4 and ZeroGPU
40
  MAX_SEED = np.iinfo(np.int32).max
41
  TRIPOSG_REPO_URL = "https://github.com/VAST-AI-Research/TripoSG.git"
42
  MV_ADAPTER_REPO_URL = "https://github.com/huanngzh/MV-Adapter.git"
 
99
  ).to(DEVICE)
100
  transform_image = transforms.Compose(
101
  [
102
+ transforms.Resize((512, 512)), # Reduced for L4 and ZeroGPU
103
  transforms.ToTensor(),
104
  transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
105
  ]
 
123
  random_hex = random_bytes.hex()
124
  return random_hex
125
 
126
+ # Retry decorator for GPU tasks
127
+ def retry_on_gpu_abort(max_attempts=3, delay=5):
128
+ def decorator(func):
129
+ @wraps(func)
130
+ def wrapper(*args, **kwargs):
131
+ attempts = 0
132
+ while attempts < max_attempts:
133
+ try:
134
+ return func(*args, **kwargs)
135
+ except gr.Error as e:
136
+ if "GPU task aborted" in str(e):
137
+ attempts += 1
138
+ logger.warning(f"GPU task aborted, retrying {attempts}/{max_attempts}")
139
+ time.sleep(delay)
140
+ else:
141
+ raise
142
+ raise gr.Error("Max retries reached for GPU task")
143
+ return wrapper
144
+ return decorator
145
+
146
+ # Quota check for ZeroGPU
147
+ def check_quota():
148
+ if not IS_ZEROGPU:
149
+ return True
150
+ hf_api = HfApi()
151
+ try:
152
+ quota = hf_api.get_space_runtime(token=os.getenv("HF_TOKEN"))
153
+ logger.info(f"Remaining ZeroGPU quota: {quota}")
154
+ return quota.get("gpu_quota_remaining", 0) > 60
155
+ except Exception as e:
156
+ logger.error(f"Failed to check quota: {str(e)}")
157
+ return False
158
+
159
+ # Conditional GPU decorator
160
+ def conditional_gpu_decorator(duration=None):
161
+ def decorator(func):
162
+ if IS_ZEROGPU:
163
+ return spaces.GPU(duration=duration)(func) if duration else spaces.GPU()(func)
164
+ return func
165
+ return decorator
166
+
167
+ @conditional_gpu_decorator(duration=10)
168
+ @retry_on_gpu_abort(max_attempts=3, delay=5)
169
+ def run_full(image: str, seed: int = 0, num_inference_steps: int = 30, guidance_scale: float = 7.5, simplify: bool = True, target_face_num: int = DEFAULT_FACE_NUMBER, req=None, style_filter: str = "None"):
170
  try:
171
+ logger.info(f"Starting run_full with image: {image}, seed: {seed}, style: {style_filter}")
172
+ if not check_quota():
173
+ raise gr.Error("Insufficient GPU quota remaining")
174
  image_seg = prepare_image(image, bg_color=np.array([1.0, 1.0, 1.0]), rmbg_net=rmbg_net)
175
+ logger.info("Image segmentation completed")
176
+ logger.info(f"VRAM usage after segmentation: {torch.cuda.memory_allocated(DEVICE)/1e9:.2f} GB")
177
 
178
  outputs = triposg_pipe(
179
  image=image_seg,
 
194
  mesh_path = os.path.join(save_dir, f"polygenixai_{get_random_hex()}.glb")
195
  mesh.export(mesh_path)
196
  logger.info(f"Saved mesh to {mesh_path}")
197
+ logger.info(f"VRAM usage after mesh generation: {torch.cuda.memory_allocated(DEVICE)/1e9:.2f} GB")
198
 
199
  torch.cuda.empty_cache()
200
 
201
+ height, width = 512, 512 # Reduced for L4 and ZeroGPU
202
  cameras = get_orthogonal_camera(
203
  elevation_deg=[0, 0, 0, 0, 89.99, -89.99],
204
  distance=[1.8] * NUM_VIEWS,
 
241
  if seed != -1 and isinstance(seed, int):
242
  pipe_kwargs["generator"] = torch.Generator(device=DEVICE).manual_seed(seed)
243
 
244
+ prompt = f"high quality, {style_filter.lower()}" if style_filter != "None" else "high quality"
245
  images = mv_adapter_pipe(
246
+ prompt,
247
  height=height,
248
  width=width,
249
+ num_inference_steps=10, # Reduced for L4 and ZeroGPU
250
  guidance_scale=3.0,
251
  num_images_per_prompt=NUM_VIEWS,
252
  control_image=control_images,
 
259
  ).images
260
 
261
  torch.cuda.empty_cache()
262
+ logger.info(f"VRAM usage after texture generation: {torch.cuda.memory_allocated(DEVICE)/1e9:.2f} GB")
263
  os.makedirs(save_dir, exist_ok=True)
264
  mv_image_path = os.path.join(save_dir, f"mv_adapter_{get_random_hex()}.png")
265
  make_image_grid(images, rows=1).save(mv_image_path)
 
276
  save_dir=save_dir,
277
  save_name=f"polygenixai_texture_mesh_{get_random_hex()}.glb",
278
  uv_unwarp=True,
279
+ uv_size=2048, # Reduced for L4 and ZeroGPU
280
  rgb_path=mv_image_path,
281
  rgb_process_config=ModProcessConfig(view_upscale=True, inpaint_mode="view"),
282
  camera_azimuth_deg=[x - 90 for x in [0, 90, 180, 270, 180, 180]],
283
  )
284
 
285
+ logger.info(f"run_full completed successfully, textured model saved to {textured_glb_path}")
286
  return image_seg, mesh_path, textured_glb_path
287
  except Exception as e:
288
  logger.error(f"Error in run_full: {str(e)}")
289
  raise
290
 
291
+ def gradio_generate(image: str, seed: int = 0, num_inference_steps: int = 30, guidance_scale: float = 7.5, simplify: bool = True, target_face_num: int = DEFAULT_FACE_NUMBER, style_filter: str = "None"):
292
  try:
293
  logger.info("Starting gradio_generate")
294
  # Verify API key
 
312
  logger.error(f"Image file not found: {temp_image_path}")
313
  raise ValueError("Invalid or missing image file")
314
 
315
+ image_seg, mesh_path, textured_glb_path = run_full(temp_image_path, seed, num_inference_steps, guidance_scale, simplify, target_face_num, req=None, style_filter=style_filter)
316
  session_hash = os.path.basename(os.path.dirname(textured_glb_path))
317
  logger.info(f"Generated model at /files/{session_hash}/{os.path.basename(textured_glb_path)}")
318
  return {"file_url": f"/files/{session_hash}/{os.path.basename(textured_glb_path)}"}
 
348
  logger.error(f"Error in get_random_seed: {str(e)}")
349
  raise
350
 
 
351
  def download_image(url: str, save_path: str) -> str:
352
  """Download an image from a URL and save it locally."""
353
  try:
 
363
  logger.error(f"Failed to download image from {url}: {str(e)}")
364
  raise
365
 
366
+ @conditional_gpu_decorator()
367
  @torch.no_grad()
368
  def run_segmentation(image):
369
  try:
 
388
 
389
  image = prepare_image(image_path, bg_color=np.array([1.0, 1.0, 1.0]), rmbg_net=rmbg_net)
390
  logger.info("Segmentation complete")
391
+ torch.cuda.empty_cache()
392
  return image
393
  except Exception as e:
394
  logger.error(f"Error in run_segmentation: {str(e)}")
395
  raise
396
 
397
+ @conditional_gpu_decorator(duration=5)
398
+ @retry_on_gpu_abort(max_attempts=3, delay=5)
399
  @torch.no_grad()
400
  def image_to_3d(
401
+ image,
402
  seed: int,
403
  num_inference_steps: int,
404
  guidance_scale: float,
 
408
  ):
409
  try:
410
  logger.info("Running image_to_3d")
411
+ if not check_quota():
412
+ raise gr.Error("Insufficient GPU quota remaining")
413
  # Handle FileData dict from gradio_client
414
  if isinstance(image, dict):
415
  image_path = image.get("path") or image.get("url")
 
444
  mesh_path = os.path.join(save_dir, f"polygenixai_{get_random_hex()}.glb")
445
  mesh.export(mesh_path)
446
  logger.info(f"Saved mesh to {mesh_path}")
447
+ logger.info(f"VRAM usage after mesh generation: {torch.cuda.memory_allocated(DEVICE)/1e9:.2f} GB")
448
 
449
  torch.cuda.empty_cache()
450
  return mesh_path
 
452
  logger.error(f"Error in image_to_3d: {str(e)}")
453
  raise
454
 
455
+ @conditional_gpu_decorator(duration=5)
456
+ @retry_on_gpu_abort(max_attempts=3, delay=5)
457
  @torch.no_grad()
458
+ def run_texture(image, mesh_path: str, seed: int, req: gr.Request, style_filter: str = "None"):
459
  try:
460
+ logger.info(f"Running texture generation with style: {style_filter}")
461
+ if not check_quota():
462
+ raise gr.Error("Insufficient GPU quota remaining")
463
+ height, width = 512, 512 # Reduced for L4 and ZeroGPU
464
  cameras = get_orthogonal_camera(
465
  elevation_deg=[0, 0, 0, 0, 89.99, -89.99],
466
  distance=[1.8] * NUM_VIEWS,
 
495
  .to(DEVICE)
496
  )
497
 
498
+ # Handle both file path and PIL Image
499
+ if isinstance(image, str):
500
+ if image.startswith("http"):
501
+ temp_image_path = os.path.join(TMP_DIR, f"input_{get_random_hex()}.png")
502
+ image = download_image(image, temp_image_path)
503
+ image = Image.open(image)
504
+ elif not isinstance(image, Image.Image):
505
+ logger.error(f"Invalid image type: {type(image)}")
506
+ raise ValueError(f"Expected PIL Image or str (path/URL), got {type(image)}")
507
+
508
  image = remove_bg_fn(image)
509
  image = preprocess_image(image, height, width)
510
 
 
512
  if seed != -1 and isinstance(seed, int):
513
  pipe_kwargs["generator"] = torch.Generator(device=DEVICE).manual_seed(seed)
514
 
515
+ prompt = f"high quality, {style_filter.lower()}" if style_filter != "None" else "high quality"
516
  images = mv_adapter_pipe(
517
+ prompt,
518
  height=height,
519
  width=width,
520
+ num_inference_steps=10, # Reduced for L4 and ZeroGPU
521
  guidance_scale=3.0,
522
  num_images_per_prompt=NUM_VIEWS,
523
  control_image=control_images,
 
530
  ).images
531
 
532
  torch.cuda.empty_cache()
533
+ logger.info(f"VRAM usage after texture generation: {torch.cuda.memory_allocated(DEVICE)/1e9:.2f} GB")
534
  save_dir = os.path.join(TMP_DIR, str(req.session_hash))
535
  os.makedirs(save_dir, exist_ok=True)
536
  mv_image_path = os.path.join(save_dir, f"mv_adapter_{get_random_hex()}.png")
 
548
  save_dir=save_dir,
549
  save_name=f"polygenixai_texture_mesh_{get_random_hex()}.glb",
550
  uv_unwarp=True,
551
+ uv_size=2048, # Reduced for L4 and ZeroGPU
552
  rgb_path=mv_image_path,
553
  rgb_process_config=ModProcessConfig(view_upscale=True, inpaint_mode="view"),
554
  camera_azimuth_deg=[x - 90 for x in [0, 90, 180, 270, 180, 180]],
 
560
  logger.error(f"Error in run_texture: {str(e)}")
561
  raise
562
 
563
+ @conditional_gpu_decorator(duration=10)
564
+ @retry_on_gpu_abort(max_attempts=3, delay=5)
565
  @torch.no_grad()
566
+ def run_full_api(image, seed: int = 0, num_inference_steps: int = 30, guidance_scale: float = 7.5, simplify: bool = True, target_face_num: int = DEFAULT_FACE_NUMBER, req: gr.Request = None, style_filter: str = "None"):
567
  try:
568
  logger.info("Running run_full_api")
569
+ if not check_quota():
570
+ raise gr.Error("Insufficient GPU quota remaining")
571
  # Handle FileData dict or URL
572
  if isinstance(image, dict):
573
  image_path = image.get("path") or image.get("url")
 
586
  logger.error(f"Invalid image path: {image_path}")
587
  raise ValueError(f"Invalid image path: {image_path}")
588
 
589
+ image_seg, mesh_path, textured_glb_path = run_full(image_path, seed, num_inference_steps, guidance_scale, simplify, target_face_num, req, style_filter)
590
  session_hash = os.path.basename(os.path.dirname(textured_glb_path))
591
  logger.info(f"Generated textured model at /files/{session_hash}/{os.path.basename(textured_glb_path)}")
592
+ return image_seg, mesh_path, textured_glb_path
593
  except Exception as e:
594
  logger.error(f"Error in run_full_api: {str(e)}")
595
  raise
 
602
  inputs=[
603
  gr.Image(type="filepath", label="Image"),
604
  gr.Number(label="Seed", value=0, precision=0),
605
+ gr.Number(label="Inference Steps", value=30, precision=0),
606
  gr.Number(label="Guidance Scale", value=7.5),
607
  gr.Checkbox(label="Simplify Mesh", value=True),
608
+ gr.Number(label="Target Face Number", value=DEFAULT_FACE_NUMBER, precision=0),
609
+ gr.Dropdown(
610
+ choices=["None", "Realistic", "Fantasy", "Cartoon", "Sci-Fi", "Vintage", "Cosmic", "Neon"],
611
+ label="Style Filter",
612
+ value="None",
613
+ ),
614
  ],
615
  outputs="json",
616
  api_name="/api/generate"
 
736
  minimum=8,
737
  maximum=50,
738
  step=1,
739
+ value=30, # Reduced for L4 and ZeroGPU
740
  info="Higher steps enhance detail but increase processing time",
741
  elem_classes="gr-slider"
742
  )
 
772
  for image in os.listdir(f"{TRIPOSG_CODE_DIR}/assets/example_data")
773
  ],
774
  fn=run_full,
775
+ inputs=[image_prompts, seed, num_inference_steps, guidance_scale, reduce_face, target_face_num, style_filter],
776
  outputs=[seg_image, model_output, textured_model_output],
777
  cache_examples=True,
778
  )
 
799
  ).then(lambda: gr.Button(interactive=True), outputs=[gen_texture_button])
800
  gen_texture_button.click(
801
  run_texture,
802
+ inputs=[image_prompts, model_output, seed, style_filter],
803
  outputs=[textured_model_output]
804
  )
805
  demo.load(start_session)