feat: HUD shows pose rejections and detect resolution; detection long-edge configurable via nbx.detectLong

This commit is contained in:
2026-08-24 11:51:19 +10:00
parent f1830fc961
commit 44126ec3b4
+14 -6
View File
@@ -4,7 +4,7 @@
* *
* v3 ships two runtime-switchable tracking engines (HUD overlay, tap top third): * v3 ships two runtime-switchable tracking engines (HUD overlay, tap top third):
* opencv (default) — joint multi-marker board solve (solve-cv.js). All detected * opencv (default) — joint multi-marker board solve (solve-cv.js). All detected
* corners -> one solvePnP -> camera pose. Non-coplanar wall * corners -> one solvePnP -> camera pose. A non-coplanar
* crest removes planar ambiguity outright. * crest removes planar ambiguity outright.
* posit — the v2 per-marker POS-IT + fuseWorld path, kept intact for * posit — the v2 per-marker POS-IT + fuseWorld path, kept intact for
* on-table A/B until the new engine is confirmed. Delete later. * on-table A/B until the new engine is confirmed. Delete later.
@@ -86,6 +86,13 @@ const focalPxFor = (w, h) => (Math.max(w, h) / 2) / Math.tan(THREE.MathUtils.deg
* ~20Hz tracks hand movement fine and the 1e filter smooths between updates, * ~20Hz tracks hand movement fine and the 1e filter smooths between updates,
* while rendering stays at full frame rate. Override: localStorage 'nbx.detectHz'. */ * while rendering stays at full frame rate. Override: localStorage 'nbx.detectHz'. */
const DETECT_HZ = parseFloat(localStorage.getItem('nbx.detectHz')) || 20; const DETECT_HZ = parseFloat(localStorage.getItem('nbx.detectHz')) || 20;
/* Long-edge resolution of the detection canvas. At 960 a 60mm crest filling the
* usual framing already gets ~23px per marker cell, well above ArUco's needs —
* so raising this is NOT an obvious cure for crests that fail to decode, and it
* costs CPU roughly linearly (960 -> 1280 is +78% pixels). Exposed as a knob to
* test that empirically rather than assume: localStorage 'nbx.detectLong'. */
const DETECT_LONG = parseFloat(localStorage.getItem('nbx.detectLong')) || 960;
const DETECT_INTERVAL_MS = 1000 / DETECT_HZ; const DETECT_INTERVAL_MS = 1000 / DETECT_HZ;
let lastDetectAt = 0; let lastDetectAt = 0;
let fps = 0, fpsFrames = 0, fpsAt = 0; let fps = 0, fpsFrames = 0, fpsAt = 0;
@@ -99,7 +106,8 @@ function dbgStatus() {
const ws = netRef && netRef.ws ? ['conn','open','closing','closed'][netRef.ws.readyState] : 'none'; const ws = netRef && netRef.ws ? ['conn','open','closing','closed'][netRef.ws.readyState] : 'none';
return `ws ${ws} rx ${rxLog.join(',') || '-'}\n` + return `ws ${ws} rx ${rxLog.join(',') || '-'}\n` +
`scene ${sceneLoaded ? anchorCount + ' anchors' : 'NOT LOADED'} ghost recs ${activeGhosts.size}\n` + `scene ${sceneLoaded ? anchorCount + ' anchors' : 'NOT LOADED'} ghost recs ${activeGhosts.size}\n` +
`${fps} fps detect ${DETECT_HZ}Hz fov ${camera3 ? camera3.fov.toFixed(0) : '?'}\n`; `${fps} fps detect ${DETECT_HZ}Hz@${DETECT_LONG} fov ${camera3 ? camera3.fov.toFixed(0) : '?'}` +
`${cvSolver && cvSolver.lastReject ? ' REJ:' + cvSolver.lastReject : ''}\n`;
} }
function paintDbg() { if (dbgEl) dbgEl.textContent = dbgStatus() + dbgPose + dbgAim; } function paintDbg() { if (dbgEl) dbgEl.textContent = dbgStatus() + dbgPose + dbgAim; }
@@ -472,11 +480,11 @@ function loop(t) {
if (video && video.readyState >= 2 && (t - lastDetectAt) >= DETECT_INTERVAL_MS) { if (video && video.readyState >= 2 && (t - lastDetectAt) >= DETECT_INTERVAL_MS) {
lastDetectAt = t; lastDetectAt = t;
/* Downscale for detection speed, capping the LONG side. Fixing the WIDTH at /* Downscale for detection, capping the LONG side. Fixing the WIDTH quietly
* 960 quietly tripled the work on a portrait stream (960x1707 = 1.6M px vs * tripled the work on a portrait stream (960x1707 vs 960x540), which is most
* 960x540 = 0.5M), which is most of the cost of a frame. */ * of the cost of a frame. */
const vw = video.videoWidth, vh = video.videoHeight; const vw = video.videoWidth, vh = video.videoHeight;
const LONG = 960; const LONG = DETECT_LONG;
const W = vw >= vh ? LONG : Math.round(LONG * vw / vh); const W = vw >= vh ? LONG : Math.round(LONG * vw / vh);
const H = vw >= vh ? Math.round(LONG * vh / vw) : LONG; const H = vw >= vh ? Math.round(LONG * vh / vw) : LONG;
if (canvas2d.width !== W || canvas2d.height !== H) { canvas2d.width = W; canvas2d.height = H; } if (canvas2d.width !== W || canvas2d.height !== H) { canvas2d.width = W; canvas2d.height = H; }