Spaces:
Sleeping
Sleeping
Zhen Ye
commited on
Commit
·
fe2ee0d
1
Parent(s):
b30c328
Integrated GPT Reasoning and Object Track Cards
Browse files- LaserPerception/LaserPerception.css +58 -0
- LaserPerception/LaserPerception.html +131 -165
- LaserPerception/LaserPerception.js +84 -53
- app.py +3 -0
- inference.py +3 -2
LaserPerception/LaserPerception.css
CHANGED
|
@@ -866,4 +866,62 @@ input[type="number"]:focus {
|
|
| 866 |
|
| 867 |
::-webkit-scrollbar-thumb:hover {
|
| 868 |
background: rgba(255, 255, 255, .16);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
}
|
|
|
|
| 866 |
|
| 867 |
::-webkit-scrollbar-thumb:hover {
|
| 868 |
background: rgba(255, 255, 255, .16);
|
| 869 |
+
}
|
| 870 |
+
|
| 871 |
+
/* Track Cards */
|
| 872 |
+
.track-card {
|
| 873 |
+
background: rgba(255, 255, 255, 0.03);
|
| 874 |
+
border: 1px solid var(--border-color);
|
| 875 |
+
border-radius: 4px;
|
| 876 |
+
padding: 8px;
|
| 877 |
+
margin-bottom: 8px;
|
| 878 |
+
cursor: pointer;
|
| 879 |
+
transition: all 0.2s;
|
| 880 |
+
}
|
| 881 |
+
|
| 882 |
+
.track-card:hover {
|
| 883 |
+
background: rgba(255, 255, 255, 0.08);
|
| 884 |
+
}
|
| 885 |
+
|
| 886 |
+
.track-card.active {
|
| 887 |
+
border-color: var(--accent);
|
| 888 |
+
background: rgba(34, 211, 238, 0.1);
|
| 889 |
+
}
|
| 890 |
+
|
| 891 |
+
.track-card-header {
|
| 892 |
+
display: flex;
|
| 893 |
+
justify-content: space-between;
|
| 894 |
+
align-items: center;
|
| 895 |
+
font-weight: 600;
|
| 896 |
+
margin-bottom: 4px;
|
| 897 |
+
font-size: 13px;
|
| 898 |
+
color: var(--text-color);
|
| 899 |
+
}
|
| 900 |
+
|
| 901 |
+
.track-card-meta {
|
| 902 |
+
font-size: 11px;
|
| 903 |
+
color: var(--text-dim);
|
| 904 |
+
margin-bottom: 4px;
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
.track-card-body {
|
| 908 |
+
font-size: 11px;
|
| 909 |
+
line-height: 1.4;
|
| 910 |
+
color: #ccc;
|
| 911 |
+
background: rgba(0, 0, 0, 0.2);
|
| 912 |
+
padding: 6px;
|
| 913 |
+
border-radius: 4px;
|
| 914 |
+
}
|
| 915 |
+
|
| 916 |
+
.gpt-badge {
|
| 917 |
+
color: gold;
|
| 918 |
+
font-size: 10px;
|
| 919 |
+
border: 1px solid gold;
|
| 920 |
+
border-radius: 3px;
|
| 921 |
+
padding: 1px 4px;
|
| 922 |
+
margin-left: 6px;
|
| 923 |
+
}
|
| 924 |
+
|
| 925 |
+
.gpt-text {
|
| 926 |
+
color: #e0e0e0;
|
| 927 |
}
|
LaserPerception/LaserPerception.html
CHANGED
|
@@ -95,6 +95,10 @@
|
|
| 95 |
<input type="checkbox" id="enableDepthToggle">
|
| 96 |
<span>Enable Legacy Depth Map (Slow)</span>
|
| 97 |
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
</div>
|
| 99 |
|
| 100 |
<div class="hint mt-sm" id="detectorHint">
|
|
@@ -335,194 +339,156 @@
|
|
| 335 |
</div>
|
| 336 |
</div>
|
| 337 |
|
| 338 |
-
<div class="panel panel-summary">
|
| 339 |
<h3>
|
| 340 |
-
<span>
|
| 341 |
-
<span class="rightnote" id="
|
| 342 |
</h3>
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
<div
|
| 346 |
-
|
| 347 |
-
<div class="value" id="mMaxP">—</div>
|
| 348 |
-
<div class="sub" id="mMaxPSub">Propagation + beam control + duty cycle</div>
|
| 349 |
-
</div>
|
| 350 |
-
<div class="metric">
|
| 351 |
-
<div class="label">Worst-case required power</div>
|
| 352 |
-
<div class="value" id="mReqP">—</div>
|
| 353 |
-
<div class="sub" id="mReqPSub">From target robustness + reflectivity + aimpoint</div>
|
| 354 |
-
</div>
|
| 355 |
-
<div class="metric">
|
| 356 |
-
<div class="label">Feasibility margin</div>
|
| 357 |
-
<div class="value" id="mMargin">—</div>
|
| 358 |
-
<div class="sub" id="mMarginSub">Positive margin indicates viable engagement</div>
|
| 359 |
-
</div>
|
| 360 |
-
<div class="metric">
|
| 361 |
-
<div class="label">Recommended engagement</div>
|
| 362 |
-
<div class="value" id="mPlan">—</div>
|
| 363 |
-
<div class="sub" id="mPlanSub">Order + dwell window + assess</div>
|
| 364 |
</div>
|
| 365 |
</div>
|
|
|
|
| 366 |
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
</div>
|
|
|
|
|
|
|
| 389 |
</div>
|
| 390 |
</div>
|
| 391 |
-
</section>
|
| 392 |
-
|
| 393 |
-
<!-- ===== Tab 2 ===== -->
|
| 394 |
-
<section class="tab" id="tab-engage">
|
| 395 |
-
<div class="engage-grid">
|
| 396 |
-
<div class="panel">
|
| 397 |
-
<h3>
|
| 398 |
-
<span>Video Engage · Tracking + Dynamic Dwell</span>
|
| 399 |
-
<div style="display: flex; gap: 8px; align-items: center;">
|
| 400 |
-
<button class="collapse-btn" id="btnToggleSidebar">◀ Hide Sidebar</button>
|
| 401 |
-
<span class="rightnote" id="engageNote">Awaiting video</span>
|
| 402 |
-
</div>
|
| 403 |
-
</h3>
|
| 404 |
-
|
| 405 |
-
<div class="viewbox" style="min-height: 420px;">
|
| 406 |
-
<video id="videoEngage" playsinline muted></video>
|
| 407 |
-
<canvas id="engageOverlay" class="overlay"></canvas>
|
| 408 |
-
<div class="watermark">LOCK · DIST · DWELL · AIMPOINT · FIRE/ASSESS</div>
|
| 409 |
-
<div class="empty" id="engageEmpty">
|
| 410 |
-
<div class="big">No video loaded</div>
|
| 411 |
-
<div class="small">Upload a video. Run <b>Reason</b> first to initialize aimpoints and baseline dwell.
|
| 412 |
-
Then click <b>Engage</b>.</div>
|
| 413 |
-
</div>
|
| 414 |
-
</div>
|
| 415 |
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
<div class="strip mt-md">
|
| 423 |
-
<span class="chip" id="chipPolicy">POLICY:AUTO</span>
|
| 424 |
-
<span class="chip" id="chipTracks">TRACKS:0</span>
|
| 425 |
-
<span class="chip" id="chipBeam">BEAM:OFF</span>
|
| 426 |
-
<span class="chip" id="chipHz">DET:6Hz</span>
|
| 427 |
-
<span class="chip" id="chipFeed" title="Toggle raw vs HF-processed feed (if available)">FEED:RAW</span>
|
| 428 |
-
<span class="chip" id="chipDepth" title="Toggle depth view (if available)">VIEW:DEFAULT</span>
|
| 429 |
-
</div>
|
| 430 |
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
|
|
|
| 438 |
|
| 439 |
-
|
| 440 |
-
|
| 441 |
</div>
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
<div class="panel radar">
|
| 445 |
-
<h3>
|
| 446 |
-
<span>Radar / Relative Geometry</span>
|
| 447 |
-
<span class="rightnote">Dynamic</span>
|
| 448 |
-
</h3>
|
| 449 |
-
<canvas id="radarCanvas" width="600" height="260" class="full-size"></canvas>
|
| 450 |
-
</div>
|
| 451 |
-
|
| 452 |
-
<div class="panel" style="flex:1; min-height:0">
|
| 453 |
-
<h3>
|
| 454 |
-
<span>Live Track Cards</span>
|
| 455 |
-
<span class="rightnote" id="liveStamp">—</span>
|
| 456 |
-
</h3>
|
| 457 |
-
<div class="list" id="trackList" style="max-height:none"></div>
|
| 458 |
-
</div>
|
| 459 |
</div>
|
| 460 |
</div>
|
| 461 |
-
</section>
|
| 462 |
-
|
| 463 |
-
<!-- ===== Tab 3 ===== -->
|
| 464 |
-
<section class="tab" id="tab-trade">
|
| 465 |
-
<div class="trade-grid">
|
| 466 |
-
<div class="panel plot">
|
| 467 |
-
<h3>
|
| 468 |
-
<span>Range Sensitivity · Max vs Required Power · Dwell</span>
|
| 469 |
-
<span class="rightnote">Interactive</span>
|
| 470 |
-
</h3>
|
| 471 |
-
<canvas id="tradeCanvas" width="1100" height="420" class="full-size"></canvas>
|
| 472 |
-
</div>
|
| 473 |
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
<span class="rightnote">What-if</span>
|
| 478 |
-
</h3>
|
| 479 |
-
<div class="hint">This plot is computed from your current HEL and atmosphere knobs. It uses the selected
|
| 480 |
-
target’s baseline requirements (from Tab 1) as a reference curve.</div>
|
| 481 |
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 497 |
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
|
| 511 |
-
|
| 512 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
</div>
|
| 514 |
</div>
|
| 515 |
-
</section>
|
| 516 |
-
</main>
|
| 517 |
-
</div>
|
| 518 |
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
|
| 524 |
-
|
| 525 |
-
|
| 526 |
</div>
|
| 527 |
|
| 528 |
<script>
|
|
|
|
| 95 |
<input type="checkbox" id="enableDepthToggle">
|
| 96 |
<span>Enable Legacy Depth Map (Slow)</span>
|
| 97 |
</label>
|
| 98 |
+
<label class="checkbox-row" for="enableGPTToggle" style="margin-top: 4px;">
|
| 99 |
+
<input type="checkbox" id="enableGPTToggle">
|
| 100 |
+
<span style="color: var(--accent-light);">Enable GPT Reasoning</span>
|
| 101 |
+
</label>
|
| 102 |
</div>
|
| 103 |
|
| 104 |
<div class="hint mt-sm" id="detectorHint">
|
|
|
|
| 339 |
</div>
|
| 340 |
</div>
|
| 341 |
|
| 342 |
+
<div class="panel panel-summary" style="display:flex; flex-direction:column; min-height: 0;">
|
| 343 |
<h3>
|
| 344 |
+
<span>Object Track Cards</span>
|
| 345 |
+
<span class="rightnote" id="trackCount">0</span>
|
| 346 |
</h3>
|
| 347 |
+
<div class="list" id="frameTrackList" style="flex:1; overflow-y:auto; padding:8px;">
|
| 348 |
+
<!-- Cards injected here -->
|
| 349 |
+
<div style="font-style:italic; color:var(--text-dim); text-align:center; margin-top:20px;">
|
| 350 |
+
No objects tracked.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
</div>
|
| 352 |
</div>
|
| 353 |
+
</div>
|
| 354 |
|
| 355 |
+
</div>
|
| 356 |
+
</div>
|
| 357 |
+
</section>
|
| 358 |
+
|
| 359 |
+
<!-- ===== Tab 2 ===== -->
|
| 360 |
+
<section class="tab" id="tab-engage">
|
| 361 |
+
<div class="engage-grid">
|
| 362 |
+
<div class="panel">
|
| 363 |
+
<h3>
|
| 364 |
+
<span>Video Engage · Tracking + Dynamic Dwell</span>
|
| 365 |
+
<div style="display: flex; gap: 8px; align-items: center;">
|
| 366 |
+
<button class="collapse-btn" id="btnToggleSidebar">◀ Hide Sidebar</button>
|
| 367 |
+
<span class="rightnote" id="engageNote">Awaiting video</span>
|
| 368 |
+
</div>
|
| 369 |
+
</h3>
|
| 370 |
+
|
| 371 |
+
<div class="viewbox" style="min-height: 420px;">
|
| 372 |
+
<video id="videoEngage" playsinline muted></video>
|
| 373 |
+
<canvas id="engageOverlay" class="overlay"></canvas>
|
| 374 |
+
<div class="watermark">LOCK · DIST · DWELL · AIMPOINT · FIRE/ASSESS</div>
|
| 375 |
+
<div class="empty" id="engageEmpty">
|
| 376 |
+
<div class="big">No video loaded</div>
|
| 377 |
+
<div class="small">Upload a video. Run <b>Reason</b> first to initialize aimpoints and baseline dwell.
|
| 378 |
+
Then click <b>Engage</b>.</div>
|
| 379 |
</div>
|
| 380 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
|
| 382 |
+
<div class="btnrow mt-md">
|
| 383 |
+
<button id="btnEngage" class="btn">Engage</button>
|
| 384 |
+
<button id="btnPause" class="btn secondary">Pause</button>
|
| 385 |
+
<button id="btnReset" class="btn secondary">Reset</button>
|
| 386 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
|
| 388 |
+
<div class="strip mt-md">
|
| 389 |
+
<span class="chip" id="chipPolicy">POLICY:AUTO</span>
|
| 390 |
+
<span class="chip" id="chipTracks">TRACKS:0</span>
|
| 391 |
+
<span class="chip" id="chipBeam">BEAM:OFF</span>
|
| 392 |
+
<span class="chip" id="chipHz">DET:6Hz</span>
|
| 393 |
+
<span class="chip" id="chipFeed" title="Toggle raw vs HF-processed feed (if available)">FEED:RAW</span>
|
| 394 |
+
<span class="chip" id="chipDepth" title="Toggle depth view (if available)">VIEW:DEFAULT</span>
|
| 395 |
+
</div>
|
| 396 |
|
| 397 |
+
<div class="mt-md">
|
| 398 |
+
<div class="row"><label>Active dwell progress (selected)</label><small class="mini" id="dwellText">—</small>
|
| 399 |
</div>
|
| 400 |
+
<div class="bar">
|
| 401 |
+
<div id="dwellBar"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
</div>
|
| 403 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
|
| 405 |
+
<div class="hint mt-md">Manual targeting: choose “Manual” in Engagement Policy, then
|
| 406 |
+
click a target in the video. The “beam” will track its aimpoint and accumulate dwell.</div>
|
| 407 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
|
| 409 |
+
<div class="engage-right">
|
| 410 |
+
<div class="panel radar">
|
| 411 |
+
<h3>
|
| 412 |
+
<span>Radar / Relative Geometry</span>
|
| 413 |
+
<span class="rightnote">Dynamic</span>
|
| 414 |
+
</h3>
|
| 415 |
+
<canvas id="radarCanvas" width="600" height="260" class="full-size"></canvas>
|
| 416 |
+
</div>
|
| 417 |
|
| 418 |
+
<div class="panel" style="flex:1; min-height:0">
|
| 419 |
+
<h3>
|
| 420 |
+
<span>Live Track Cards</span>
|
| 421 |
+
<span class="rightnote" id="liveStamp">—</span>
|
| 422 |
+
</h3>
|
| 423 |
+
<div class="list" id="trackList" style="max-height:none"></div>
|
| 424 |
+
</div>
|
| 425 |
+
</div>
|
| 426 |
+
</div>
|
| 427 |
+
</section>
|
| 428 |
+
|
| 429 |
+
<!-- ===== Tab 3 ===== -->
|
| 430 |
+
<section class="tab" id="tab-trade">
|
| 431 |
+
<div class="trade-grid">
|
| 432 |
+
<div class="panel plot">
|
| 433 |
+
<h3>
|
| 434 |
+
<span>Range Sensitivity · Max vs Required Power · Dwell</span>
|
| 435 |
+
<span class="rightnote">Interactive</span>
|
| 436 |
+
</h3>
|
| 437 |
+
<canvas id="tradeCanvas" width="1100" height="420" class="full-size"></canvas>
|
| 438 |
+
</div>
|
| 439 |
|
| 440 |
+
<div class="panel">
|
| 441 |
+
<h3>
|
| 442 |
+
<span>Trade Controls</span>
|
| 443 |
+
<span class="rightnote">What-if</span>
|
| 444 |
+
</h3>
|
| 445 |
+
<div class="hint">This plot is computed from your current HEL and atmosphere knobs. It uses the selected
|
| 446 |
+
target’s baseline requirements (from Tab 1) as a reference curve.</div>
|
| 447 |
|
| 448 |
+
<div class="mt-md">
|
| 449 |
+
<label>Selected target for curve</label>
|
| 450 |
+
<select id="tradeTarget"></select>
|
| 451 |
+
</div>
|
| 452 |
|
| 453 |
+
<div class="grid2 mt-sm">
|
| 454 |
+
<div>
|
| 455 |
+
<label>Range sweep min (m)</label>
|
| 456 |
+
<input id="rMin" type="number" value="200" min="50" max="10000" step="50" />
|
| 457 |
+
</div>
|
| 458 |
+
<div>
|
| 459 |
+
<label>Range sweep max (m)</label>
|
| 460 |
+
<input id="rMax" type="number" value="6000" min="100" max="20000" step="50" />
|
| 461 |
</div>
|
| 462 |
</div>
|
|
|
|
|
|
|
|
|
|
| 463 |
|
| 464 |
+
<div class="row mt-md">
|
| 465 |
+
<label>Show P(kill)</label>
|
| 466 |
+
<select id="showPk">
|
| 467 |
+
<option value="on">On</option>
|
| 468 |
+
<option value="off">Off</option>
|
| 469 |
+
</select>
|
| 470 |
+
</div>
|
| 471 |
+
|
| 472 |
+
<div class="btnrow">
|
| 473 |
+
<button class="btn secondary" id="btnReplot">Replot</button>
|
| 474 |
+
<button class="btn secondary" id="btnSnap">Snapshot (log)</button>
|
| 475 |
+
</div>
|
| 476 |
+
|
| 477 |
+
<div class="hint">This tab is designed to look like a weapon trade-space console: propagation, lethality
|
| 478 |
+
margin, and dwell inflation with range and atmosphere.</div>
|
| 479 |
+
</div>
|
| 480 |
+
</div>
|
| 481 |
+
</section>
|
| 482 |
+
</main>
|
| 483 |
+
</div>
|
| 484 |
+
|
| 485 |
+
<footer>
|
| 486 |
+
<div>Demo mode · Unclassified visuals · Integrate your APIs where marked</div>
|
| 487 |
+
<div class="mono" id="telemetry">HEL=60kW · VIS=16km · Cn²=5/10 · AO=7/10 · DET=6Hz</div>
|
| 488 |
+
</footer>
|
| 489 |
|
| 490 |
+
<!-- Hidden video used only for first-frame capture -->
|
| 491 |
+
<video id="videoHidden" playsinline muted style="display:none"></video>
|
| 492 |
</div>
|
| 493 |
|
| 494 |
<script>
|
LaserPerception/LaserPerception.js
CHANGED
|
@@ -146,8 +146,11 @@
|
|
| 146 |
const objCount = $("#objCount");
|
| 147 |
const featureTable = $("#featureTable");
|
| 148 |
const selId = $("#selId");
|
|
|
|
| 149 |
|
| 150 |
-
const
|
|
|
|
|
|
|
| 151 |
const summaryTable = $("#summaryTable");
|
| 152 |
const mMaxP = $("#mMaxP");
|
| 153 |
const mReqP = $("#mReqP");
|
|
@@ -876,9 +879,11 @@
|
|
| 876 |
// Add depth_estimator parameter for depth processing
|
| 877 |
const enableDepthToggle = document.getElementById("enableDepthToggle");
|
| 878 |
const useLegacyDepth = enableDepthToggle && enableDepthToggle.checked;
|
|
|
|
| 879 |
|
| 880 |
form.append("depth_estimator", useLegacyDepth ? "depth" : "");
|
| 881 |
form.append("enable_depth", useLegacyDepth ? "true" : "false");
|
|
|
|
| 882 |
|
| 883 |
// Submit async job
|
| 884 |
setHfStatus(`submitting ${mode} job...`);
|
|
@@ -1927,9 +1932,9 @@
|
|
| 1927 |
// Clear previous detections before running new detection
|
| 1928 |
state.detections = [];
|
| 1929 |
state.selectedId = null;
|
| 1930 |
-
|
| 1931 |
renderFrameOverlay();
|
| 1932 |
-
renderSummary();
|
| 1933 |
renderFeatures(null);
|
| 1934 |
renderTrade();
|
| 1935 |
|
|
@@ -2023,9 +2028,9 @@
|
|
| 2023 |
|
| 2024 |
// pick default selection
|
| 2025 |
state.selectedId = state.detections[0]?.id || null;
|
| 2026 |
-
|
| 2027 |
renderFrameOverlay();
|
| 2028 |
-
renderSummary();
|
| 2029 |
renderFeatures(getSelected());
|
| 2030 |
renderTrade();
|
| 2031 |
|
|
@@ -2160,61 +2165,87 @@
|
|
| 2160 |
}
|
| 2161 |
|
| 2162 |
// ========= Rendering: Object list, features, summary table =========
|
| 2163 |
-
|
| 2164 |
-
|
| 2165 |
-
|
| 2166 |
-
|
| 2167 |
-
|
| 2168 |
-
|
| 2169 |
-
|
| 2170 |
-
|
| 2171 |
-
|
| 2172 |
-
|
| 2173 |
}
|
| 2174 |
|
| 2175 |
-
|
| 2176 |
-
|
| 2177 |
-
|
|
|
|
| 2178 |
|
| 2179 |
-
|
| 2180 |
-
|
| 2181 |
-
|
| 2182 |
-
div.dataset.id = d.id;
|
| 2183 |
|
| 2184 |
-
|
| 2185 |
-
|
| 2186 |
-
const rangeSuffix = rangeTxt === "—" ? "" : ` (${rangeData.source})`;
|
| 2187 |
-
const relVal = getDisplayRel(d);
|
| 2188 |
-
const relTxt = relVal != null ? relVal.toFixed(2) : "—";
|
| 2189 |
-
const dwellTxt = d.baseDwell_s ? `${d.baseDwell_s.toFixed(1)} s` : "—";
|
| 2190 |
-
const pkTxt = (d.pkill != null) ? `${Math.round(d.pkill * 100)}%` : "—";
|
| 2191 |
|
| 2192 |
-
|
| 2193 |
-
|
| 2194 |
-
|
| 2195 |
-
|
| 2196 |
-
<div class="cls">${escapeHtml(d.label)}</div>
|
| 2197 |
-
</div>
|
| 2198 |
-
<div style="display:flex; gap:8px; align-items:center; justify-content:flex-end;">${isMissionFocusLabel(d.label) ? `<span class="badge" style="border-color: rgba(34,211,238,.45); background: rgba(34,211,238,.08)">FOCUS</span>` : ""}<div class="badge"><span class="dot" style="width:7px;height:7px"></span><span>${Math.round(d.score * 100)}%</span></div></div>
|
| 2199 |
-
</div>
|
| 2200 |
-
<div class="meta">
|
| 2201 |
-
<span class="badge">RANGE:${rangeTxt}${rangeSuffix}</span>
|
| 2202 |
-
<span class="badge">REL:${relTxt}</span>
|
| 2203 |
-
<span class="badge">DWELL:${dwellTxt}</span>
|
| 2204 |
-
<span class="badge">P(k):${pkTxt}</span>
|
| 2205 |
-
<span class="badge">AIM:${escapeHtml(d.aim?.label || "center")}</span>
|
| 2206 |
-
</div>
|
| 2207 |
-
`;
|
| 2208 |
|
| 2209 |
-
|
| 2210 |
-
|
| 2211 |
-
|
| 2212 |
-
|
| 2213 |
-
|
| 2214 |
-
|
| 2215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2216 |
|
| 2217 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2218 |
});
|
| 2219 |
}
|
| 2220 |
|
|
|
|
| 146 |
const objCount = $("#objCount");
|
| 147 |
const featureTable = $("#featureTable");
|
| 148 |
const selId = $("#selId");
|
| 149 |
+
const checkEnableGPT = $("#enableGPTToggle");
|
| 150 |
|
| 151 |
+
const trackCount = $("#trackCount");
|
| 152 |
+
const frameTrackList = $("#frameTrackList");
|
| 153 |
+
// Removed old summary references
|
| 154 |
const summaryTable = $("#summaryTable");
|
| 155 |
const mMaxP = $("#mMaxP");
|
| 156 |
const mReqP = $("#mReqP");
|
|
|
|
| 879 |
// Add depth_estimator parameter for depth processing
|
| 880 |
const enableDepthToggle = document.getElementById("enableDepthToggle");
|
| 881 |
const useLegacyDepth = enableDepthToggle && enableDepthToggle.checked;
|
| 882 |
+
const useGPT = checkEnableGPT && checkEnableGPT.checked;
|
| 883 |
|
| 884 |
form.append("depth_estimator", useLegacyDepth ? "depth" : "");
|
| 885 |
form.append("enable_depth", useLegacyDepth ? "true" : "false");
|
| 886 |
+
form.append("enable_gpt", useGPT ? "true" : "false");
|
| 887 |
|
| 888 |
// Submit async job
|
| 889 |
setHfStatus(`submitting ${mode} job...`);
|
|
|
|
| 1932 |
// Clear previous detections before running new detection
|
| 1933 |
state.detections = [];
|
| 1934 |
state.selectedId = null;
|
| 1935 |
+
renderFrameTrackList();
|
| 1936 |
renderFrameOverlay();
|
| 1937 |
+
// renderSummary(); // Removed
|
| 1938 |
renderFeatures(null);
|
| 1939 |
renderTrade();
|
| 1940 |
|
|
|
|
| 2028 |
|
| 2029 |
// pick default selection
|
| 2030 |
state.selectedId = state.detections[0]?.id || null;
|
| 2031 |
+
renderFrameTrackList();
|
| 2032 |
renderFrameOverlay();
|
| 2033 |
+
// renderSummary(); // Removed
|
| 2034 |
renderFeatures(getSelected());
|
| 2035 |
renderTrade();
|
| 2036 |
|
|
|
|
| 2165 |
}
|
| 2166 |
|
| 2167 |
// ========= Rendering: Object list, features, summary table =========
|
| 2168 |
+
// ========= Track Cards & Interaction =========
|
| 2169 |
+
function selectObject(id) {
|
| 2170 |
+
state.selectedId = id;
|
| 2171 |
+
|
| 2172 |
+
// Highlight Card
|
| 2173 |
+
$$(".track-card").forEach(el => el.classList.remove("active"));
|
| 2174 |
+
const card = document.getElementById("card-" + id);
|
| 2175 |
+
if (card) {
|
| 2176 |
+
// card.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
| 2177 |
+
card.classList.add("active");
|
| 2178 |
}
|
| 2179 |
|
| 2180 |
+
// Highlight BBox (via Overlay)
|
| 2181 |
+
renderFrameOverlay();
|
| 2182 |
+
// Highlight Radar uses state.selectedId, loops automatically
|
| 2183 |
+
}
|
| 2184 |
|
| 2185 |
+
function renderFrameTrackList() {
|
| 2186 |
+
if (!frameTrackList || !trackCount) return;
|
| 2187 |
+
frameTrackList.innerHTML = "";
|
|
|
|
| 2188 |
|
| 2189 |
+
const dets = state.detections || [];
|
| 2190 |
+
trackCount.textContent = dets.length;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2191 |
|
| 2192 |
+
if (dets.length === 0) {
|
| 2193 |
+
frameTrackList.innerHTML = '<div style="font-style:italic; color:var(--text-dim); text-align:center; margin-top:20px;">No objects tracked.</div>';
|
| 2194 |
+
return;
|
| 2195 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2196 |
|
| 2197 |
+
dets.forEach((det, i) => {
|
| 2198 |
+
// ID: T01, T02...
|
| 2199 |
+
// Ensure ID exists
|
| 2200 |
+
const id = det.id || `T${String(i + 1).padStart(2, '0')}`;
|
| 2201 |
+
|
| 2202 |
+
// Resolve Range/Bearing
|
| 2203 |
+
let rangeStr = "---";
|
| 2204 |
+
let bearingStr = "---";
|
| 2205 |
+
|
| 2206 |
+
if (det.gpt_distance_m) {
|
| 2207 |
+
rangeStr = `${det.gpt_distance_m}m (GPT)`;
|
| 2208 |
+
} else if (det.depth_est_m) {
|
| 2209 |
+
rangeStr = `${Math.round(det.depth_est_m)}m (Lidar)`;
|
| 2210 |
+
} else {
|
| 2211 |
+
// Fallback
|
| 2212 |
+
if (det.box) {
|
| 2213 |
+
const [x1, y1, x2, y2] = det.box;
|
| 2214 |
+
const area = ((x2 - x1) * (y2 - y1)) / (state.frame.w * state.frame.h);
|
| 2215 |
+
const est = clamp(200 / Math.sqrt(Math.max(1e-6, area)), 50, 6000);
|
| 2216 |
+
rangeStr = `~${Math.round(est)}m (Est)`;
|
| 2217 |
+
}
|
| 2218 |
+
}
|
| 2219 |
+
|
| 2220 |
+
if (det.gpt_direction) {
|
| 2221 |
+
bearingStr = det.gpt_direction;
|
| 2222 |
+
}
|
| 2223 |
|
| 2224 |
+
const card = document.createElement("div");
|
| 2225 |
+
card.className = "track-card";
|
| 2226 |
+
if (state.selectedId === id) card.classList.add("active");
|
| 2227 |
+
card.id = `card-${id}`;
|
| 2228 |
+
card.onclick = () => selectObject(id);
|
| 2229 |
+
|
| 2230 |
+
const desc = det.gpt_description
|
| 2231 |
+
? `<div class="track-card-body"><span class="gpt-text">${det.gpt_description}</span></div>`
|
| 2232 |
+
: ""; // No description, hide body
|
| 2233 |
+
|
| 2234 |
+
const gptBadge = (det.gpt_distance_m || det.gpt_description)
|
| 2235 |
+
? `<span class="gpt-badge">GPT</span>`
|
| 2236 |
+
: "";
|
| 2237 |
+
|
| 2238 |
+
card.innerHTML = `
|
| 2239 |
+
<div class="track-card-header">
|
| 2240 |
+
<span>${id} · ${det.label} ${gptBadge}</span>
|
| 2241 |
+
<span class="badgemini">${(det.score * 100).toFixed(0)}%</span>
|
| 2242 |
+
</div>
|
| 2243 |
+
<div class="track-card-meta">
|
| 2244 |
+
RANGE: ${rangeStr} | BEARING: ${bearingStr}
|
| 2245 |
+
</div>
|
| 2246 |
+
${desc}
|
| 2247 |
+
`;
|
| 2248 |
+
frameTrackList.appendChild(card);
|
| 2249 |
});
|
| 2250 |
}
|
| 2251 |
|
app.py
CHANGED
|
@@ -146,6 +146,7 @@ async def detect_endpoint(
|
|
| 146 |
detector: str = Form("hf_yolov8"),
|
| 147 |
segmenter: str = Form("sam3"),
|
| 148 |
enable_depth: bool = Form(False),
|
|
|
|
| 149 |
):
|
| 150 |
"""
|
| 151 |
Main detection endpoint.
|
|
@@ -289,6 +290,7 @@ async def detect_async_endpoint(
|
|
| 289 |
depth_estimator: str = Form("depth"),
|
| 290 |
depth_scale: float = Form(25.0),
|
| 291 |
enable_depth: bool = Form(False),
|
|
|
|
| 292 |
):
|
| 293 |
if mode not in VALID_MODES:
|
| 294 |
raise HTTPException(
|
|
@@ -346,6 +348,7 @@ async def detect_async_endpoint(
|
|
| 346 |
depth_estimator_name=active_depth,
|
| 347 |
depth_scale=depth_scale,
|
| 348 |
enable_depth_estimator=enable_depth,
|
|
|
|
| 349 |
)
|
| 350 |
cv2.imwrite(str(first_frame_path), processed_frame)
|
| 351 |
except Exception:
|
|
|
|
| 146 |
detector: str = Form("hf_yolov8"),
|
| 147 |
segmenter: str = Form("sam3"),
|
| 148 |
enable_depth: bool = Form(False),
|
| 149 |
+
enable_gpt: bool = Form(False),
|
| 150 |
):
|
| 151 |
"""
|
| 152 |
Main detection endpoint.
|
|
|
|
| 290 |
depth_estimator: str = Form("depth"),
|
| 291 |
depth_scale: float = Form(25.0),
|
| 292 |
enable_depth: bool = Form(False),
|
| 293 |
+
enable_gpt: bool = Form(False),
|
| 294 |
):
|
| 295 |
if mode not in VALID_MODES:
|
| 296 |
raise HTTPException(
|
|
|
|
| 348 |
depth_estimator_name=active_depth,
|
| 349 |
depth_scale=depth_scale,
|
| 350 |
enable_depth_estimator=enable_depth,
|
| 351 |
+
enable_gpt=enable_gpt,
|
| 352 |
)
|
| 353 |
cv2.imwrite(str(first_frame_path), processed_frame)
|
| 354 |
except Exception:
|
inference.py
CHANGED
|
@@ -404,6 +404,7 @@ def process_first_frame(
|
|
| 404 |
depth_estimator_name: Optional[str] = None,
|
| 405 |
depth_scale: Optional[float] = None,
|
| 406 |
enable_depth_estimator: bool = False,
|
|
|
|
| 407 |
) -> Tuple[np.ndarray, List[Dict[str, Any]]]:
|
| 408 |
frame, _, _, _ = extract_first_frame(video_path)
|
| 409 |
if mode == "segmentation":
|
|
@@ -426,8 +427,8 @@ def process_first_frame(
|
|
| 426 |
_DEPTH_SCALE if depth_scale is None else depth_scale,
|
| 427 |
)
|
| 428 |
|
| 429 |
-
# 2. GPT-based Distance/Direction Estimation (
|
| 430 |
-
if
|
| 431 |
# We need to save the frame temporarily to pass to GPT (or refactor gpt_distance to take buffer)
|
| 432 |
# For now, write to temp file
|
| 433 |
try:
|
|
|
|
| 404 |
depth_estimator_name: Optional[str] = None,
|
| 405 |
depth_scale: Optional[float] = None,
|
| 406 |
enable_depth_estimator: bool = False,
|
| 407 |
+
enable_gpt: bool = False,
|
| 408 |
) -> Tuple[np.ndarray, List[Dict[str, Any]]]:
|
| 409 |
frame, _, _, _ = extract_first_frame(video_path)
|
| 410 |
if mode == "segmentation":
|
|
|
|
| 427 |
_DEPTH_SCALE if depth_scale is None else depth_scale,
|
| 428 |
)
|
| 429 |
|
| 430 |
+
# 2. GPT-based Distance/Direction Estimation (Explicitly enabled)
|
| 431 |
+
if enable_gpt:
|
| 432 |
# We need to save the frame temporarily to pass to GPT (or refactor gpt_distance to take buffer)
|
| 433 |
# For now, write to temp file
|
| 434 |
try:
|