Skip to content

Commit 609b50f

Browse files
MagicalTuxclaude
andcommitted
test: add scan1ddebug tool + curved-bottle EAN fixture
Diagnostic example that runs scan1d on a PNG, prints each recovered candidate's module count/confidence, and (given the known payload) its best-alignment Hamming distance to the true encoded pattern plus the EAN decode verdict. testdata/real_scene_ean_curved.png is the exact located box (256x96) from a real capture of an EAN-13 on a curved bottle: small (~2.4px/module), blurred, low contrast (brown-on-gold). scan1d recovers the pattern to within Hamming 4 of the truth but fails to decode — the errors sit in the thin single-module guard features that blur merges, and different scanlines corrupt different modules. Pins the real-world 1D decode-robustness gap for the edge-ratio fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 60a74b9 commit 609b50f

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

‎Cargo.toml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,7 @@ required-features = ["cli"]
5454
[[example]]
5555
name = "locdebug"
5656
required-features = ["cli"]
57+
58+
[[example]]
59+
name = "scan1ddebug"
60+
required-features = ["cli"]

‎examples/scan1ddebug.rs‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Diagnostic: what does scan1d recover on a real 1D photo, and why does EAN reject it?
2+
use anyd::codes::ean::EanDecoder;
3+
use anyd::output::Encoding;
4+
use anyd::scan1d::{ScanOptions, scan_lines};
5+
use anyd::traits::Decode;
6+
7+
fn main() {
8+
let path = std::env::args().nth(1).expect("usage: scan1ddebug <png>");
9+
let rgba = oxideav_png::decode_png_to_rgba(&std::fs::read(&path).unwrap()).unwrap();
10+
let (w, h) = (rgba.width as usize, rgba.height as usize);
11+
let luma: Vec<u8> = rgba
12+
.data
13+
.chunks_exact(4)
14+
.map(|p| ((p[0] as u32 * 299 + p[1] as u32 * 587 + p[2] as u32 * 114) / 1000) as u8)
15+
.collect();
16+
let frame = anyd::GrayFrame::new(&luma, w, h).unwrap();
17+
18+
// Reference: encode the known payload to compare recovered patterns against.
19+
let reference: Option<Vec<bool>> = std::env::args().nth(2).and_then(|digits| {
20+
use anyd::codes::ean::EanEncoder;
21+
use anyd::traits::Encode;
22+
let enc = EanEncoder::new();
23+
let sym = enc.build_ean13(&digits).ok()?;
24+
match enc.encode(&sym).ok()? {
25+
Encoding::Linear(p) => Some(p.modules),
26+
_ => None,
27+
}
28+
});
29+
if let Some(r) = &reference {
30+
println!("reference pattern: {} modules", r.len());
31+
}
32+
let as_str = |m: &[bool]| {
33+
m.iter()
34+
.map(|&b| if b { '#' } else { '.' })
35+
.collect::<String>()
36+
};
37+
38+
let opts = ScanOptions::default();
39+
let cands = scan_lines(&frame, &opts);
40+
println!("frame {w}x{h}: {} scan1d candidates", cands.len());
41+
let dec = EanDecoder::new();
42+
for (i, c) in cands.iter().enumerate() {
43+
let mods = c.pattern.modules.len();
44+
let quiet = c.pattern.quiet_zone;
45+
let res = dec.decode(&Encoding::Linear(c.pattern.clone()));
46+
let verdict = match res {
47+
Ok(s) => format!("OK -> {:?}", s.text()),
48+
Err(e) => format!("ERR {e}"),
49+
};
50+
// Best-alignment Hamming distance vs reference (slide the shorter over the longer).
51+
let hd = reference.as_ref().map(|r| {
52+
let (a, b) = (&c.pattern.modules, r);
53+
let (short, long) = if a.len() <= b.len() { (a, b) } else { (b, a) };
54+
let mut best = short.len();
55+
for off in 0..=(long.len() - short.len()) {
56+
let d = short
57+
.iter()
58+
.zip(&long[off..])
59+
.filter(|(x, y)| x != y)
60+
.count();
61+
best = best.min(d);
62+
}
63+
best
64+
});
65+
println!(
66+
" [{i}] conf={:.3} modules={mods} quiet={quiet} hamming={hd:?} :: {verdict}",
67+
c.confidence
68+
);
69+
if reference.is_some() && (93..=97).contains(&mods) {
70+
println!(" {}", as_str(&c.pattern.modules));
71+
}
72+
}
73+
if let Some(r) = &reference {
74+
println!(" REF {}", as_str(r));
75+
}
76+
}

‎testdata/real_scene_ean_curved.png‎

39.4 KB
Loading

0 commit comments

Comments
 (0)