Skip to content

Commit 22bde9e

Browse files
committed
Demo: add capture button (saves native frame PNG + detection JSON) and request 1080p
Requests ideal 1920x1080 from getUserMedia. The Capture button saves the exact frame the decoder processes plus a JSON of located candidates and decode results — so a missed scan is a reproducible bug report.
1 parent 69880d3 commit 22bde9e

1 file changed

Lines changed: 37 additions & 1 deletion

File tree

‎web/index.html‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ <h2>Live camera detection</h2>
100100
<p class="sub" style="margin-bottom:.5rem">Locates every code's <em>position</em> each frame (no decode), then decodes periodically — the two-stage pipeline a live feed needs.</p>
101101
<button id="cam-btn">Start camera</button>
102102
<button id="cam-stop" class="secondary" disabled>Stop</button>
103+
<button id="cam-capture" class="secondary" disabled title="Save the exact frame the decoder sees + a JSON of what was detected">Capture frame + data</button>
103104
<div class="stat hud" id="cam-stat"></div>
104105
<div class="stage" id="cam-stage" hidden>
105106
<video id="cam-video" playsinline muted></video>
@@ -431,18 +432,53 @@ <h2>Decode an image</h2>
431432

432433
document.getElementById('cam-btn').onclick = async () => {
433434
try {
434-
stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
435+
stream = await navigator.mediaDevices.getUserMedia({
436+
video: { facingMode: 'environment', width: { ideal: 1920 }, height: { ideal: 1080 } }
437+
});
435438
} catch (e) { document.getElementById('cam-stat').textContent = 'Camera error: ' + e.message; return; }
436439
video.srcObject = stream; await video.play();
437440
document.getElementById('cam-stage').hidden = false;
438441
document.getElementById('cam-btn').disabled = true;
439442
document.getElementById('cam-stop').disabled = false;
443+
document.getElementById('cam-capture').disabled = false;
440444
running = true; requestAnimationFrame(loop);
441445
};
442446
document.getElementById('cam-stop').onclick = () => {
443447
running = false; stream && stream.getTracks().forEach(t => t.stop());
444448
document.getElementById('cam-btn').disabled = false;
445449
document.getElementById('cam-stop').disabled = true;
450+
document.getElementById('cam-capture').disabled = true;
451+
};
452+
453+
// Capture the exact frame the decoder sees + a JSON of the detection state, so a
454+
// missed scan becomes a reproducible bug report (hand both files back).
455+
document.getElementById('cam-capture').onclick = () => {
456+
const vw = video.videoWidth, vh = video.videoHeight;
457+
if (!vw) return;
458+
const pw = Math.min(vw, 1920), ph = Math.round(pw * vh / vw);
459+
scratch.width = pw; scratch.height = ph;
460+
const sctx = scratch.getContext('2d', { willReadFrequently: true });
461+
sctx.drawImage(video, 0, 0, pw, ph);
462+
const img = sctx.getImageData(0, 0, pw, ph);
463+
const frameArea = pw * ph;
464+
const cands = locate(img).filter(c => { const b = bbox(c.corners); return (b.x1 - b.x0) * (b.y1 - b.y0) < 0.5 * frameArea; });
465+
const seen = new Set(), decoded = [];
466+
const add = (r, box) => { const k = r.symbology + '|' + r.text; if (!seen.has(k)) { seen.add(k); decoded.push({ ...r, box }); } };
467+
decode(img).forEach(r => add(r, null));
468+
for (const c of cands) { const b = bbox(c.corners); const crop = cropRGBA(img, b.x0 - 16, b.y0 - 16, b.x1 + 16, b.y1 + 16); if (crop) decode(crop).forEach(r => add(r, b)); }
469+
const report = {
470+
capturedAt: new Date().toISOString(),
471+
video: { width: vw, height: vh },
472+
processed: { width: pw, height: ph },
473+
locate: cands.map(c => ({ family: c.family, corners: c.corners.map(p => [Math.round(p[0]), Math.round(p[1])]) })),
474+
decode: decoded.map(r => ({ symbology: r.symbology, text: r.text, box: r.box || null })),
475+
userAgent: navigator.userAgent,
476+
};
477+
const n = (window.__capN = (window.__capN || 0) + 1);
478+
scratch.toBlob(b => dl(`anyd-capture-${n}.png`, b));
479+
setTimeout(() => dl(`anyd-capture-${n}.json`, new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' })), 300);
480+
document.getElementById('cam-result').textContent =
481+
`captured frame ${pw}×${ph} — ${cands.length} located, ${decoded.length} decoded (saved PNG + JSON)`;
446482
};
447483

448484
function loop(ts) {

0 commit comments

Comments
 (0)