From 3c575ed2d3d794696818a69a670f823dd7670a6e Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sun, 12 Mar 2023 11:49:33 -0500 Subject: [PATCH] port concentric finder (no test) --- src/common/cpp_essentials/bitmatrix_cursor.rs | 12 +- .../cpp_essentials/concentric_finder.rs | 90 +++-- .../cpp_essentials/dm_regression_line.rs | 25 +- src/common/cpp_essentials/edge_tracer.rs | 17 +- .../fast_edge_to_edge_counter.rs | 48 ++- src/common/cpp_essentials/mod.rs | 6 +- src/common/cpp_essentials/pattern.rs | 41 ++- src/common/cpp_essentials/regression_line.rs | 334 +++++++++++------- .../cpp_essentials/regression_line_trait.rs | 149 ++++++++ src/common/cpp_essentials/util.rs | 31 +- src/common/quad.rs | 41 ++- .../zxing_cpp_detector/cpp_new_detector.rs | 7 +- 12 files changed, 524 insertions(+), 277 deletions(-) create mode 100644 src/common/cpp_essentials/regression_line_trait.rs diff --git a/src/common/cpp_essentials/bitmatrix_cursor.rs b/src/common/cpp_essentials/bitmatrix_cursor.rs index 5cf6950..2f059a0 100644 --- a/src/common/cpp_essentials/bitmatrix_cursor.rs +++ b/src/common/cpp_essentials/bitmatrix_cursor.rs @@ -78,12 +78,12 @@ pub trait BitMatrixCursor { // } fn movedBy(self, d: Point) -> Self; - fn turnedBack(&self) -> Self;// { return {*img, p, back()}; } - // { - // auto res = *this; - // res.p += d; - // return res; - // } + fn turnedBack(&self) -> Self; // { return {*img, p, back()}; } + // { + // auto res = *this; + // res.p += d; + // return res; + // } /** * @brief stepToEdge advances cursor to one step behind the next (or n-th) edge. diff --git a/src/common/cpp_essentials/concentric_finder.rs b/src/common/cpp_essentials/concentric_finder.rs index 6fffe69..04cddd4 100644 --- a/src/common/cpp_essentials/concentric_finder.rs +++ b/src/common/cpp_essentials/concentric_finder.rs @@ -9,7 +9,8 @@ use crate::{ }; use super::{ - BitMatrixCursor, DMRegressionLine, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine, + BitMatrixCursor, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine, + RegressionLineTrait, }; pub fn CenterFromEnd + std::iter::Sum + Copy>( @@ -378,7 +379,7 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option Option Option( image:&BitMatrix, pattern:&Pattern, center:Point, range:i32) -> Option -{ - let mut cur = EdgeTracer::new(image, center, Point::default()); - let mut minSpread = image.getWidth() as i32; +pub fn LocateConcentricPattern< + const RELAXED_THRESHOLD: bool, + const LEN: usize, + const SUM: usize, + T: BitMatrixCursor, +>( + image: &BitMatrix, + pattern: &Pattern, + center: Point, + range: i32, +) -> Option { + let mut cur = EdgeTracer::new(image, center, Point::default()); + let mut minSpread = image.getWidth() as i32; let mut maxSpread = 0_i32; - for d in [point(0.0,1.0), point(1.0,0.0)] { - // for (auto d : {PointI{0, 1}, {1, 0}}) { + for d in [point(0.0, 1.0), point(1.0, 0.0)] { + // for (auto d : {PointI{0, 1}, {1, 0}}) { cur.setDirection(d); // THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? - let spread = CheckSymmetricPattern(&mut cur, pattern, range, true); - if (!(spread != 0)) - {return None} - UpdateMinMax(&mut minSpread, &mut maxSpread, spread); - } + let spread = + CheckSymmetricPattern::(&mut cur, pattern, range, true); + if (!(spread != 0)) { + return None; + } + UpdateMinMax(&mut minSpread, &mut maxSpread, spread); + } -//#if 1 -for d in [point(1.0,1.0), point(1.0,-1.0)] { - // for (auto d : {PointI{1, 1}, {1, -1}}) { - cur.setDirection(d);// THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? - let spread = CheckSymmetricPattern(&mut cur, pattern, range * 2, false); - if (!(spread != 0)) - {return None} - UpdateMinMax(&mut minSpread, &mut maxSpread, spread); - } -//#endif + //#if 1 + for d in [point(1.0, 1.0), point(1.0, -1.0)] { + // for (auto d : {PointI{1, 1}, {1, -1}}) { + cur.setDirection(d); // THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? + let spread = CheckSymmetricPattern::( + &mut cur, + pattern, + range * 2, + false, + ); + if (!(spread != 0)) { + return None; + } + UpdateMinMax(&mut minSpread, &mut maxSpread, spread); + } + //#endif - if (maxSpread > 5 * minSpread) - {return None} + if (maxSpread > 5 * minSpread) { + return None; + } - let newCenter = FinetuneConcentricPatternCenter(image, cur.p(), range, pattern.len() as u32)?; + let newCenter = FinetuneConcentricPatternCenter(image, cur.p(), range, pattern.len() as u32)?; - Some(ConcentricPattern{*newCenter, (maxSpread + minSpread) / 2}) + Some(ConcentricPattern { + p: newCenter, + size: (maxSpread + minSpread) / 2, + }) } fn UpdateMinMax(min: &mut T, max: &mut T, val: T) { diff --git a/src/common/cpp_essentials/dm_regression_line.rs b/src/common/cpp_essentials/dm_regression_line.rs index 8a574bd..dfb7cf0 100644 --- a/src/common/cpp_essentials/dm_regression_line.rs +++ b/src/common/cpp_essentials/dm_regression_line.rs @@ -1,10 +1,7 @@ use crate::common::Result; -use crate::{Exceptions, Point, point}; +use crate::{Exceptions, Point}; -use super::{ - util::{float_max, float_min}, - RegressionLine, -}; +use super::RegressionLineTrait; #[derive(Clone)] pub struct DMRegressionLine { @@ -30,7 +27,7 @@ impl Default for DMRegressionLine { } } -impl RegressionLine for DMRegressionLine { +impl RegressionLineTrait for DMRegressionLine { fn points(&self) -> &[Point] { &self.points } @@ -136,14 +133,14 @@ impl RegressionLine for DMRegressionLine { let Some(mut min) = self.points.first().copied() else { return false }; let Some(mut max) = self.points.first().copied() else { return false }; for p in &self.points { - min.x = float_min(min.x, p.x); - min.y = float_min(min.y, p.y); - max.x = float_max(max.x, p.x); - max.y = float_max(max.y, p.y); + min.x = f32::min(min.x, p.x); + min.y = f32::min(min.y, p.y); + max.x = f32::max(max.x, p.x); + max.y = f32::max(max.y, p.y); } let diff = max - min; let len = diff.maxAbsComponent(); - let steps = float_min(diff.x.abs(), diff.y.abs()); + let steps = f32::min(diff.x.abs(), diff.y.abs()); // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal steps > 2.0 || len > 50.0 } @@ -226,12 +223,6 @@ impl RegressionLine for DMRegressionLine { } impl DMRegressionLine { - pub fn with_two_points(point1: Point, point2: Point) -> Self { - let mut new_rl = DMRegressionLine::default(); - new_rl.evaluate(&[point1, point2]); - new_rl - } - // template fn average(c: &[f64], f: T) -> f64 where diff --git a/src/common/cpp_essentials/edge_tracer.rs b/src/common/cpp_essentials/edge_tracer.rs index 0f78223..b91b09f 100644 --- a/src/common/cpp_essentials/edge_tracer.rs +++ b/src/common/cpp_essentials/edge_tracer.rs @@ -6,7 +6,7 @@ use crate::{ Exceptions, Point, }; -use super::{BitMatrixCursor, Direction, RegressionLine, StepResult, Value}; +use super::{BitMatrixCursor, Direction, RegressionLineTrait, StepResult, Value}; #[derive(Clone)] pub struct EdgeTracer<'a> { @@ -142,9 +142,9 @@ impl BitMatrixCursor for EdgeTracer<'_> { */ fn stepToEdge(&mut self, nth: Option, range: Option, backup: Option) -> i32 { let mut nth = nth.unwrap_or(1); //if let Some(nth) = nth { nth } else { 1 }; - let range = range.unwrap_or(0);//if let Some(r) = range { r } else { 0 }; - let backup = backup.unwrap_or(false);//if let Some(b) = backup { b } else { false }; - // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt() + let range = range.unwrap_or(0); //if let Some(r) = range { r } else { 0 }; + let backup = backup.unwrap_or(false); //if let Some(b) = backup { b } else { false }; + // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt() let mut steps = 0; let mut lv = self.testAt(self.p); @@ -166,7 +166,6 @@ impl BitMatrixCursor for EdgeTracer<'_> { fn p(&self) -> Point { self.p } - } impl<'a> EdgeTracer<'_> { @@ -271,7 +270,11 @@ impl<'a> EdgeTracer<'_> { true } - pub fn traceLine(&mut self, dEdge: Point, line: &mut T) -> Result { + pub fn traceLine( + &mut self, + dEdge: Point, + line: &mut T, + ) -> Result { line.setDirectionInward(dEdge); loop { // log(self.p); @@ -298,7 +301,7 @@ impl<'a> EdgeTracer<'_> { } // while (true); } - pub fn traceGaps( + pub fn traceGaps( &mut self, dEdge: Point, line: &mut T, diff --git a/src/common/cpp_essentials/fast_edge_to_edge_counter.rs b/src/common/cpp_essentials/fast_edge_to_edge_counter.rs index 835cb2a..cd0b7b3 100644 --- a/src/common/cpp_essentials/fast_edge_to_edge_counter.rs +++ b/src/common/cpp_essentials/fast_edge_to_edge_counter.rs @@ -2,39 +2,37 @@ use super::BitMatrixCursor; pub struct FastEdgeToEdgeCounter { // const uint8_t* p = nullptr; - // int stride = 0; - // int stepsToBorder = 0; + // int stride = 0; + // int stepsToBorder = 0; } -impl FastEdgeToEdgeCounter -{ +impl FastEdgeToEdgeCounter { pub fn new(cur: &T) -> Self { todo!() // stride = cur.d.y * cur.img->width() + cur.d.x; - // p = cur.img->row(cur.p.y).begin() + cur.p.x; + // p = cur.img->row(cur.p.y).begin() + cur.p.x; - // int maxStepsX = cur.d.x ? (cur.d.x > 0 ? cur.img->width() - 1 - cur.p.x : cur.p.x) : INT_MAX; - // int maxStepsY = cur.d.y ? (cur.d.y > 0 ? cur.img->height() - 1 - cur.p.y : cur.p.y) : INT_MAX; - // stepsToBorder = std::min(maxStepsX, maxStepsY); + // int maxStepsX = cur.d.x ? (cur.d.x > 0 ? cur.img->width() - 1 - cur.p.x : cur.p.x) : INT_MAX; + // int maxStepsY = cur.d.y ? (cur.d.y > 0 ? cur.img->height() - 1 - cur.p.y : cur.p.y) : INT_MAX; + // stepsToBorder = std::min(maxStepsX, maxStepsY); } - pub fn stepToNextEdge(&self, range: i32) -> i32 - { + pub fn stepToNextEdge(&self, range: i32) -> i32 { todo!() - // int maxSteps = std::min(stepsToBorder, range); - // int steps = 0; - // do { - // if (++steps > maxSteps) { - // if (maxSteps == stepsToBorder) - // break; - // else - // return 0; - // } - // } while (p[steps * stride] == p[0]); + // int maxSteps = std::min(stepsToBorder, range); + // int steps = 0; + // do { + // if (++steps > maxSteps) { + // if (maxSteps == stepsToBorder) + // break; + // else + // return 0; + // } + // } while (p[steps * stride] == p[0]); - // p += steps * stride; - // stepsToBorder -= steps; + // p += steps * stride; + // stepsToBorder -= steps; - // return steps; - } -} \ No newline at end of file + // return steps; + } +} diff --git a/src/common/cpp_essentials/mod.rs b/src/common/cpp_essentials/mod.rs index 3f040c0..725388d 100644 --- a/src/common/cpp_essentials/mod.rs +++ b/src/common/cpp_essentials/mod.rs @@ -3,21 +3,23 @@ pub mod concentric_finder; pub mod direction; pub mod dm_regression_line; pub mod edge_tracer; +pub mod fast_edge_to_edge_counter; pub mod pattern; pub mod regression_line; +pub mod regression_line_trait; pub mod step_result; pub mod util; pub mod value; -pub mod fast_edge_to_edge_counter; pub use bitmatrix_cursor::*; pub use concentric_finder::*; pub use direction::*; pub use dm_regression_line::*; pub use edge_tracer::*; +pub use fast_edge_to_edge_counter::*; pub use pattern::*; pub use regression_line::*; +pub use regression_line_trait::*; pub use step_result::*; pub use util::*; pub use value::*; -pub use fast_edge_to_edge_counter::*; diff --git a/src/common/cpp_essentials/pattern.rs b/src/common/cpp_essentials/pattern.rs index ff5cf36..3b6d82c 100644 --- a/src/common/cpp_essentials/pattern.rs +++ b/src/common/cpp_essentials/pattern.rs @@ -8,19 +8,19 @@ use crate::{common::Result, Exceptions}; pub type PatternType = u16; pub type Pattern = [PatternType; N]; -#[derive(Default,Debug)] +#[derive(Default, Debug)] pub struct PatternRow(Vec); // pub struct PatternRow + Into + Copy>(Vec); impl PatternRow { -pub fn new( v: Vec) -> Self { - Self(v) -} + pub fn new(v: Vec) -> Self { + Self(v) + } -pub fn len(&self) -> usize { - self.0.len() -} + pub fn len(&self) -> usize { + self.0.len() + } pub fn into_pattern_view(&self) -> PatternView { PatternView::new(self) @@ -534,7 +534,7 @@ pub fn NormalizedPattern<'a, const LEN: usize, const SUM: usize>( #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum Color { White = 0, - Black = 1 + Black = 1, } impl> From for Color { @@ -546,11 +546,14 @@ impl> From for Color { } } -fn GetPatternRow + Copy + Default + From>(b_row: &[T], p_row: &mut PatternRow) { +fn GetPatternRow + Copy + Default + From>( + b_row: &[T], + p_row: &mut PatternRow, +) { p_row.0.clear(); if Color::from(p_row.0.first().copied().unwrap_or_default()) == Color::Black { - // first + // first p_row.0.push(0); } @@ -567,7 +570,7 @@ fn GetPatternRow + Copy + Default + From>(b_row: &[T], p current_color = this_color; } - count +=1 ; + count += 1; } if count != 0 { @@ -577,7 +580,6 @@ fn GetPatternRow + Copy + Default + From>(b_row: &[T], p if current_color == Color::Black { p_row.0.push(0); } - } #[cfg(test)] @@ -591,7 +593,7 @@ mod tests { fn all_white() { for s in 1..=N { // for (int s = 1; s <= N; ++s) { - let t_in:Vec = vec![0; s]; + let t_in: Vec = vec![0; s]; // std::vector in(s, 0); let mut pr = PatternRow::default(); GetPatternRow(&t_in, &mut pr); @@ -620,7 +622,7 @@ mod tests { fn black_white() { for s in 1..=N { // for (int s = 1; s <= N; ++s) { - let mut t_in : Vec = vec![0; N]; + let mut t_in: Vec = vec![0; N]; t_in[..s].copy_from_slice(&vec![1; s]); // std::fill_n(in.data(), s, 0xff); let mut pr = PatternRow::default(); @@ -652,11 +654,16 @@ mod tests { #[test] fn basic_pattern_view() { let mut p_row = PatternRow::default(); - GetPatternRow(&vec![0_u16,1,0,1,0,0,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,1], &mut p_row); + GetPatternRow( + &vec![ + 0_u16, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + ], + &mut p_row, + ); let mut pv = PatternView::new(&p_row); - assert_eq!(pv.data().0,p_row.0); + assert_eq!(pv.data().0, p_row.0); assert_eq!(pv[0], 1_u16); assert_eq!(pv[1], 1_u16); @@ -667,6 +674,6 @@ mod tests { assert!(pv.shift(1)); assert_eq!(pv.index(), 1); assert!(pv.skipPair()); - assert_eq!(pv.index(),3); + assert_eq!(pv.index(), 3); } } diff --git a/src/common/cpp_essentials/regression_line.rs b/src/common/cpp_essentials/regression_line.rs index a859480..8847389 100644 --- a/src/common/cpp_essentials/regression_line.rs +++ b/src/common/cpp_essentials/regression_line.rs @@ -1,147 +1,231 @@ use crate::common::Result; -use crate::{Point, point}; +use crate::{Exceptions, Point}; -pub trait RegressionLine { - // points: Vec, - // direction_inward: Point, +use super::RegressionLineTrait; - // } - // impl RegressionLine { +#[derive(Clone)] +pub struct RegressionLine { + points: Vec, + direction_inward: Point, + pub(super) a: f32, + pub(super) b: f32, + pub(super) c: f32, // std::vector _points; // PointF _directionInward; // PointF::value_t a = NAN, b = NAN, c = NAN; +} - fn intersect( l1: &T, l2: &T2) - -> Option{ - if !(l1.isValid() && l2.isValid()) { - return None; - } - - let d = l1.a() * l2.b() - l1.b() * l2.a(); - let x = (l1.c() * l2.b() - l1.b() * l2.c()) / d; - let y = (l1.a() * l2.c() - l1.c() * l2.a()) / d; - - Some(point(x, y)) +impl Default for RegressionLine { + fn default() -> Self { + Self { + points: Default::default(), + direction_inward: Default::default(), + a: f32::NAN, + b: f32::NAN, + c: f32::NAN, } + } +} - // fn evaluate_begin_end(&self, begin: Point, end: Point) -> bool;// { - // { - // let mean = std::accumulate(begin, end, PointF()) / std::distance(begin, end); - // PointF::value_t sumXX = 0, sumYY = 0, sumXY = 0; - // for (auto p = begin; p != end; ++p) { - // auto d = *p - mean; - // sumXX += d.x * d.x; - // sumYY += d.y * d.y; - // sumXY += d.x * d.y; - // } - // if (sumYY >= sumXX) { - // auto l = std::sqrt(sumYY * sumYY + sumXY * sumXY); - // a = +sumYY / l; - // b = -sumXY / l; - // } else { - // auto l = std::sqrt(sumXX * sumXX + sumXY * sumXY); - // a = +sumXY / l; - // b = -sumXX / l; - // } - // if (dot(_directionInward, normal()) < 0) { - // a = -a; - // b = -b; - // } - // c = dot(normal(), mean); // (a*mean.x + b*mean.y); - // return dot(_directionInward, normal()) > 0.5f; // angle between original and new direction is at most 60 degree - // } - - fn evaluate(&mut self, points: &[Point]) -> bool; // { return self.evaluate_begin_end(&points.front(), &points.back() + 1); } - fn evaluateSelf(&mut self) -> bool; - - // RegressionLine() { _points.reserve(16); } // arbitrary but plausible start size (tiny performance improvement) - - // template RegressionLine(PointT a, PointT b) - // { - // evaluate(std::vector{a, b}); - // } - - // template RegressionLine(const PointT* b, const PointT* e) - // { - // evaluate(b, e); - // } - - fn points(&self) -> &[Point]; //const { return _points; } - fn length(&self) -> u32; //const { return _points.size() >= 2 ? int(distance(_points.front(), _points.back())) : 0; } - fn isValid(&self) -> bool; //const { return !std::isnan(a); } - fn normal(&self) -> Point; //const { return isValid() ? PointF(a, b) : _directionInward; } - fn signedDistance(&self, p: Point) -> f32; //const { return dot(normal(), p) - c; } - fn distance_single(&self, p: Point) -> f32; //const { return std::abs(signedDistance(PointF(p))); } - fn project(&self, p: Point) -> Point { - p - self.normal() * self.signedDistance(p) +impl RegressionLineTrait for RegressionLine { + fn points(&self) -> &[Point] { + &self.points } - fn reset(&mut self); - // { - // _points.clear(); - // _directionInward = {}; - // a = b = c = NAN; - // } + fn length(&self) -> u32 { + if self.points.len() >= 2 { + Point::distance(*self.points.first().unwrap(), *self.points.last().unwrap()) as u32 + } else { + 0 + } + } - fn add(&mut self, p: Point) -> Result<()>; //{ - // assert(_directionInward != PointF()); - // _points.push_back(p); - // if (_points.size() == 1) - // c = dot(normal(), p); - // } + fn isValid(&self) -> bool { + !self.a.is_nan() + } - fn pop_back(&mut self); // { _points.pop_back(); } + fn normal(&self) -> Point { + if self.isValid() { + Point { + x: self.a, + y: self.b, + } + } else { + self.direction_inward + } + } - fn setDirectionInward(&mut self, d: Point); //{ _directionInward = normalized(d); } + fn signedDistance(&self, p: Point) -> f32 { + Point::dot(self.normal(), p) - self.c + } + + fn distance_single(&self, p: Point) -> f32 { + (self.signedDistance(p)).abs() + } + + fn reset(&mut self) { + self.points.clear(); + self.direction_inward = Point { x: 0.0, y: 0.0 }; + self.a = f32::NAN; + self.b = f32::NAN; + self.c = f32::NAN; + } + + fn add(&mut self, p: Point) -> Result<()> { + if self.direction_inward == Point::default() { + return Err(Exceptions::ILLEGAL_STATE); + } + self.points.push(p); + if self.points.len() == 1 { + self.c = Point::dot(self.normal(), p); + } + Ok(()) + } + + fn pop_back(&mut self) { + self.points.pop(); + } + + fn setDirectionInward(&mut self, d: Point) { + self.direction_inward = Point::normalized(d); + } - // fn evaluate(&self, double maxSignedDist = -1, bool updatePoints = false) -> bool fn evaluate_max_distance( &mut self, maxSignedDist: Option, updatePoints: Option, - ) -> bool; - // { - // bool ret = evaluate(_points); - // if (maxSignedDist > 0) { - // auto points = _points; - // while (true) { - // auto old_points_size = points.size(); - // // remove points that are further 'inside' than maxSignedDist or further 'outside' than 2 x maxSignedDist - // auto end = std::remove_if(points.begin(), points.end(), [this, maxSignedDist](auto p) { - // auto sd = this->signedDistance(p); - // return sd > maxSignedDist || sd < -2 * maxSignedDist; - // }); - // points.erase(end, points.end()); - // if (old_points_size == points.size()) - // break; - // // #ifdef PRINT_DEBUG - // // printf("removed %zu points\n", old_points_size - points.size()); - // // #endif - // ret = evaluate(points); - // } + ) -> bool { + let maxSignedDist = if let Some(m) = maxSignedDist { m } else { -1.0 }; + let updatePoints = if let Some(u) = updatePoints { u } else { false }; - // if (updatePoints) - // _points = std::move(points); - // } - // return ret; - // } + let mut ret = self.evaluateSelf(); + if maxSignedDist > 0.0 { + let mut points = self.points.clone(); + loop { + let old_points_size = points.len(); + // remove points that are further 'inside' than maxSignedDist or further 'outside' than 2 x maxSignedDist + // auto end = std::remove_if(points.begin(), points.end(), [this, maxSignedDist](auto p) { + // auto sd = this->signedDistance(p); + // return sd > maxSignedDist || sd < -2 * maxSignedDist; + // }); + // points.erase(end, points.end()); + points.retain(|&p| { + let sd = self.signedDistance(p) as f64; + !(sd > maxSignedDist || sd < -2.0 * maxSignedDist) + }); + if old_points_size == points.len() { + break; + } + // #ifdef PRINT_DEBUG + // printf("removed %zu points\n", old_points_size - points.size()); + // #endif + ret = self.evaluate(&points); + } - fn isHighRes(&self) -> bool; //const - // { - // PointF min = _points.front(), max = _points.front(); - // for (auto p : _points) { - // min.x = std::min(min.x, p.x); - // min.y = std::min(min.y, p.y); - // max.x = std::max(max.x, p.x); - // max.y = std::max(max.y, p.y); - // } - // auto diff = max - min; - // auto len = maxAbsComponent(diff); - // auto steps = std::min(std::abs(diff.x), std::abs(diff.y)); - // // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal - // return steps > 2 || len > 50; - // } - fn a(&self) -> f32; - fn b(&self) -> f32; - fn c(&self) -> f32; + if updatePoints { + self.points = points; + } + } + ret + } + + fn isHighRes(&self) -> bool { + let Some(mut min) = self.points.first().copied() else { return false }; + let Some(mut max) = self.points.first().copied() else { return false }; + for p in &self.points { + min.x = f32::min(min.x, p.x); + min.y = f32::min(min.y, p.y); + max.x = f32::max(max.x, p.x); + max.y = f32::max(max.y, p.y); + } + let diff = max - min; + let len = diff.maxAbsComponent(); + let steps = f32::min(diff.x.abs(), diff.y.abs()); + // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal + steps > 2.0 || len > 50.0 + } + + fn evaluate(&mut self, points: &[Point]) -> bool { + let mean = points.iter().sum::() / points.len() as f32; + + let mut sumXX = 0.0; + let mut sumYY = 0.0; + let mut sumXY = 0.0; + for p in points { + // for (auto p = begin; p != end; ++p) { + let d = *p - mean; + sumXX += d.x * d.x; + sumYY += d.y * d.y; + sumXY += d.x * d.y; + } + if sumYY >= sumXX { + let l = (sumYY * sumYY + sumXY * sumXY).sqrt(); + self.a = sumYY / l; + self.b = -sumXY / l; + } else { + let l = (sumXX * sumXX + sumXY * sumXY).sqrt(); + self.a = sumXY / l; + self.b = -sumXX / l; + } + if Point::dot(self.direction_inward, self.normal()) < 0.0 { + // if (dot(_directionInward, normal()) < 0) { + self.a = -self.a; + self.b = -self.b; + } + self.c = Point::dot(self.normal(), mean); // (a*mean.x + b*mean.y); + Point::dot(self.direction_inward, self.normal()) > 0.5 + // angle between original and new direction is at most 60 degree + } + + fn evaluateSelf(&mut self) -> bool { + let mean = self.points.iter().sum::() / self.points.len() as f32; + + let mut sumXX = 0.0; + let mut sumYY = 0.0; + let mut sumXY = 0.0; + for p in &self.points { + // for (auto p = begin; p != end; ++p) { + let d = *p - mean; + sumXX += d.x * d.x; + sumYY += d.y * d.y; + sumXY += d.x * d.y; + } + if sumYY >= sumXX { + let l = (sumYY * sumYY + sumXY * sumXY).sqrt(); + self.a = sumYY / l; + self.b = -sumXY / l; + } else { + let l = (sumXX * sumXX + sumXY * sumXY).sqrt(); + self.a = sumXY / l; + self.b = -sumXX / l; + } + if Point::dot(self.direction_inward, self.normal()) < 0.0 { + // if (dot(_directionInward, normal()) < 0) { + self.a = -self.a; + self.b = -self.b; + } + self.c = Point::dot(self.normal(), mean); // (a*mean.x + b*mean.y); + Point::dot(self.direction_inward, self.normal()) > 0.5 + // angle between original and new direction is at most 60 degree + } + + fn a(&self) -> f32 { + self.a + } + + fn b(&self) -> f32 { + self.b + } + + fn c(&self) -> f32 { + self.c + } +} + +impl RegressionLine { + pub fn with_two_points(point1: Point, point2: Point) -> Self { + let mut new_rl = RegressionLine::default(); + new_rl.evaluate(&[point1, point2]); + new_rl + } } diff --git a/src/common/cpp_essentials/regression_line_trait.rs b/src/common/cpp_essentials/regression_line_trait.rs new file mode 100644 index 0000000..4fa8bf3 --- /dev/null +++ b/src/common/cpp_essentials/regression_line_trait.rs @@ -0,0 +1,149 @@ +use crate::common::Result; +use crate::{point, Point}; + +pub trait RegressionLineTrait { + // points: Vec, + // direction_inward: Point, + + // } + // impl RegressionLine { + // std::vector _points; + // PointF _directionInward; + // PointF::value_t a = NAN, b = NAN, c = NAN; + + fn intersect( + l1: &T, + l2: &T2, + ) -> Option { + if !(l1.isValid() && l2.isValid()) { + return None; + } + + let d = l1.a() * l2.b() - l1.b() * l2.a(); + let x = (l1.c() * l2.b() - l1.b() * l2.c()) / d; + let y = (l1.a() * l2.c() - l1.c() * l2.a()) / d; + + Some(point(x, y)) + } + + // fn evaluate_begin_end(&self, begin: Point, end: Point) -> bool;// { + // { + // let mean = std::accumulate(begin, end, PointF()) / std::distance(begin, end); + // PointF::value_t sumXX = 0, sumYY = 0, sumXY = 0; + // for (auto p = begin; p != end; ++p) { + // auto d = *p - mean; + // sumXX += d.x * d.x; + // sumYY += d.y * d.y; + // sumXY += d.x * d.y; + // } + // if (sumYY >= sumXX) { + // auto l = std::sqrt(sumYY * sumYY + sumXY * sumXY); + // a = +sumYY / l; + // b = -sumXY / l; + // } else { + // auto l = std::sqrt(sumXX * sumXX + sumXY * sumXY); + // a = +sumXY / l; + // b = -sumXX / l; + // } + // if (dot(_directionInward, normal()) < 0) { + // a = -a; + // b = -b; + // } + // c = dot(normal(), mean); // (a*mean.x + b*mean.y); + // return dot(_directionInward, normal()) > 0.5f; // angle between original and new direction is at most 60 degree + // } + + fn evaluate(&mut self, points: &[Point]) -> bool; // { return self.evaluate_begin_end(&points.front(), &points.back() + 1); } + fn evaluateSelf(&mut self) -> bool; + + // RegressionLine() { _points.reserve(16); } // arbitrary but plausible start size (tiny performance improvement) + + // template RegressionLine(PointT a, PointT b) + // { + // evaluate(std::vector{a, b}); + // } + + // template RegressionLine(const PointT* b, const PointT* e) + // { + // evaluate(b, e); + // } + + fn points(&self) -> &[Point]; //const { return _points; } + fn length(&self) -> u32; //const { return _points.size() >= 2 ? int(distance(_points.front(), _points.back())) : 0; } + fn isValid(&self) -> bool; //const { return !std::isnan(a); } + fn normal(&self) -> Point; //const { return isValid() ? PointF(a, b) : _directionInward; } + fn signedDistance(&self, p: Point) -> f32; //const { return dot(normal(), p) - c; } + fn distance_single(&self, p: Point) -> f32; //const { return std::abs(signedDistance(PointF(p))); } + fn project(&self, p: Point) -> Point { + p - self.normal() * self.signedDistance(p) + } + + fn reset(&mut self); + // { + // _points.clear(); + // _directionInward = {}; + // a = b = c = NAN; + // } + + fn add(&mut self, p: Point) -> Result<()>; //{ + // assert(_directionInward != PointF()); + // _points.push_back(p); + // if (_points.size() == 1) + // c = dot(normal(), p); + // } + + fn pop_back(&mut self); // { _points.pop_back(); } + + fn setDirectionInward(&mut self, d: Point); //{ _directionInward = normalized(d); } + + // fn evaluate(&self, double maxSignedDist = -1, bool updatePoints = false) -> bool + fn evaluate_max_distance( + &mut self, + maxSignedDist: Option, + updatePoints: Option, + ) -> bool; + // { + // bool ret = evaluate(_points); + // if (maxSignedDist > 0) { + // auto points = _points; + // while (true) { + // auto old_points_size = points.size(); + // // remove points that are further 'inside' than maxSignedDist or further 'outside' than 2 x maxSignedDist + // auto end = std::remove_if(points.begin(), points.end(), [this, maxSignedDist](auto p) { + // auto sd = this->signedDistance(p); + // return sd > maxSignedDist || sd < -2 * maxSignedDist; + // }); + // points.erase(end, points.end()); + // if (old_points_size == points.size()) + // break; + // // #ifdef PRINT_DEBUG + // // printf("removed %zu points\n", old_points_size - points.size()); + // // #endif + // ret = evaluate(points); + // } + + // if (updatePoints) + // _points = std::move(points); + // } + // return ret; + // } + + fn isHighRes(&self) -> bool; //const + // { + // PointF min = _points.front(), max = _points.front(); + // for (auto p : _points) { + // min.x = std::min(min.x, p.x); + // min.y = std::min(min.y, p.y); + // max.x = std::max(max.x, p.x); + // max.y = std::max(max.y, p.y); + // } + // auto diff = max - min; + // auto len = maxAbsComponent(diff); + // auto steps = std::min(std::abs(diff.x), std::abs(diff.y)); + // // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal + // return steps > 2 || len > 50; + // } + fn a(&self) -> f32; + fn b(&self) -> f32; + fn c(&self) -> f32; +} diff --git a/src/common/cpp_essentials/util.rs b/src/common/cpp_essentials/util.rs index c2c4180..6b580a1 100644 --- a/src/common/cpp_essentials/util.rs +++ b/src/common/cpp_essentials/util.rs @@ -1,34 +1,19 @@ use crate::common::Result; use crate::{Exceptions, Point}; -use super::{DMRegressionLine, Direction, RegressionLine}; +use super::{Direction, RegressionLineTrait}; #[inline(always)] -pub fn float_min(a: T, b: T) -> T { - if a > b { - b - } else { - a - } -} - -#[inline(always)] -pub fn float_max(a: T, b: T) -> T { - if a < b { - b - } else { - a - } -} - -#[inline(always)] -pub fn intersect(l1: &DMRegressionLine, l2: &DMRegressionLine) -> Result { +pub fn intersect( + l1: &T, + l2: &T2, +) -> Result { if !(l1.isValid() && l2.isValid()) { return Err(Exceptions::ILLEGAL_STATE); } - let d = l1.a * l2.b - l1.b * l2.a; - let x = (l1.c * l2.b - l1.b * l2.c) / d; - let y = (l1.a * l2.c - l1.c * l2.a) / d; + let d = l1.a() * l2.b() - l1.b() * l2.a(); + let x = (l1.c() * l2.b() - l1.b() * l2.c()) / d; + let y = (l1.a() * l2.c() - l1.c() * l2.a()) / d; Ok(Point { x, y }) } diff --git a/src/common/quad.rs b/src/common/quad.rs index 3d33ad2..a2b94a6 100644 --- a/src/common/quad.rs +++ b/src/common/quad.rs @@ -209,26 +209,31 @@ impl Quadrilateral { !(x || y) } - pub fn blend(a: &Quadrilateral, b:&Quadrilateral) -> Self { + pub fn blend(a: &Quadrilateral, b: &Quadrilateral) -> Self { let c = a[0]; - let dist2First = | a, b| { Point::distance(a, c) < Point::distance(b, c) }; - // rotate points such that the the two topLeft points are closest to each other - let min_element = b.0.iter().copied().min_by(|a,b| { - match dist2First(*a,*b) { - true => std::cmp::Ordering::Less, - false => std::cmp::Ordering::Greater, + let dist2First = |a, b| Point::distance(a, c) < Point::distance(b, c); + // rotate points such that the the two topLeft points are closest to each other + let min_element = + b.0.iter() + .copied() + .min_by(|a, b| match dist2First(*a, *b) { + true => std::cmp::Ordering::Less, + false => std::cmp::Ordering::Greater, + }) + .unwrap_or_default(); + let offset = + b.0.iter() + .position(|v| *v == min_element) + .unwrap_or_default(); + // let offset = std::min_element(b.begin(), b.end(), dist2First) - b.begin(); + + let mut res = Quadrilateral::default(); + for i in 0..4 { + // for (int i = 0; i < 4; ++i){ + res[i] = (a[i] + b[(i + offset) % 4]) / 2.0; } - }).unwrap_or_default(); - let offset = b.0.iter().position(|v| *v == min_element).unwrap_or_default(); - // let offset = std::min_element(b.begin(), b.end(), dist2First) - b.begin(); - let mut res= Quadrilateral::default(); - for i in 0..4 { - // for (int i = 0; i < 4; ++i){ - res[i] = (a[i] + b[(i + offset) % 4]) / 2.0; - } - - res + res } } @@ -250,4 +255,4 @@ impl std::ops::IndexMut for Quadrilateral { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.0[index] } -} \ No newline at end of file +} diff --git a/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs b/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs index 761cba3..2b98e76 100644 --- a/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs +++ b/src/datamatrix/detector/zxing_cpp_detector/cpp_new_detector.rs @@ -14,9 +14,12 @@ macro_rules! CHECK { use std::{cell::RefCell, rc::Rc}; use crate::{ - common::{BitMatrix, DefaultGridSampler, GridSampler, Quadrilateral, Result}, + common::{ + cpp_essentials::RegressionLineTrait, BitMatrix, DefaultGridSampler, GridSampler, + Quadrilateral, Result, + }, datamatrix::detector::{ - zxing_cpp_detector::{util::intersect, BitMatrixCursor, RegressionLine}, + zxing_cpp_detector::{util::intersect, BitMatrixCursor}, DatamatrixDetectorResult, }, point,