use crate::geometry::Location;
use crate::symbol::Symbol;
use crate::symbology::Symbology;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Fingerprint(pub u64);
#[derive(Debug, Clone)]
pub struct Candidate {
pub location: Location,
pub symbology: Option<Symbology>,
pub fingerprint: Option<Fingerprint>,
pub known: Option<Symbol>,
}
impl Candidate {
pub fn at(location: Location) -> Self {
Candidate {
location,
symbology: None,
fingerprint: None,
known: None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Hints {
pub previous: Vec<KnownSymbol>,
}
#[derive(Debug, Clone)]
pub struct KnownSymbol {
pub symbol: Symbol,
pub fingerprint: Option<Fingerprint>,
}
impl Hints {
pub fn new() -> Self {
Hints::default()
}
pub fn find(&self, fp: Fingerprint) -> Option<&KnownSymbol> {
self.previous.iter().find(|k| k.fingerprint == Some(fp))
}
}
pub fn scan_all(frame: &crate::image::GrayFrame<'_>) -> Vec<Symbol> {
let mut found = scan_2d(frame);
dedup_extend(&mut found, scan_1d(frame));
found
}
pub fn scan_2d(frame: &crate::image::GrayFrame<'_>) -> Vec<Symbol> {
let mut found: Vec<Symbol> = Vec::new();
if let Ok(s) = crate::codes::qr::scan(frame) {
found.push(s);
}
if let Ok(s) = crate::codes::datamatrix::scan(frame) {
dedup_push(&mut found, s);
}
if let Some(s) = crate::codes::pdf417::scan(frame) {
dedup_push(&mut found, s);
}
#[cfg(feature = "appclip")]
if let Ok(s) = crate::codes::appclip::scan(frame) {
dedup_push(&mut found, s);
}
found
}
pub fn scan_1d(frame: &crate::image::GrayFrame<'_>) -> Vec<Symbol> {
let scan_opts = crate::scan1d::ScanOptions::default();
let found = scan_1d_sweep(frame, &scan_opts);
if !found.is_empty() {
return found;
}
let Some(angle) = crate::imgproc::orient::dominant_gradient_angle(frame) else {
return found;
};
if angle.abs() < MIN_DEROTATE_ANGLE {
return found;
}
let image = to_image(frame);
let rotated = crate::transform::rotate(&image, -angle);
let mut found = scan_1d_sweep(&rotated.as_frame(), &scan_opts);
let (s, c) = (-angle).sin_cos();
let (cx, cy) = (frame.width() as f32 / 2.0, frame.height() as f32 / 2.0);
let (ncx, ncy) = (rotated.width() as f32 / 2.0, rotated.height() as f32 / 2.0);
for sym in &mut found {
if let Some(loc) = &mut sym.location {
for p in &mut loc.outline.corners {
let (dx, dy) = (p.x - ncx, p.y - ncy);
p.x = c * dx + s * dy + cx;
p.y = -s * dx + c * dy + cy;
}
loc.rotation = Some(loc.rotation.unwrap_or(0.0) + angle);
}
}
found
}
const MIN_DEROTATE_ANGLE: f32 = 8.0 * core::f32::consts::PI / 180.0;
fn scan_1d_sweep(
frame: &crate::image::GrayFrame<'_>,
scan_opts: &crate::scan1d::ScanOptions,
) -> Vec<Symbol> {
use crate::traits::Decode;
let mut found: Vec<Symbol> = Vec::new();
if let Some(s) = crate::codes::ean::scan(frame, scan_opts) {
found.push(s);
}
let candidates = crate::scan1d::scan_lines(frame, scan_opts);
let linear: [Box<dyn Decode>; 6] = [
Box::new(crate::codes::code128::Code128Decoder::new()),
Box::new(crate::codes::ean::EanDecoder::new()),
Box::new(crate::codes::code93::Code93Decoder::new()),
Box::new(crate::codes::code39::Code39Decoder::new()),
Box::new(crate::codes::itf::ItfDecoder::new()),
Box::new(crate::codes::codabar::CodabarDecoder::new()),
];
for cand in &candidates {
let mut mirrored = cand.clone();
mirrored.pattern.modules.reverse();
mirrored.edges.clear();
for dec in &linear {
if let Some(s) = crate::scan1d::try_decode(cand, dec.as_ref()) {
dedup_push(&mut found, s);
}
if let Some(s) = crate::scan1d::try_decode(&mirrored, dec.as_ref()) {
dedup_push(&mut found, s);
}
}
}
found
}
fn to_image(frame: &crate::image::GrayFrame<'_>) -> crate::image::GrayImage {
let (w, h) = (frame.width(), frame.height());
let mut data = Vec::with_capacity(w * h);
for y in 0..h {
data.extend_from_slice(frame.row(y).expect("row in range"));
}
crate::image::GrayImage::from_raw(w, h, data)
}
fn dedup_push(found: &mut Vec<Symbol>, sym: Symbol) {
let key = (sym.symbology, sym.text().unwrap_or_default());
if !found
.iter()
.any(|s| (s.symbology, s.text().unwrap_or_default()) == key)
{
found.push(sym);
}
}
fn dedup_extend(found: &mut Vec<Symbol>, more: Vec<Symbol>) {
for sym in more {
dedup_push(found, sym);
}
}