← back to Dw Photo Capture
Batch capture: harden against camera death + low-confidence SKU
23bf80403e735243d5347766100a354075aa4391 · 2026-09-19 09:31:21 -0700 · Steve Abrams
- Camera 'ended' → toast + drop to retake gate (no silent frozen-frame scoring)
- gateTick stops scoring when !camLive (no frozen-frame auto-captures)
- Low-confidence guessed SKU → operator must confirm; never auto-fires/auto-saves
- Add batchDone flag so auto-fire stops at the batch target
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Xadpnv4YPkFPKu6jDygAH
Files touched
Diff
commit 23bf80403e735243d5347766100a354075aa4391
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Sep 19 09:31:21 2026 -0700
Batch capture: harden against camera death + low-confidence SKU
- Camera 'ended' → toast + drop to retake gate (no silent frozen-frame scoring)
- gateTick stops scoring when !camLive (no frozen-frame auto-captures)
- Low-confidence guessed SKU → operator must confirm; never auto-fires/auto-saves
- Add batchDone flag so auto-fire stops at the batch target
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Xadpnv4YPkFPKu6jDygAH
---
public/batch.html | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/public/batch.html b/public/batch.html
index b9890ae..3eb399f 100644
--- a/public/batch.html
+++ b/public/batch.html
@@ -261,7 +261,8 @@ async function startCameraInto(videoEl){
track=stream.getVideoTracks()[0];
videoEl.srcObject=stream;
videoEl.onloadedmetadata=()=>{ videoEl.play().catch(()=>{}); };
- track.addEventListener('ended',()=>{ camLive=false; setCamDot(); });
+ track.addEventListener('ended',()=>{ camLive=false; setCamDot();
+ if(!$('#vShoot').hidden){ toast('⚠ Camera stopped — tap “Turn on camera”'); $('#gate').hidden=false; setState('retake','Camera stopped — tap to restart'); } });
camLive=true; setCamDot();
requestWake();
return true;
@@ -396,7 +397,7 @@ $('#cSave').onclick=()=>{
// ════════════════════════ SHOOT VIEW ════════════════════════
let gateTimer=null, prevLuma=null, stillCount=0, lastFireAt=0, busy=false, backoffUntil=0;
-let lastGate=null, curSku='', skuConf=1, lastOcrKey='', ocrPending=false, batchCost=0;
+let lastGate=null, curSku='', skuConf=1, lastOcrKey='', ocrPending=false, batchCost=0, batchDone=false;
function enterShoot(){
show('vShoot');
$('#hBatch').textContent=(session.vendor+' '+(session.collection||'')).trim();
@@ -422,7 +423,7 @@ function drawShootOverlayLoop(){
// ── the gate loop: motion + quality + drift, every tickMs on the downscaled canvas ──
function startGateLoop(){ if(gateTimer) clearInterval(gateTimer); gateTimer=setInterval(gateTick, CFG.tickMs); }
function gateTick(){
- if(busy||$('#vShoot').hidden) return;
+ if(busy||$('#vShoot').hidden||!camLive) return; // camera dead → stop scoring (no frozen-frame captures)
const v=$('#v'); const g=gateGrab(v); if(!g){ return; } lastGate=g;
const whole=stats(g.data,null); // whole-frame: motion detection ONLY
const tgt=stats(g.data, CAL_TARGET); // the sample region
@@ -462,11 +463,12 @@ function gateTick(){
if(still && !ocrPending && !fails.includes('no sample in frame')){ maybeOcr(g); }
// ready?
const ready = still && fails.length===0 && (curSku||$('#skuInput').value.trim());
- setState(ready?'ready':(fails.length?'retake':'wait'), ready?'✓ Ready — hold still':(fails.length?FRIENDLY_FAIL(fails[0]):'Put a sample in the box'));
+ const lowSku = !!curSku && skuConf<CFG.skuConfMin; // OCR guessed but wasn't sure → operator must confirm
+ setState(ready?'ready':(fails.length?'retake':'wait'), ready?(lowSku?'✓ Ready — check the code first':'✓ Ready — hold still'):(fails.length?FRIENDLY_FAIL(fails[0]):'Put a sample in the box'));
$('#shutter').disabled = busy;
- // hands-free auto-fire
+ // hands-free auto-fire — never auto-save a low-confidence guessed SKU, and stop at the target
const now=Date.now();
- if(ready && $('#autoFire').checked && now-lastFireAt>CFG.minCadenceMs && now>backoffUntil){
+ if(ready && !lowSku && !batchDone && $('#autoFire').checked && now-lastFireAt>CFG.minCadenceMs && now>backoffUntil){
capture('auto', fails);
}
}
← 721a8a6 auto-data-snapshot: 2026-09-19T08:55:30 (2 data files) — dat
·
back to Dw Photo Capture
·
DEVICE-TEST: batch checklist for PSku→Info two-shot flow, si 243f75a →