use crate::config::DecoderConfig;
use crate::image::Image;
use crate::img_scanner::ImageScanner;
use crate::symbol::Symbol;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FinderRegion {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
pub struct ScanResult {
symbols: Vec<Symbol>,
finder_regions: Vec<FinderRegion>,
}
impl ScanResult {
pub(crate) fn new(symbols: Vec<Symbol>, finder_regions: Vec<FinderRegion>) -> Self {
Self {
symbols,
finder_regions,
}
}
pub fn symbols(&self) -> &[Symbol] {
&self.symbols
}
pub fn into_symbols(self) -> Vec<Symbol> {
self.symbols
}
pub fn finder_regions(&self) -> &[FinderRegion] {
&self.finder_regions
}
}
impl IntoIterator for ScanResult {
type Item = Symbol;
type IntoIter = std::vec::IntoIter<Symbol>;
fn into_iter(self) -> Self::IntoIter {
self.symbols.into_iter()
}
}
impl<'a> IntoIterator for &'a ScanResult {
type Item = &'a Symbol;
type IntoIter = std::slice::Iter<'a, Symbol>;
fn into_iter(self) -> Self::IntoIter {
self.symbols.iter()
}
}
impl std::ops::Deref for ScanResult {
type Target = [Symbol];
fn deref(&self) -> &[Symbol] {
&self.symbols
}
}
pub struct Scanner {
scanner: ImageScanner,
retry_undecoded_regions: bool,
}
impl Scanner {
pub fn new() -> Self {
Self {
scanner: ImageScanner::default(),
retry_undecoded_regions: false,
}
}
pub fn with_config(config: DecoderConfig) -> Self {
let retry = config.retry_undecoded_regions;
Self {
scanner: ImageScanner::with_config(config),
retry_undecoded_regions: retry,
}
}
pub fn scan(&mut self, image: &mut Image) -> ScanResult {
let (mut symbols, raw_regions) = self.scanner.scan_image(image.as_mut_image());
let finder_regions: Vec<FinderRegion> = raw_regions
.into_iter()
.map(|(x, y, w, h)| FinderRegion {
x,
y,
width: w,
height: h,
})
.collect();
if !self.retry_undecoded_regions || finder_regions.is_empty() {
return ScanResult::new(symbols, finder_regions);
}
const SCALES: &[u32] = &[2, 4, 6];
let apply_area_filter = image.width() >= 200 && image.height() >= 200;
let image_area = image.width() as u64 * image.height() as u64;
let area_limit = image_area / 10;
let mut unresolved: Vec<FinderRegion> = Vec::new();
for region in &finder_regions {
if apply_area_filter {
let region_area = region.width as u64 * region.height as u64;
if region_area > area_limit {
unresolved.push(*region);
continue;
}
}
let pad_x = region.width / 2;
let pad_y = region.height / 2;
let cx = region.x.saturating_sub(pad_x);
let cy = region.y.saturating_sub(pad_y);
let cw = (region.width + 2 * pad_x).min(image.width().saturating_sub(cx));
let ch = (region.height + 2 * pad_y).min(image.height().saturating_sub(cy));
let Some(cropped) = image.crop(cx, cy, cw, ch) else {
unresolved.push(*region);
continue;
};
let mut decoded = false;
for &scale in SCALES {
let Some(mut upscaled) = cropped.upscale(scale) else {
continue;
};
let (mut retry_symbols, _) = self.scanner.scan_image(upscaled.as_mut_image());
if retry_symbols.is_empty() {
continue;
}
let half = scale as i32 / 2;
for sym in &mut retry_symbols {
for pt in &mut sym.pts {
pt[0] = (pt[0] + half) / scale as i32 + cx as i32;
pt[1] = (pt[1] + half) / scale as i32 + cy as i32;
}
}
for sym in retry_symbols {
if !symbols
.iter()
.any(|s| s.symbol_type() == sym.symbol_type() && s.data == sym.data)
{
symbols.push(sym);
}
}
decoded = true;
break;
}
if !decoded {
unresolved.push(*region);
}
}
ScanResult::new(symbols, unresolved)
}
}
impl Default for Scanner {
fn default() -> Self {
Self::new()
}
}