ported latest c++ changes, does not pass all tests

This commit is contained in:
Henry Schimke
2023-04-25 15:59:47 -05:00
parent d841c1d751
commit b216a446ee
4 changed files with 215 additions and 119 deletions

View File

@@ -79,7 +79,7 @@ pub fn ReadSymmetricPattern<const N: usize, Cursor: BitMatrixCursorTrait>(
// default for RELAXED_THRESHOLD should be false
pub fn CheckSymmetricPattern<
const RELAXED_THRESHOLD: bool,
const E2E: bool,
const LEN: usize,
const SUM: usize,
T: BitMatrixCursorTrait,
@@ -125,13 +125,12 @@ pub fn CheckSymmetricPattern<
}
}
if IsPattern(
if IsPattern::<E2E, LEN, SUM, false>(
&PatternView::new(&res),
&FixedPattern::<LEN, SUM, false>::with_reference(pattern),
None,
0.0,
0.0,
Some(RELAXED_THRESHOLD),
) == 0.0
{
return 0;
@@ -258,46 +257,28 @@ pub fn CenterOfRings(
range: i32,
numOfRings: u32,
) -> Option<Point> {
let mut n = numOfRings;
let mut sum = numOfRings * center;
for i in 1..numOfRings {
let mut n = 1;
let mut sum = center;
for i in 2..(numOfRings + 1) {
// for (int i = 1; i < numOfRings; ++i) {
let c = CenterOfRing(image, center, range, i as i32 + 1, false)?;
let c = CenterOfRing(image, center.floor(), range, i as i32, false)?;
// TODO: decide whether this wheighting depending on distance to the center is worth it
let weight = numOfRings - i;
sum += weight * c;
n += weight;
if !(c == Point::default()) {
if n == 1 {
return None;
} else {
return Some(sum / n as f32);
}
} else if Point::distance(c, center) > range as f32 / numOfRings as f32 / 2.0 {
return None;
}
sum += c;
n += 1;
}
Some(sum / n as f32)
}
pub fn FinetuneConcentricPatternCenter(
image: &BitMatrix,
center: Point,
range: i32,
finderPatternSize: u32,
) -> Option<Point> {
// make sure we have at least one path of white around the center
let res = CenterOfRing(image, center, range, 1, false)?;
let center = res;
let mut res = CenterOfRings(image, center, range, finderPatternSize / 2);
if res.is_none() || !image.get_point(res?) {
res = CenterOfDoubleCross(image, center, range, finderPatternSize / 2 + 1);
}
if res.is_none() || !image.get_point(res?) {
res = Some(center);
}
if res.is_none() || !image.get_point(res?) {
return None;
}
res
}
pub fn CollectRingPoints(
image: &BitMatrix,
center: Point,
@@ -419,12 +400,6 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
points.iter().position(|p| *p == corners[3])?,
];
// let lines = [
// RegressionLine::with_two_points(corners[0] + 1.0, corners[1]),
// RegressionLine::with_two_points(corners[1] + 1.0, corners[2]),
// RegressionLine::with_two_points(corners[2] + 1.0, corners[3]),
// RegressionLine::with_two_points(corners[3] + 1.0, *points.last()? + 1.0),
// ];
let lines = [
RegressionLine::with_point_slice(&points[corner_positions[0] + 1..corner_positions[1]]),
RegressionLine::with_point_slice(&points[corner_positions[1] + 1..corner_positions[2]]),
@@ -437,6 +412,36 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
return None;
}
let beg: [usize; 4] = [
corner_positions[0] + 1,
corner_positions[1] + 1,
corner_positions[2] + 1,
corner_positions[3] + 1,
];
let end: [usize; 4] = [
corner_positions[1],
corner_positions[2],
corner_positions[3],
points.len(),
];
// check if all points belonging to each line segment are sufficiently close to that line
for i in 0..4 {
// for (int i = 0; i < 4; ++i){
for p in &points[beg[i]..end[i]] {
// for (const PointF* p = beg[i]; p != end[i]; ++p) {
let len = (end[i] - beg[i]) as f64; //std::distance(beg[i], end[i]);
if (len > 3.0
&& (lines[i].distance_single(*p) as f64) > f64::max(1.0, f64::min(8.0, len / 8.0)))
{
// #ifdef PRINT_DEBUG
// printf("%d: %.2f > %.2f @ %.fx%.f\n", i, lines[i].distance(*p), std::distance(beg[i], end[i]) / 1., p->x, p->y);
// #endif
return None;
}
}
}
let mut res = Quadrilateral::default();
for i in 0..4 {
// for (int i = 0; i < 4; ++i) {
@@ -446,44 +451,6 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
Some(res)
}
// fn fit_quadralateral_to_points(center: Point, points: Vec<Point>) -> Option<Quadrilateral> {
// let dist2center = |a, b| Point::distance(a, center) < Point::distance(b, center);
// // Rotate points such that the first one is the furthest away from the center (hence, a corner)
// let mut points = points;
// points.rotate_left(points.iter().position(|p| dist2center(p, center)).unwrap());
// let mut corners = [&points[0]; 4];
// corners[0] = &points[0];
// // Find the opposite corner by looking for the farthest point near the opposite point
// let opposite_corner = points.iter().take(points.len() / 2).max_by(|p| dist2center(p, corners[0]));
// corners[2] = opposite_corner;
// // Find the two in between corners by looking for the points farthest from the long diagonal
// let dist2diagonal = |l: &RegressionLine, a| l.distance(a);
// corners[1] = points.iter().take(points.len() / 2).max_by(|p| dist2diagonal(&RegressionLine(*corners[0], *corners[2]), *p));
// corners[3] = points.iter().skip(points.len() / 2).max_by(|p| dist2diagonal(&RegressionLine(*corners[0], *corners[2]), *p));
// let lines = [
// RegressionLine::new(corners[0], corners[1]),
// RegressionLine::new(corners[1], corners[2]),
// RegressionLine::new(corners[2], corners[3]),
// RegressionLine::new(corners[3], corners[0]),
// ];
// if lines.iter().any(|l| !l.is_valid()) {
// return None;
// }
// let mut res = QuadrilateralF::new();
// for i in 0..4 {
// res[i] = intersect(lines[i], lines[(i + 1) % 4]);
// }
// Some(res)
// }
pub fn QuadrilateralIsPlausibleSquare(q: &Quadrilateral, lineIndex: usize) -> bool {
let mut m;
@@ -593,11 +560,7 @@ impl ConcentricPattern {
}
}
pub fn LocateConcentricPattern<
const RELAXED_THRESHOLD: bool,
const LEN: usize,
const SUM: usize,
>(
pub fn LocateConcentricPattern<const E2E: bool, const LEN: usize, const SUM: usize>(
image: &BitMatrix,
pattern: &Pattern<LEN>,
center: Point,
@@ -606,31 +569,38 @@ pub fn LocateConcentricPattern<
let mut cur = EdgeTracer::new(image, center, Point::default());
let mut minSpread = image.getWidth() as i32;
let mut maxSpread = 0_i32;
// TODO: setting maxError to 1 can subtantially help with detecting symbols with low print quality resulting in damaged
// finder patterns, but it sutantially increases the runtime (approx. 20% slower for the falsepositive images).
let mut maxError = 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::<RELAXED_THRESHOLD, LEN, SUM, _>(&mut cur, pattern, range, true);
if spread == 0 {
return None;
let spread = CheckSymmetricPattern::<E2E, LEN, SUM, _>(&mut cur, pattern, range, true);
if spread != 0 {
UpdateMinMax(&mut minSpread, &mut maxSpread, spread);
} else {
maxError -= 1;
if maxError < 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::<RELAXED_THRESHOLD, LEN, SUM, _>(
&mut cur,
pattern,
range * 2,
false,
);
if spread == 0 {
return None;
let spread = CheckSymmetricPattern::<E2E, LEN, SUM, _>(&mut cur, pattern, range * 2, false);
if spread != 0 {
UpdateMinMax(&mut minSpread, &mut maxSpread, spread);
} else {
maxError -= 1;
if maxError < 0 {
return None;
}
}
UpdateMinMax(&mut minSpread, &mut maxSpread, spread);
}
//#endif
@@ -645,3 +615,51 @@ pub fn LocateConcentricPattern<
size: (maxSpread + minSpread) / 2,
})
}
pub fn FinetuneConcentricPatternCenter(
image: &BitMatrix,
center: Point,
range: i32,
finderPatternSize: u32,
) -> Option<Point> {
// make sure we have at least one path of white around the center
if let Some(res1) = CenterOfRing(image, center.floor(), range, 1, true) {
if !image.get_point(res1) {
return None;
}
// and then either at least one more ring around that
if let Some(res2) = CenterOfRings(image, res1, range, finderPatternSize / 2) {
return Some(res2);
}
// or the center can be approximated by a square
if (FitSquareToPoints(image, res1, range, 1, false).is_some()) {
return Some(res1);
}
// TODO: this is currently only keeping #258 alive, evaluate if still worth it
if let Some(res2) =
CenterOfDoubleCross(image, res1.floor(), range, finderPatternSize / 2 + 1)
{
return Some(res2);
}
}
None
// // make sure we have at least one path of white around the center
// let res = CenterOfRing(image, center, range, 1, false)?;
// let center = res;
// let mut res = CenterOfRings(image, center, range, finderPatternSize / 2);
// if res.is_none() || !image.get_point(res?) {
// res = CenterOfDoubleCross(image, center, range, finderPatternSize / 2 + 1);
// }
// if res.is_none() || !image.get_point(res?) {
// res = Some(center);
// }
// if res.is_none() || !image.get_point(res?) {
// return None;
// }
// res
}

View File

@@ -11,6 +11,21 @@ use crate::{
pub type PatternType = u16;
pub type Pattern<const N: usize> = [PatternType; N];
fn BarAndSpaceSum<
const LEN: usize,
T: Into<RT> + Copy,
RT: Default + std::cmp::PartialEq + std::ops::AddAssign,
>(
view: &[T],
) -> BarAndSpace<RT> {
let mut res = BarAndSpace::default();
for i in 0..LEN {
// for (int i = 0; i < LEN; ++i)
res[i] += view[i].into();
}
res
}
#[derive(Default, Debug)]
pub struct PatternRow(Vec<PatternType>);
@@ -341,6 +356,7 @@ impl<'a> std::ops::Index<i32> for PatternView<'_> {
*
* The operator[](int) can be used in combination with a PatternView
*/
#[derive(Default)]
struct BarAndSpace<T: Default + std::cmp::PartialEq> {
bar: T,
space: T,
@@ -355,7 +371,7 @@ impl<T: Default + std::cmp::PartialEq> std::ops::Index<usize> for BarAndSpace<T>
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
match index {
match index & 1 {
0 => &self.bar,
1 => &self.space,
_ => panic!("Index out of range for BarAndSpace"),
@@ -365,7 +381,7 @@ impl<T: Default + std::cmp::PartialEq> std::ops::Index<usize> for BarAndSpace<T>
impl<T: Default + std::cmp::PartialEq> std::ops::IndexMut<usize> for BarAndSpace<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
match index & 1 {
0 => &mut self.bar,
1 => &mut self.space,
_ => panic!("Index out of range for BarAndSpace"),
@@ -418,6 +434,10 @@ impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> FixedPattern<N, SU
fn size(&self) -> usize {
N
}
fn sums(&self) -> BarAndSpace<PatternType> {
return BarAndSpaceSum::<N, PatternType, PatternType>(&self.data);
}
}
impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> std::ops::Index<usize>
@@ -445,17 +465,58 @@ pub type FixedSparcePattern<const N: usize, const SUM: usize> = FixedPattern<N,
// template <int N, int SUM>
// using FixedSparcePattern = FixedPattern<N, SUM, true>;
pub fn IsPattern<const LEN: usize, const SUM: usize, const SPARSE: bool>(
pub fn IsPattern<const E2E: bool, const LEN: usize, const SUM: usize, const SPARSE: bool>(
view: &PatternView,
pattern: &FixedPattern<LEN, SUM, SPARSE>,
space_in_pixel: Option<f32>,
min_quiet_zone: f32,
module_size_ref: f32,
relaxed_threshold: Option<bool>,
// e2e: Option<bool>,
) -> f32 {
let relaxed_threshold = relaxed_threshold.unwrap_or(false);
let e2e = E2E; //e2e.unwrap_or(false);
let mut module_size_ref = module_size_ref;
if (e2e) {
//using float_t = double;
let widths = BarAndSpaceSum::<LEN, PatternType, f64>(&view.data().0);
let sums = pattern.sums();
let modSize: BarAndSpace<f64> = BarAndSpace {
bar: widths[0] / sums[0] as f64,
space: widths[1] / sums[1] as f64,
};
let [m, M] = [
f64::min(modSize[0], modSize[1]),
f64::max(modSize[0], modSize[1]),
];
if (M > 4.0 * m) {
// make sure module sizes of bars and spaces are not too far away from each other
return 0.0;
}
if (min_quiet_zone != 0.0
&& (space_in_pixel.unwrap_or_default()) < min_quiet_zone * modSize.space as f32)
{
return 0.0;
}
let thr: BarAndSpace<f64> = BarAndSpace {
bar: modSize[0] * 0.75 + 0.5,
space: modSize[1] / (2.0 + f64::from(LEN < 6)) + 0.5,
};
for x in 0..LEN {
// for (int x = 0; x < LEN; ++x){
if (view[x] as f64 - pattern[x] as f64 * modSize[x]).abs() > thr[x] {
return 0.0;
}
}
let moduleSize: f64 = (modSize[0] + modSize[1]) / 2.0;
return moduleSize as f32;
}
let width = view.sum(Some(LEN));
if SUM > LEN && Into::<usize>::into(width) < SUM {
return 0.0;
@@ -473,7 +534,7 @@ pub fn IsPattern<const LEN: usize, const SUM: usize, const SPARSE: bool>(
module_size_ref = module_size;
}
let threshold = module_size_ref * (0.5 + (relaxed_threshold as u8) as f32 * 0.25) + 0.5;
let threshold = module_size_ref * (0.5 + (e2e as u8) as f32 * 0.25) + 0.5;
// the offset of 0.5 is to make the code less sensitive to quantization errors for small (near 1) module sizes.
// TODO: review once we have upsampling in the binarizer in place.
@@ -501,13 +562,15 @@ pub fn IsRightGuard<const N: usize, const SUM: usize, const IS_SPARCE: bool>(
Some(view.end().unwrap().into())
};
IsPattern(
const E2E: bool = false;
IsPattern::<E2E, N, SUM, IS_SPARCE>(
view,
pattern,
spaceInPixel,
minQuietZone,
moduleSizeRef,
None,
// None,
) != 0.0
}
@@ -558,7 +621,13 @@ pub fn FindLeftGuard<'a, const LEN: usize, const SUM: usize, const IS_SPARCE: bo
{
return false;
}
return IsPattern(window, pattern, spaceInPixel, minQuietZone, 0.0, None) != 0.0;
return IsPattern::<false, LEN, SUM, IS_SPARCE>(
window,
pattern,
spaceInPixel,
minQuietZone,
0.0,
) != 0.0;
})
}