feat: add engine auto-recompilation, mortality detection, multi-execution batching, and dashboard updates
- Add engine_utils for TensorRT compatibility verification, auto-recompilation from .pt models, and YAML auto-updates - Add mortality detection pipeline (mortality.py, test_run_mortality.sh, mortality_config.yaml) - Add multi-execution batch modes (parallel_processes, tensor_batching, hybrid) in batch_runner.py - Add daily test run automation scripts and video processing runners - Add dashboard REST API, live stream endpoints, and web UI templates - Clean up git tracking by ignoring __pycache__, .pyc, and build artifacts
This commit is contained in:
1 parent
c05f3d98d0
commit
010f6e1493
97 files changed
+3361
-643
No files matched your search
+99
-18
@@ -12,7 +12,7 @@ body{font-family:system-ui,monospace;background:#0f0f14;color:#e0e0e0;overflow:h
|
||||
#top-bar .dot{display:inline-block;width:6px;height:6px;border-radius:50%;margin:0 6px}
|
||||
#top-bar .dot.online{background:#80dc5a;box-shadow:0 0 4px #80dc5a}
|
||||
#top-bar .dot.offline{background:#555}
|
||||
#main{flex:1;display:flex}
|
||||
#main{flex:1;display:flex;overflow:hidden}
|
||||
#frame-area{flex:1;background:#000;display:flex;align-items:center;justify-content:center;position:relative}
|
||||
#frame-area img{max-width:100%;max-height:100%;object-fit:contain}
|
||||
#frame-area .overlay{position:absolute;top:12px;left:12px;pointer-events:none}
|
||||
@@ -20,7 +20,7 @@ body{font-family:system-ui,monospace;background:#0f0f14;color:#e0e0e0;overflow:h
|
||||
#frame-area .overlay .tag.live{background:#1a3a2a;color:#80dc5a}
|
||||
#frame-area .overlay .tag.off{background:#333;color:#888}
|
||||
#frame-area .overlay .count{font-size:36px;font-weight:900;color:#fff;text-shadow:0 0 16px rgba(0,0,0,.8)}
|
||||
#sidebar{width:280px;background:#12121a;padding:16px;overflow-y:auto;flex-shrink:0;display:flex;flex-direction:column;gap:12px}
|
||||
#sidebar{width:290px;background:#12121a;padding:16px;overflow-y:auto;flex-shrink:0;display:flex;flex-direction:column;gap:12px}
|
||||
#sidebar .stat{padding:12px;background:#16161e;border-radius:8px}
|
||||
#sidebar .stat label{display:block;font-size:9px;color:#555;text-transform:uppercase;letter-spacing:1px;margin-bottom:2px}
|
||||
#sidebar .stat .val{font-size:22px;font-weight:700}
|
||||
@@ -32,10 +32,27 @@ body{font-family:system-ui,monospace;background:#0f0f14;color:#e0e0e0;overflow:h
|
||||
#sidebar .cam-list .cam-row.act{background:#1a3a2a;color:#80dc5a;font-weight:700}
|
||||
#sidebar .cam-list .cam-row .cam-total{font-size:11px;color:#666}
|
||||
#sidebar .cam-list .cam-row.act .cam-total{color:#5a9a4a}
|
||||
#db-panel{padding:12px;background:#16161e;border-radius:8px;font-size:11px;max-height:200px;overflow-y:auto}
|
||||
#db-panel h3{font-size:10px;color:#555;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px}
|
||||
#db-panel, #mort-panel{padding:12px;background:#16161e;border-radius:8px;font-size:11px}
|
||||
#db-panel h3, #mort-panel h3{font-size:10px;color:#555;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;display:flex;justify-content:space-between;align-items:center}
|
||||
#db-panel .db-row{display:flex;justify-content:space-between;padding:2px 0;color:#888}
|
||||
#db-panel .db-row .db-total{color:#aaa;font-weight:600}
|
||||
|
||||
/* Mortality UI */
|
||||
.mort-badge{background:#2a1518;color:#ff6b81;border:1px solid #ff4757;font-weight:700;padding:2px 6px;border-radius:4px;font-size:10px}
|
||||
.mort-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:6px}
|
||||
.mort-card{background:#0e0e14;border-radius:6px;overflow:hidden;cursor:pointer;border:1px solid #222;transition:all .15s;text-align:left}
|
||||
.mort-card:hover{border-color:#ff4757;transform:translateY(-1px)}
|
||||
.mort-card img{width:100%;height:65px;object-fit:cover;display:block;background:#000}
|
||||
.mort-card .info{padding:4px 6px;display:flex;justify-content:space-between;align-items:center;font-size:10px;color:#888}
|
||||
.mort-card .info .cnt{color:#ff9f43;font-weight:700}
|
||||
|
||||
/* Fullscreen Image Modal */
|
||||
#mort-modal{display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,.88);z-index:999;align-items:center;justify-content:center;flex-direction:column;backdrop-filter:blur(4px)}
|
||||
#mort-modal.active{display:flex}
|
||||
#mort-modal img{max-width:90vw;max-height:85vh;border-radius:8px;border:1px solid #333;box-shadow:0 0 32px rgba(0,0,0,.9);object-fit:contain}
|
||||
#mort-modal .close-btn{position:absolute;top:20px;right:28px;font-size:32px;color:#aaa;cursor:pointer;background:none;border:none;line-height:1;transition:color .15s}
|
||||
#mort-modal .close-btn:hover{color:#fff}
|
||||
#mort-modal .caption{margin-top:12px;font-size:13px;color:#eee;background:#16161e;padding:6px 14px;border-radius:6px;border:1px solid #333}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -60,11 +77,30 @@ body{font-family:system-ui,monospace;background:#0f0f14;color:#e0e0e0;overflow:h
|
||||
<div class="stat"><label>Frame</label><div class="val" id="s-frame">--</div></div>
|
||||
<div class="stat"><label>Motion</label><div class="val" id="s-speed">--</div></div>
|
||||
<div class="cam-list" id="cam-list"></div>
|
||||
|
||||
<!-- Mortality / Carcass Detection Panel -->
|
||||
<div class="mort-panel" id="mort-panel" style="display:none">
|
||||
<h3>
|
||||
<span>☠️ Mortality Scans</span>
|
||||
<span class="mort-badge" id="mort-header-badge">0</span>
|
||||
</h3>
|
||||
<div id="mort-summary-text" style="color:#777;font-size:10px;margin-bottom:4px"></div>
|
||||
<div class="mort-grid" id="mort-grid"></div>
|
||||
</div>
|
||||
|
||||
<div class="db-panel" id="db-panel">
|
||||
<h3>📊 History</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal for high-res mortality preview -->
|
||||
<div id="mort-modal" onclick="closeMortModal(event)">
|
||||
<button class="close-btn" onclick="closeMortModal(event)">×</button>
|
||||
<img id="mort-modal-img" src="" alt="Mortality detection preview" onclick="event.stopPropagation()">
|
||||
<div class="caption" id="mort-modal-caption" onclick="event.stopPropagation()"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var POLL_MS = {{ poll_ms }};
|
||||
var SHM = "{{ shm_dir }}";
|
||||
@@ -80,6 +116,8 @@ function boot() {
|
||||
setInterval(updateClock, 1000);
|
||||
loadHistory();
|
||||
setInterval(loadHistory, 60000);
|
||||
loadMortality();
|
||||
setInterval(loadMortality, 30000);
|
||||
updateClock();
|
||||
}
|
||||
|
||||
@@ -90,7 +128,7 @@ function loadCameras() {
|
||||
if (cameras.length && !activeCam) selectCam(cameras[0]);
|
||||
detectActive();
|
||||
if (!cameras.length) showWaiting("no cameras detected");
|
||||
});
|
||||
}).catch(function() {});
|
||||
updateSidebar();
|
||||
}
|
||||
|
||||
@@ -106,7 +144,7 @@ function detectActive() {
|
||||
lastFramePerCam[cam] = s.frame_index;
|
||||
if (activeCam !== cam) selectCam(cam);
|
||||
}
|
||||
}).finally(function() { if (pending === 0) renderCamList(); });
|
||||
}).catch(function() {}).finally(function() { if (pending === 0) renderCamList(); });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,13 +170,13 @@ function poll() {
|
||||
document.getElementById("s-inside").textContent = s.inside_box_count;
|
||||
document.getElementById("s-tracks").textContent = s.track_count;
|
||||
document.getElementById("s-frame").textContent = s.frame_index;
|
||||
document.getElementById("s-speed").textContent = s.smoothed_speed.toFixed(1);
|
||||
document.getElementById("s-speed").textContent = Number(s.smoothed_speed).toFixed(1);
|
||||
var tag = document.getElementById("cam-tag");
|
||||
var status = s.backward_active ? "BACKWARD STOP" : "RUNNING";
|
||||
tag.textContent = activeCam + " \u2022 " + status;
|
||||
tag.className = "tag " + (s.backward_active ? "off" : "live");
|
||||
document.getElementById("cam-count").textContent = s.total_entered_count;
|
||||
});
|
||||
}).catch(function() { setOffline(); });
|
||||
}
|
||||
|
||||
function refreshNow() {
|
||||
@@ -153,8 +191,11 @@ function showWaiting(msg) {
|
||||
}
|
||||
|
||||
function setOffline() {
|
||||
document.getElementById("cam-tag").textContent = activeCam + " \u2022 OFFLINE";
|
||||
document.getElementById("cam-tag").className = "tag off";
|
||||
var tag = document.getElementById("cam-tag");
|
||||
if (tag && activeCam) {
|
||||
tag.textContent = activeCam + " \u2022 OFFLINE";
|
||||
tag.className = "tag off";
|
||||
}
|
||||
}
|
||||
|
||||
function updateSidebar() {}
|
||||
@@ -179,19 +220,17 @@ function loadHistory() {
|
||||
var panel = document.getElementById("db-panel");
|
||||
panel.style.display = "block";
|
||||
|
||||
// per-day totals
|
||||
fetch("/api/db/history").then(r => r.json()).then(rows => {
|
||||
var html = "<h3>📊 History</h3>";
|
||||
if (rows.length) {
|
||||
if (rows && rows.length) {
|
||||
html += rows.map(function(r) {
|
||||
return '<div class="db-row"><span>' + r.date + ' ' + r.location + '</span>' +
|
||||
'<span class="db-total">' + r.total.toLocaleString() + '</span></div>';
|
||||
return '<div class="db-row"><span>' + r.date + ' ' + (r.location || '') + '</span>' +
|
||||
'<span class="db-total">' + Number(r.total || 0).toLocaleString() + '</span></div>';
|
||||
}).join("");
|
||||
}
|
||||
panel.innerHTML = html;
|
||||
});
|
||||
}).catch(function() {});
|
||||
|
||||
// per-camera breakdown for current date
|
||||
if (RUN_DATE && RUN_DATE !== "today") {
|
||||
fetch("/api/db/date/" + RUN_DATE).then(r => r.json()).then(data => {
|
||||
if (!data.cameras || !data.cameras.length) return;
|
||||
@@ -199,13 +238,55 @@ function loadHistory() {
|
||||
html += '<h3 style="margin-top:12px">📹 ' + RUN_DATE + ' (' + (data.total.total || 0).toLocaleString() + ')</h3>';
|
||||
data.cameras.forEach(function(c) {
|
||||
html += '<div class="db-row"><span>' + c.camera_id + '</span>' +
|
||||
'<span class="db-total">' + c.total_entered.toLocaleString() + '</span></div>';
|
||||
'<span class="db-total">' + Number(c.total_entered || 0).toLocaleString() + '</span></div>';
|
||||
});
|
||||
panel.innerHTML = html;
|
||||
});
|
||||
}).catch(function() {});
|
||||
}
|
||||
}
|
||||
|
||||
function loadMortality() {
|
||||
fetch("/api/mortality/latest").then(r => r.ok ? r.json() : null).then(data => {
|
||||
var panel = document.getElementById("mort-panel");
|
||||
if (!data || data.error || !data.results || !data.results.length) {
|
||||
panel.style.display = "none";
|
||||
return;
|
||||
}
|
||||
panel.style.display = "block";
|
||||
var totalCarcasses = data.total_mortality_count !== undefined ? data.total_mortality_count : data.results.reduce((acc, x) => acc + (x.count || 0), 0);
|
||||
document.getElementById("mort-header-badge").textContent = totalCarcasses + " detected";
|
||||
document.getElementById("mort-summary-text").textContent = (data.date ? data.date + " \u2022 " : "") + data.results.length + " photo(s) scanned";
|
||||
|
||||
var grid = document.getElementById("mort-grid");
|
||||
grid.innerHTML = data.results.map(function(item) {
|
||||
var imgSrc = "/api/mortality/image/" + encodeURIComponent(item.output_image || item.input_image);
|
||||
var title = (item.output_image || item.input_image || "scan").replace(/^output_/, "");
|
||||
var count = item.count !== undefined ? item.count : (item.detections ? item.detections.length : 0);
|
||||
return '<div class="mort-card" onclick="openMortModal(\'' + imgSrc + '\', \'' + title + ' (' + count + ' carcasses)\')">' +
|
||||
'<img src="' + imgSrc + '" alt="' + title + '" loading="lazy" onerror="this.style.opacity=0.3">' +
|
||||
'<div class="info">' +
|
||||
'<span style="max-width:70px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + title + '">' + title + '</span>' +
|
||||
'<span class="cnt">' + count + ' 💀</span>' +
|
||||
'</div></div>';
|
||||
}).join("");
|
||||
}).catch(function() {});
|
||||
}
|
||||
|
||||
function openMortModal(src, caption) {
|
||||
var modal = document.getElementById("mort-modal");
|
||||
document.getElementById("mort-modal-img").src = src;
|
||||
document.getElementById("mort-modal-caption").textContent = caption || "";
|
||||
modal.classList.add("active");
|
||||
}
|
||||
|
||||
function closeMortModal(event) {
|
||||
document.getElementById("mort-modal").classList.remove("active");
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", function(e) {
|
||||
if (e.key === "Escape") closeMortModal();
|
||||
});
|
||||
|
||||
setTimeout(function() { if (activeCam) refreshNow(); }, 500);
|
||||
boot();
|
||||
</script>
|
||||
|
||||
Reference in new issue
Block a user