Spaces:
Sleeping
Sleeping
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>
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
-
See documentation for
|
| 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 |
|