Confirming Barcodes Across Missed Camera Frames
A camera can show a live image while a barcode scanner never completes a capture. I encountered that failure in Scantrast, an app that reads food-product barcodes and interprets nutrition against a person's targets. The
A camera can show a live image while a barcode scanner never completes a capture. I encountered that failure in Scantrast, an app that reads food-product barcodes and interprets nutrition against a person's targets. The problem was in the rule used to confirm a decoded result.
The original confirmation rule required consecutive matching reads. An unreadable frame cleared the candidate. That seemed reasonable as a way to reject an isolated incorrect decode, and automated tests could establish that the rule worked as specified.
The sequence that mattered on the phone was different:
frame 1: readable code A
frame 2: no readable code
frame 3: readable code A
A and B in this article are symbolic labels for valid, normalized barcode identities, not real product data. Under the consecutive-read rule, frame 2 erased the progress made on frame 1. Frame 3 started over. Repeated interruptions could leave the camera looking functional while confirmation never finished.
The acceptance requirement needed to tolerate an intermittent observation. We changed it to two matching sightings within a two-second window. An unreadable frame could occur between them. A different valid code started a new candidate, and an expired candidate could not contribute an old sighting to a later confirmation.
Those boundaries matter. Keeping a candidate forever could combine observations made too far apart. Combining different codes would weaken identity. Counting the same image twice would provide less evidence than two sightings from distinct frames. The corrected requirement addressed all three concerns while allowing a missed read.
The two-second value is the choice made for this scanner, not a universal recommendation for cameras. A different capture rate, decoder or interaction could justify a different threshold. Two matching sightings are also a confirmation heuristic, not proof that a decoder can never produce a repeated wrong result.
Here is one way to explain the state machine. Assume an upstream frame gate calls this only for fresh frames, supplies a monotonic timestamp, and passes either a valid normalized code or no code. That gate is a separate responsibility. The real implementation separates frame tracking from barcode confirmation; this pseudocode focuses on the confirmation decision.
candidate = none
window = 2000 milliseconds
observe(code, now):
if candidate exists and now - candidate.startedAt > window:
candidate = none
if code is none:
return pending
if candidate is none or code != candidate.code:
candidate = { code, startedAt: now }
return pending
result = candidate.code
candidate = none
return confirmed(result)
An unreadable fresh frame leaves an unexpired candidate intact. A different code replaces it. A second matching code confirms only if the candidate is still within its window. On confirmation, the surrounding scan operation should handle delivery once and end or transition deliberately; repeatedly accepting results from a still-running loop is a separate concern.
Frame identity deserves its own tests. The camera's media time alone was an unsuitable way to assume a new image had arrived. The corrected Scantrast frame tracker uses composited-frame callbacks where available, with presented-frame counts and media time as fallbacks. These routes have different strengths; a fallback does not certify fresh physical images on every browser. The guard's job is to keep a frozen image from supplying both confirmation sightings.
Useful regression checks exercise sequences rather than only the confirmation count:
A fresh A, a miss, then another fresh A within the window should confirm. This is the observation that invalidated the original rule.
A single A followed by misses should remain pending, then expire. A later A should seed a new candidate rather than inherit the old sighting.
A, then B, then A should not confirm A from its first appearance. Each different valid identity starts a new candidate.
A repeated decode of a frozen frame should not reach confirmation. Fresh frames should remain usable even when the media timeline does not advance as expected.
At the window boundary, test the chosen comparison explicitly. The implementation expires a candidate after the window; avoid leaving exact-boundary behavior accidental.
Run the interrupted sequence through decoding and the scan loop as well as the small confirmation helper. A helper can behave correctly while a frame gate or asynchronous loop prevents it from receiving the expected observations.
The engineering record includes an unreadable frame between successful reads, confirmation expiry, duplicate-frame rejection and fresh-frame behavior with unchanged media time. The original regression checks were observed failing before the correction. That failing-before-fix observation connected the checks to the reported behavior, rather than merely showing that the revised code passed a newly written test.
Camera lifecycle introduces another class of acceptance conditions. Pausing, switching cameras, closing the dialog or backgrounding the app can interrupt acquisition, decoding or a product lookup. A result arriving after cancellation must not silently complete the previous interaction. A stream arriving after its request has been abandoned needs cleanup rather than ownership by a closed view.
Automated frame and stream doubles can exercise cancellation, timeouts and late results. Physical-device review is still needed for optics, focus, camera choice and actual image delivery. During this correction I confirmed capture worked in the preview on my phone. That is a bounded observation, not a claim that every camera, low-light condition or recovery sequence was tested successfully.
The practical lesson is to preserve the sequence that failed. A test for two uninterrupted successful reads can validate a rule while missing the activity that rule is supposed to support. Recording interruptions, identity changes, expiry and cancellation makes the intended behavior more explicit and gives regression checks something meaningful to protect.
This expands the scanner example in Context Matters. The Scantrast overview describes the product context; this article focuses on the confirmation and acceptance lesson.
I used AI assistance to draft and edit this article. I supplied the requirements, device observations and acceptance judgment; coding agents implemented changes and ran automated checks. The pseudocode above is an illustrative explanation, not a copy of production code.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.