Spaces:
Running
on
Zero
Running
on
Zero
demo
Browse files
app.py
CHANGED
|
@@ -333,71 +333,99 @@ def create_splat_viewer_html(ply_url: str) -> str:
|
|
| 333 |
viewer_js_content = viewer_js_path.read_text() if viewer_js_path.exists() else ""
|
| 334 |
|
| 335 |
return f"""
|
| 336 |
-
<div id="splat-viewer-container" style="width:100%; height:600px; position:relative; background:#000; border:1px solid #333; border-radius:8px;">
|
| 337 |
<canvas id="canvas" style="width:100%; height:100%; display:block;"></canvas>
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
</div>
|
| 343 |
-
|
| 344 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
</div>
|
| 346 |
</div>
|
|
|
|
| 347 |
<script>
|
| 348 |
-
|
|
|
|
|
|
|
|
|
|
| 349 |
</script>
|
|
|
|
| 350 |
<script>
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
|
|
|
| 362 |
|
| 363 |
-
|
| 364 |
-
.
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
</script>
|
| 402 |
"""
|
| 403 |
|
|
@@ -433,6 +461,7 @@ def main():
|
|
| 433 |
### π‘ Tips for Best Results
|
| 434 |
- Works best with near-frontal portrait images
|
| 435 |
- The provided checkpoints were not trained with accessories (glasses, hats, etc.). Portraits containing accessories may produce suboptimal results.
|
|
|
|
| 436 |
""")
|
| 437 |
|
| 438 |
with gr.Row():
|
|
|
|
| 333 |
viewer_js_content = viewer_js_path.read_text() if viewer_js_path.exists() else ""
|
| 334 |
|
| 335 |
return f"""
|
| 336 |
+
<div id="splat-viewer-container" style="width:100%; height:600px; position:relative; background:#000; border:1px solid #333; border-radius:8px; overflow:hidden;">
|
| 337 |
<canvas id="canvas" style="width:100%; height:100%; display:block;"></canvas>
|
| 338 |
+
|
| 339 |
+
<!-- Required elements for viewer.js -->
|
| 340 |
+
<div id="spinner" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:white; font-family:Arial; z-index:10;">
|
| 341 |
+
<div style="text-align:center; background:rgba(0,0,0,0.8); padding:20px; border-radius:8px;">
|
| 342 |
+
<div style="font-size:14px; margin-bottom:10px;">Loading 3D Viewer...</div>
|
| 343 |
+
<div style="background:#333; height:4px; width:200px; border-radius:2px; overflow:hidden;">
|
| 344 |
+
<div id="progress" style="background:#4CAF50; height:100%; width:0%; transition:width 0.3s;"></div>
|
| 345 |
+
</div>
|
| 346 |
+
</div>
|
| 347 |
</div>
|
| 348 |
+
|
| 349 |
+
<div id="message" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#ff4444; font-family:Arial; font-size:14px; background:rgba(0,0,0,0.9); padding:20px; border-radius:8px; display:none; z-index:11;"></div>
|
| 350 |
+
|
| 351 |
+
<div id="fps" style="position:absolute; top:10px; right:10px; color:white; font-family:monospace; font-size:11px; background:rgba(0,0,0,0.7); padding:6px 10px; border-radius:4px; display:none;"></div>
|
| 352 |
+
|
| 353 |
+
<div id="camid" style="position:absolute; top:40px; right:10px; color:white; font-family:monospace; font-size:11px; background:rgba(0,0,0,0.7); padding:6px 10px; border-radius:4px; display:none;"></div>
|
| 354 |
+
|
| 355 |
+
<div id="controls-info" style="position:absolute; bottom:10px; left:10px; color:white; font-family:Arial; font-size:11px; background:rgba(0,0,0,0.7); padding:8px 12px; border-radius:4px;">
|
| 356 |
+
<strong>Controls:</strong> Drag: Rotate | Scroll: Zoom | Right-drag: Pan
|
| 357 |
</div>
|
| 358 |
</div>
|
| 359 |
+
|
| 360 |
<script>
|
| 361 |
+
// Inject viewer.js
|
| 362 |
+
(function() {{
|
| 363 |
+
{viewer_js_content}
|
| 364 |
+
}})();
|
| 365 |
</script>
|
| 366 |
+
|
| 367 |
<script>
|
| 368 |
+
// Auto-load the PLY file after viewer initializes
|
| 369 |
+
(function() {{
|
| 370 |
+
const plyUrl = '{ply_url}';
|
| 371 |
+
console.log('=== Splat Viewer Init ===');
|
| 372 |
+
console.log('PLY URL:', plyUrl);
|
| 373 |
+
|
| 374 |
+
// Wait for viewer to initialize
|
| 375 |
+
let attempts = 0;
|
| 376 |
+
const maxAttempts = 50;
|
| 377 |
+
|
| 378 |
+
const checkAndLoad = setInterval(function() {{
|
| 379 |
+
attempts++;
|
| 380 |
|
| 381 |
+
if (window.worker) {{
|
| 382 |
+
console.log('β Worker initialized after', attempts * 100, 'ms');
|
| 383 |
+
clearInterval(checkAndLoad);
|
| 384 |
+
|
| 385 |
+
// Load the PLY file
|
| 386 |
+
fetch(plyUrl)
|
| 387 |
+
.then(response => {{
|
| 388 |
+
console.log('Fetch status:', response.status);
|
| 389 |
+
if (!response.ok) throw new Error(`HTTP ${{response.status}}`);
|
| 390 |
+
return response.arrayBuffer();
|
| 391 |
+
}})
|
| 392 |
+
.then(buffer => {{
|
| 393 |
+
console.log('β PLY loaded:', buffer.byteLength, 'bytes');
|
| 394 |
+
|
| 395 |
+
// Send to worker (viewer.js will handle it)
|
| 396 |
+
const file = new File([buffer], "model.ply");
|
| 397 |
+
const reader = new FileReader();
|
| 398 |
+
reader.onload = function() {{
|
| 399 |
+
window.worker.postMessage({{ ply: reader.result }});
|
| 400 |
+
console.log('β PLY sent to worker');
|
| 401 |
+
}};
|
| 402 |
+
reader.readAsArrayBuffer(file);
|
| 403 |
+
}})
|
| 404 |
+
.catch(error => {{
|
| 405 |
+
console.error('β Error:', error);
|
| 406 |
+
const spinner = document.getElementById('spinner');
|
| 407 |
+
const message = document.getElementById('message');
|
| 408 |
+
if (spinner) spinner.style.display = 'none';
|
| 409 |
+
if (message) {{
|
| 410 |
+
message.textContent = 'Error loading model: ' + error.message;
|
| 411 |
+
message.style.display = 'block';
|
| 412 |
+
}}
|
| 413 |
+
}});
|
| 414 |
+
}} else if (attempts >= maxAttempts) {{
|
| 415 |
+
console.error('β Worker failed to initialize');
|
| 416 |
+
clearInterval(checkAndLoad);
|
| 417 |
+
const spinner = document.getElementById('spinner');
|
| 418 |
+
const message = document.getElementById('message');
|
| 419 |
+
if (spinner) spinner.style.display = 'none';
|
| 420 |
+
if (message) {{
|
| 421 |
+
message.textContent = 'Viewer failed to initialize. Please refresh the page.';
|
| 422 |
+
message.style.display = 'block';
|
| 423 |
+
}}
|
| 424 |
+
}} else {{
|
| 425 |
+
console.log('Waiting for worker...', attempts);
|
| 426 |
+
}}
|
| 427 |
+
}}, 100);
|
| 428 |
+
}})();
|
| 429 |
</script>
|
| 430 |
"""
|
| 431 |
|
|
|
|
| 461 |
### π‘ Tips for Best Results
|
| 462 |
- Works best with near-frontal portrait images
|
| 463 |
- The provided checkpoints were not trained with accessories (glasses, hats, etc.). Portraits containing accessories may produce suboptimal results.
|
| 464 |
+
- If face detection fails, try disabling auto-cropping and manually crop to square
|
| 465 |
""")
|
| 466 |
|
| 467 |
with gr.Row():
|