Georg Claude Sonnet 4.5 commited on
Commit
08948c8
·
1 Parent(s): 703d3c2

Fix ZeroGPU startup error by adding proper Gradio UI

Browse files

- Add gradio_initialize() and gradio_estimate() wrapper functions
- Create two-tab UI: Initialize Object and Estimate Pose
- Add camera intrinsics inputs (fx, fy, cx, cy) with defaults (500, 500, 320, 240)
- Add File upload for reference images (multiple files)
- Add Image upload for query image
- Connect buttons to @spaces.GPU decorated functions via wrappers
- Keep FastAPI REST endpoints alongside Gradio UI

This fixes: 'No @spaces.GPU function detected during startup'

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +208 -5
app.py CHANGED
@@ -264,7 +264,118 @@ async def api_estimate(request: EstimateRequest):
264
  raise HTTPException(status_code=500, detail=str(e))
265
 
266
 
267
- # Gradio UI (simplified)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  with gr.Blocks(title="FoundationPose Inference", theme=gr.themes.Soft()) as gradio_app:
269
  gr.Markdown("# 🎯 FoundationPose 6D Object Pose Estimation")
270
 
@@ -273,12 +384,104 @@ with gr.Blocks(title="FoundationPose Inference", theme=gr.themes.Soft()) as grad
273
  elem_id="mode"
274
  )
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  gr.Markdown("""
277
- API Endpoints:
278
- - POST `/api/initialize` - Register object
279
- - POST `/api/estimate` - Estimate pose
 
 
 
280
 
281
- See documentation for usage examples.
282
  """)
283
 
284
 
 
264
  raise HTTPException(status_code=500, detail=str(e))
265
 
266
 
267
+ # Gradio wrapper functions
268
+ def gradio_initialize(object_id: str, reference_files: List, fx: float, fy: float, cx: float, cy: float):
269
+ """Gradio wrapper for object initialization."""
270
+ try:
271
+ if not reference_files:
272
+ return "Error: No reference images provided"
273
+
274
+ # Load reference images
275
+ reference_images = []
276
+ for file in reference_files:
277
+ img = cv2.imread(file.name)
278
+ if img is None:
279
+ continue
280
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
281
+ reference_images.append(img)
282
+
283
+ if not reference_images:
284
+ return "Error: Could not load any reference images"
285
+
286
+ # Prepare camera intrinsics
287
+ camera_intrinsics = {
288
+ "fx": fx,
289
+ "fy": fy,
290
+ "cx": cx,
291
+ "cy": cy
292
+ }
293
+
294
+ # Register object (calls @spaces.GPU decorated method)
295
+ success = pose_estimator.register_object(
296
+ object_id=object_id,
297
+ reference_images=reference_images,
298
+ camera_intrinsics=camera_intrinsics
299
+ )
300
+
301
+ if success:
302
+ return f"✓ Object '{object_id}' initialized with {len(reference_images)} reference images"
303
+ else:
304
+ return f"✗ Failed to initialize object '{object_id}'"
305
+
306
+ except Exception as e:
307
+ logger.error(f"Gradio initialization error: {e}", exc_info=True)
308
+ return f"Error: {str(e)}"
309
+
310
+
311
+ def gradio_estimate(object_id: str, query_image: np.ndarray, fx: float, fy: float, cx: float, cy: float):
312
+ """Gradio wrapper for pose estimation."""
313
+ try:
314
+ if query_image is None:
315
+ return "Error: No query image provided", None
316
+
317
+ # Prepare camera intrinsics
318
+ camera_intrinsics = {
319
+ "fx": fx,
320
+ "fy": fy,
321
+ "cx": cx,
322
+ "cy": cy
323
+ }
324
+
325
+ # Estimate pose (calls @spaces.GPU decorated method)
326
+ result = pose_estimator.estimate_pose(
327
+ object_id=object_id,
328
+ query_image=query_image,
329
+ camera_intrinsics=camera_intrinsics
330
+ )
331
+
332
+ if not result.get("success"):
333
+ error = result.get("error", "Unknown error")
334
+ return f"✗ Estimation failed: {error}", None
335
+
336
+ poses = result.get("poses", [])
337
+ note = result.get("note", "")
338
+
339
+ # Format output
340
+ if not poses:
341
+ output = "⚠ No poses detected\n"
342
+ if note:
343
+ output += f"\nNote: {note}"
344
+ return output, query_image
345
+
346
+ output = f"✓ Detected {len(poses)} pose(s):\n\n"
347
+ for i, pose in enumerate(poses):
348
+ output += f"Pose {i + 1}:\n"
349
+ output += f" Object ID: {pose.get('object_id', 'unknown')}\n"
350
+
351
+ if 'position' in pose:
352
+ pos = pose['position']
353
+ output += f" Position:\n"
354
+ output += f" x: {pos.get('x', 0):.4f} m\n"
355
+ output += f" y: {pos.get('y', 0):.4f} m\n"
356
+ output += f" z: {pos.get('z', 0):.4f} m\n"
357
+
358
+ if 'orientation' in pose:
359
+ ori = pose['orientation']
360
+ output += f" Orientation (quaternion):\n"
361
+ output += f" w: {ori.get('w', 0):.4f}\n"
362
+ output += f" x: {ori.get('x', 0):.4f}\n"
363
+ output += f" y: {ori.get('y', 0):.4f}\n"
364
+ output += f" z: {ori.get('z', 0):.4f}\n"
365
+
366
+ if 'confidence' in pose:
367
+ output += f" Confidence: {pose['confidence']:.2%}\n"
368
+
369
+ output += "\n"
370
+
371
+ return output, query_image
372
+
373
+ except Exception as e:
374
+ logger.error(f"Gradio estimation error: {e}", exc_info=True)
375
+ return f"Error: {str(e)}", None
376
+
377
+
378
+ # Gradio UI with proper @spaces.GPU function calls
379
  with gr.Blocks(title="FoundationPose Inference", theme=gr.themes.Soft()) as gradio_app:
380
  gr.Markdown("# 🎯 FoundationPose 6D Object Pose Estimation")
381
 
 
384
  elem_id="mode"
385
  )
386
 
387
+ with gr.Tabs():
388
+ # Tab 1: Initialize Object
389
+ with gr.Tab("Initialize Object"):
390
+ gr.Markdown("""
391
+ Upload reference images of your object from different angles (8-20 images recommended).
392
+ The model will learn the object's appearance for pose estimation.
393
+ """)
394
+
395
+ with gr.Row():
396
+ with gr.Column():
397
+ init_object_id = gr.Textbox(
398
+ label="Object ID",
399
+ placeholder="e.g., target_cube",
400
+ value="target_cube"
401
+ )
402
+
403
+ init_ref_files = gr.File(
404
+ label="Reference Images",
405
+ file_count="multiple",
406
+ file_types=["image"]
407
+ )
408
+
409
+ gr.Markdown("### Camera Intrinsics")
410
+ with gr.Row():
411
+ init_fx = gr.Number(label="fx (focal length x)", value=500.0)
412
+ init_fy = gr.Number(label="fy (focal length y)", value=500.0)
413
+ with gr.Row():
414
+ init_cx = gr.Number(label="cx (principal point x)", value=320.0)
415
+ init_cy = gr.Number(label="cy (principal point y)", value=240.0)
416
+
417
+ init_button = gr.Button("Initialize Object", variant="primary")
418
+
419
+ with gr.Column():
420
+ init_output = gr.Textbox(
421
+ label="Initialization Result",
422
+ lines=5,
423
+ interactive=False
424
+ )
425
+
426
+ init_button.click(
427
+ fn=gradio_initialize,
428
+ inputs=[init_object_id, init_ref_files, init_fx, init_fy, init_cx, init_cy],
429
+ outputs=init_output
430
+ )
431
+
432
+ # Tab 2: Estimate Pose
433
+ with gr.Tab("Estimate Pose"):
434
+ gr.Markdown("""
435
+ Upload a query image containing the initialized object.
436
+ The model will estimate the 6D pose (position + orientation).
437
+ """)
438
+
439
+ with gr.Row():
440
+ with gr.Column():
441
+ est_object_id = gr.Textbox(
442
+ label="Object ID",
443
+ placeholder="e.g., target_cube",
444
+ value="target_cube"
445
+ )
446
+
447
+ est_query_image = gr.Image(
448
+ label="Query Image",
449
+ type="numpy"
450
+ )
451
+
452
+ gr.Markdown("### Camera Intrinsics")
453
+ with gr.Row():
454
+ est_fx = gr.Number(label="fx (focal length x)", value=500.0)
455
+ est_fy = gr.Number(label="fy (focal length y)", value=500.0)
456
+ with gr.Row():
457
+ est_cx = gr.Number(label="cx (principal point x)", value=320.0)
458
+ est_cy = gr.Number(label="cy (principal point y)", value=240.0)
459
+
460
+ est_button = gr.Button("Estimate Pose", variant="primary")
461
+
462
+ with gr.Column():
463
+ est_output = gr.Textbox(
464
+ label="Pose Estimation Result",
465
+ lines=15,
466
+ interactive=False
467
+ )
468
+ est_viz = gr.Image(label="Query Image")
469
+
470
+ est_button.click(
471
+ fn=gradio_estimate,
472
+ inputs=[est_object_id, est_query_image, est_fx, est_fy, est_cx, est_cy],
473
+ outputs=[est_output, est_viz]
474
+ )
475
+
476
  gr.Markdown("""
477
+ ---
478
+ ## REST API Endpoints
479
+
480
+ This Space also provides REST API endpoints for programmatic access:
481
+ - POST `/api/initialize` - Register object with reference images
482
+ - POST `/api/estimate` - Estimate 6D pose from query image
483
 
484
+ See the [API documentation](https://huggingface.co/spaces/gpue/foundationpose) for details.
485
  """)
486
 
487