HaiderCG commited on
Commit
8a8e6e8
·
1 Parent(s): ef2c2fe

Bind ZeroGPU handler directly and log SAM stages

Browse files
Files changed (1) hide show
  1. app.py +41 -66
app.py CHANGED
@@ -233,10 +233,17 @@ def _load_models_at_startup() -> None:
233
  _load_models_at_startup()
234
 
235
 
236
- def _segment(image: Image.Image, prompt: str) -> tuple[np.ndarray | None, int]:
 
 
237
  import torch
238
 
239
  model, processor = _load_sam3()
 
 
 
 
 
240
  inputs = processor(images=image, text=prompt, return_tensors="pt").to(model.device)
241
  with torch.inference_mode():
242
  output = model(**inputs)
@@ -248,10 +255,17 @@ def _segment(image: Image.Image, prompt: str) -> tuple[np.ndarray | None, int]:
248
  )[0]
249
  masks = result["masks"]
250
  if len(masks) == 0:
251
- return None, 0
 
252
  scores = torch.as_tensor(result["scores"])
253
  best = int(scores.argmax().item())
254
- return masks[best].detach().cpu().numpy().astype(bool), len(masks)
 
 
 
 
 
 
255
 
256
 
257
  def _overlay(image: np.ndarray, mask: np.ndarray) -> np.ndarray:
@@ -282,9 +296,28 @@ def _request_log(request_id: str, event: str, detail: str = "") -> None:
282
 
283
 
284
  @spaces.GPU(duration=360)
285
- def _reconstruct_gpu(
286
- image: Image.Image, prompt: str, request_id: str
287
  ) -> tuple[Any, Any, Any, str]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  started = time.monotonic()
289
  rgb = image.convert("RGB")
290
  image_array = np.asarray(rgb, dtype=np.uint8)
@@ -297,11 +330,11 @@ def _reconstruct_gpu(
297
  try:
298
  _request_log(request_id, "stage started", stage)
299
  stage_started = time.monotonic()
300
- mask, count = _segment(rgb, prompt)
301
  _request_log(
302
  request_id,
303
- "stage completed",
304
- f"{stage}; matching_instances={count}; stage_seconds={time.monotonic() - stage_started:.1f}; total_seconds={time.monotonic() - started:.1f}",
305
  )
306
  if mask is None:
307
  message = f"SAM 3 did not find '{prompt}' in this image."
@@ -356,64 +389,6 @@ def _reconstruct_gpu(
356
  )
357
 
358
 
359
- def reconstruct(image: Image.Image | None, prompt: str):
360
- """Stream a start message, then call the bounded GPU function."""
361
- if image is None:
362
- yield None, None, None, "Upload an image first."
363
- return
364
- prompt = (prompt or "").strip()
365
- if not prompt:
366
- yield None, None, None, "Enter a short object description, such as 'chair'."
367
- return
368
- if SETUP_ERROR:
369
- yield None, None, None, f"Space setup is incomplete: {SETUP_ERROR}"
370
- return
371
- if MODEL_SETUP_ERROR:
372
- yield None, None, None, f"Model setup failed: {MODEL_SETUP_ERROR}"
373
- return
374
- if SAM3 is None or SAM3D is None:
375
- yield None, None, None, "Models are not ready; check the Space startup logs."
376
- return
377
-
378
- request_id = uuid.uuid4().hex[:8]
379
- _request_log(
380
- request_id,
381
- "request accepted",
382
- "submitting to ZeroGPU; maximum GPU time is 360s",
383
- )
384
- yield None, None, None, "Queued on ZeroGPU; maximum GPU time is 360 seconds. Progress is recorded in Space Container logs."
385
- try:
386
- result = _reconstruct_gpu(image, prompt, request_id)
387
- except Exception as exc:
388
- _request_log(
389
- request_id,
390
- "ZeroGPU call failed",
391
- f"{type(exc).__name__}: {exc}",
392
- )
393
- traceback.print_exc()
394
- error_text = str(exc)
395
- error_lower = error_text.lower()
396
- is_timeout = isinstance(exc, TimeoutError) or any(
397
- word in error_lower for word in ("timeout", "timed out", "deadline", "time limit")
398
- )
399
- is_abort = any(word in error_lower for word in ("aborted", "killed", "worker lost"))
400
- if is_timeout:
401
- status_text = (
402
- "ZeroGPU reported that the request hit its runtime limit (maximum 360 seconds). "
403
- "Check Container logs for the last completed stage."
404
- )
405
- elif is_abort:
406
- status_text = (
407
- "ZeroGPU aborted or lost the request before it returned; that alone does not prove "
408
- "a timeout. Check Container logs for the last completed stage."
409
- )
410
- else:
411
- status_text = f"ZeroGPU request failed: {type(exc).__name__}: {error_text}"
412
- yield None, None, None, status_text
413
- return
414
- yield result
415
-
416
-
417
  def _readiness() -> str:
418
  if SETUP_ERROR:
419
  return f"Setup: {SETUP_ERROR}"
 
233
  _load_models_at_startup()
234
 
235
 
236
+ def _segment(
237
+ image: Image.Image, prompt: str, request_id: str
238
+ ) -> tuple[np.ndarray | None, int, float | None]:
239
  import torch
240
 
241
  model, processor = _load_sam3()
242
+ _request_log(
243
+ request_id,
244
+ "SAM 3 inference started",
245
+ f"prompt={prompt!r}; image={image.width}x{image.height}",
246
+ )
247
  inputs = processor(images=image, text=prompt, return_tensors="pt").to(model.device)
248
  with torch.inference_mode():
249
  output = model(**inputs)
 
255
  )[0]
256
  masks = result["masks"]
257
  if len(masks) == 0:
258
+ _request_log(request_id, "SAM 3 inference completed", "instances=0")
259
+ return None, 0, None
260
  scores = torch.as_tensor(result["scores"])
261
  best = int(scores.argmax().item())
262
+ best_score = float(scores[best].item())
263
+ _request_log(
264
+ request_id,
265
+ "SAM 3 inference completed",
266
+ f"instances={len(masks)}; selected_mask={best}; score={best_score:.4f}",
267
+ )
268
+ return masks[best].detach().cpu().numpy().astype(bool), len(masks), best_score
269
 
270
 
271
  def _overlay(image: np.ndarray, mask: np.ndarray) -> np.ndarray:
 
296
 
297
 
298
  @spaces.GPU(duration=360)
299
+ def reconstruct(
300
+ image: Image.Image | None, prompt: str
301
  ) -> tuple[Any, Any, Any, str]:
302
+ """Gradio's direct ZeroGPU event handler; keep it directly decorated."""
303
+ if image is None:
304
+ return None, None, None, "Upload an image first."
305
+ prompt = (prompt or "").strip()
306
+ if not prompt:
307
+ return None, None, None, "Enter a short object description, such as 'chair'."
308
+ if SETUP_ERROR:
309
+ return None, None, None, f"Space setup is incomplete: {SETUP_ERROR}"
310
+ if MODEL_SETUP_ERROR:
311
+ return None, None, None, f"Model setup failed: {MODEL_SETUP_ERROR}"
312
+ if SAM3 is None or SAM3D is None:
313
+ return None, None, None, "Models are not ready; check the Space startup logs."
314
+
315
+ request_id = uuid.uuid4().hex[:8]
316
+ _request_log(
317
+ request_id,
318
+ "GPU handler entered",
319
+ "maximum_gpu_seconds=360",
320
+ )
321
  started = time.monotonic()
322
  rgb = image.convert("RGB")
323
  image_array = np.asarray(rgb, dtype=np.uint8)
 
330
  try:
331
  _request_log(request_id, "stage started", stage)
332
  stage_started = time.monotonic()
333
+ mask, count, score = _segment(rgb, prompt, request_id)
334
  _request_log(
335
  request_id,
336
+ "SAM 3 stage completed",
337
+ f"matching_instances={count}; selected_score={score}; stage_seconds={time.monotonic() - stage_started:.1f}; total_seconds={time.monotonic() - started:.1f}",
338
  )
339
  if mask is None:
340
  message = f"SAM 3 did not find '{prompt}' in this image."
 
389
  )
390
 
391
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  def _readiness() -> str:
393
  if SETUP_ERROR:
394
  return f"Setup: {SETUP_ERROR}"