mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-27 21:02:35 +00:00
does not build, mostly ported concentric_finder
This commit is contained in:
@@ -78,6 +78,7 @@ pub trait BitMatrixCursor {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
fn movedBy<T: BitMatrixCursor>(self, d: Point) -> Self;
|
fn movedBy<T: BitMatrixCursor>(self, d: Point) -> Self;
|
||||||
|
fn turnedBack(&self) -> Self;// { return {*img, p, back()}; }
|
||||||
// {
|
// {
|
||||||
// auto res = *this;
|
// auto res = *this;
|
||||||
// res.p += d;
|
// res.p += d;
|
||||||
@@ -160,6 +161,8 @@ pub trait BitMatrixCursor {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn p(&self) -> Point;
|
||||||
|
|
||||||
// template<typename ARRAY>
|
// template<typename ARRAY>
|
||||||
// ARRAY readPattern(int range = 0)
|
// ARRAY readPattern(int range = 0)
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -1 +1,532 @@
|
|||||||
|
use crate::{
|
||||||
|
common::{
|
||||||
|
cpp_essentials::{
|
||||||
|
Direction, FixedPattern, IsPattern, PatternRow, PatternType, PatternView,
|
||||||
|
},
|
||||||
|
BitMatrix, Quadrilateral,
|
||||||
|
},
|
||||||
|
point, Point,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
BitMatrixCursor, DMRegressionLine, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn CenterFromEnd<const N: usize, T: Into<f32> + std::iter::Sum<T> + Copy>(
|
||||||
|
pattern: &[T; N],
|
||||||
|
end: f32,
|
||||||
|
) -> f32 {
|
||||||
|
if (N == 5) {
|
||||||
|
let a: f32 = pattern[4].into() + pattern[3].into() + pattern[2].into() / 2.0;
|
||||||
|
let b: f32 =
|
||||||
|
pattern[4].into() + (pattern[3].into() + pattern[2].into() + pattern[1].into()) / 2.0;
|
||||||
|
let c: f32 = (pattern[4].into()
|
||||||
|
+ pattern[3].into()
|
||||||
|
+ pattern[2].into()
|
||||||
|
+ pattern[1].into()
|
||||||
|
+ pattern[0].into())
|
||||||
|
/ 2.0;
|
||||||
|
end - (2.0 * a + b + c) / 4.0
|
||||||
|
} else if (N == 3) {
|
||||||
|
let a: f32 = pattern[2].into() + pattern[1].into() / 2.0;
|
||||||
|
let b: f32 = (pattern[2].into() + pattern[1].into() + pattern[0].into()) / 2.0;
|
||||||
|
end - (2.0 * a + b) / 3.0
|
||||||
|
} else {
|
||||||
|
// aztec
|
||||||
|
let a: f32 =
|
||||||
|
pattern.iter().skip(N / 2 + 1).copied().sum::<T>().into() + pattern[N / 2].into() / 2.0;
|
||||||
|
// let a = std::accumulate(pattern.begin() + (N/2 + 1), pattern.end(), pattern[N/2] / 2.0);
|
||||||
|
end - a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ReadSymmetricPattern<const N: usize, Cursor: BitMatrixCursor>(
|
||||||
|
cur: &mut Cursor,
|
||||||
|
range: i32,
|
||||||
|
) -> Option<Pattern<N>> {
|
||||||
|
assert!(N % 2 == 1);
|
||||||
|
|
||||||
|
assert!(range > 0);
|
||||||
|
|
||||||
|
let mut range = range;
|
||||||
|
|
||||||
|
let mut res: Pattern<N> = [0; N];
|
||||||
|
let s_2 = res.len() as isize / 2;
|
||||||
|
let mut cuo = cur.turnedBack();
|
||||||
|
|
||||||
|
let mut next = |cur: &mut Cursor, i: isize| {
|
||||||
|
let v = cur.stepToEdge(Some(1), Some(range), None);
|
||||||
|
res[(s_2 + i) as usize] = (res[(s_2 + i) as usize] as i32 + v) as u16;
|
||||||
|
// res[(s_2 + i) as usize] += v;
|
||||||
|
if (range != 0) {
|
||||||
|
range -= v;
|
||||||
|
}
|
||||||
|
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..=s_2 {
|
||||||
|
// for (int i = 0; i <= s_2; ++i) {
|
||||||
|
if (!next(cur, i) != 0 || !next(&mut cuo, -i) != 0) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res[s_2 as usize] -= 1; // the starting pixel has been counted twice, fix this
|
||||||
|
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// default for RELAXED_THRESHOLD should be false
|
||||||
|
pub fn CheckSymmetricPattern<
|
||||||
|
const RELAXED_THRESHOLD: bool,
|
||||||
|
const LEN: usize,
|
||||||
|
const SUM: usize,
|
||||||
|
T: BitMatrixCursor,
|
||||||
|
>(
|
||||||
|
cur: &mut T,
|
||||||
|
pattern: &Pattern<LEN>,
|
||||||
|
range: i32,
|
||||||
|
updatePosition: bool,
|
||||||
|
) -> i32 {
|
||||||
|
let mut range = range;
|
||||||
|
|
||||||
|
let curFwd: FastEdgeToEdgeCounter = FastEdgeToEdgeCounter::new(cur);
|
||||||
|
let curBwd: FastEdgeToEdgeCounter = FastEdgeToEdgeCounter::new(&cur.turnedBack());
|
||||||
|
|
||||||
|
let centerFwd = curFwd.stepToNextEdge(range);
|
||||||
|
if (!(centerFwd != 0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let centerBwd = curBwd.stepToNextEdge(range);
|
||||||
|
if (!(centerBwd != 0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(range > 0);
|
||||||
|
let mut res: PatternRow = PatternRow::new(vec![0; LEN]);
|
||||||
|
let s_2 = (res.len()) / 2;
|
||||||
|
res[s_2] = (centerFwd + centerBwd - 1) as u16; // -1 because the starting pixel is counted twice
|
||||||
|
range -= res[s_2] as i32;
|
||||||
|
|
||||||
|
let mut next = |cur: &FastEdgeToEdgeCounter, i: isize| {
|
||||||
|
let v = cur.stepToNextEdge(range);
|
||||||
|
res[(s_2 as isize + i) as usize] = v as u16;
|
||||||
|
range -= v;
|
||||||
|
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 1..=s_2 {
|
||||||
|
// for (int i = 1; i <= s_2; ++i) {
|
||||||
|
if (!(next(&curFwd, i as isize) != 0) || !(next(&curBwd, -(i as isize)) != 0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(IsPattern(
|
||||||
|
&PatternView::new(&res),
|
||||||
|
&FixedPattern::<LEN, SUM, false>::with_reference(pattern),
|
||||||
|
None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Some(RELAXED_THRESHOLD),
|
||||||
|
) != 0.0))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatePosition) {
|
||||||
|
cur.step(Some((res[s_2] as i32 / 2 - (centerBwd as i32 - 1)) as f32));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.into_iter().sum::<PatternType>() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn AverageEdgePixels<T: BitMatrixCursor>(
|
||||||
|
cur: &mut T,
|
||||||
|
range: i32,
|
||||||
|
numOfEdges: u32,
|
||||||
|
) -> Option<Point> {
|
||||||
|
let mut sum = Point::default();
|
||||||
|
|
||||||
|
for i in 0..numOfEdges {
|
||||||
|
// for (int i = 0; i < numOfEdges; ++i) {
|
||||||
|
if (!cur.isInSelf()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
cur.stepToEdge(Some(1), Some(range), None);
|
||||||
|
sum += cur.p().centered() + (cur.p() + cur.back()).centered()
|
||||||
|
// sum += centered(cur.p) + centered(cur.p + cur.back());
|
||||||
|
// log(cur.p + cur.back(), 2);
|
||||||
|
}
|
||||||
|
Some(sum / (2 * numOfEdges) as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn CenterOfDoubleCross(
|
||||||
|
image: &BitMatrix,
|
||||||
|
center: Point,
|
||||||
|
range: i32,
|
||||||
|
numOfEdges: u32,
|
||||||
|
) -> Option<Point> {
|
||||||
|
let mut sum = Point::default();
|
||||||
|
|
||||||
|
for d in [
|
||||||
|
point(0.0, 1.0),
|
||||||
|
point(1.0, 0.0),
|
||||||
|
point(1.0, 1.0),
|
||||||
|
point(1.0, -1.0),
|
||||||
|
] {
|
||||||
|
// for (auto d : {PointI{0, 1}, {1, 0}, {1, 1}, {1, -1}}) {
|
||||||
|
let avr1 = AverageEdgePixels(&mut EdgeTracer::new(image, center, d), range, numOfEdges)?;
|
||||||
|
let avr2 = AverageEdgePixels(&mut EdgeTracer::new(image, center, -d), range, numOfEdges)?;
|
||||||
|
|
||||||
|
sum += avr1 + avr2;
|
||||||
|
}
|
||||||
|
Some(sum / 8.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn CenterOfRing(
|
||||||
|
image: &BitMatrix,
|
||||||
|
center: Point,
|
||||||
|
range: i32,
|
||||||
|
nth: i32,
|
||||||
|
requireCircle: bool,
|
||||||
|
) -> Option<Point> {
|
||||||
|
// range is the approximate width/height of the nth ring, if nth>1 then it would be plausible to limit the search radius
|
||||||
|
// to approximately range / 2 * sqrt(2) == range * 0.75 but it turned out to be too limiting with realworld/noisy data.
|
||||||
|
let radius = range;
|
||||||
|
let inner = nth < 0;
|
||||||
|
let nth = nth.abs();
|
||||||
|
// log(center, 3);
|
||||||
|
let mut cur = EdgeTracer::new(image, center, point(0.0, 1.0));
|
||||||
|
if (!(cur.stepToEdge(Some(nth), Some(radius), Some(inner)) != 0)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup
|
||||||
|
let edgeDir = if inner {
|
||||||
|
Direction::Left
|
||||||
|
} else {
|
||||||
|
Direction::Right
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut neighbourMask = 0;
|
||||||
|
let start = cur.p();
|
||||||
|
let mut sum = Point::default();
|
||||||
|
let mut n = 0;
|
||||||
|
loop {
|
||||||
|
// log(cur.p, 4);
|
||||||
|
sum += cur.p().centered();
|
||||||
|
n += 1;
|
||||||
|
|
||||||
|
// find out if we come full circle around the center. 8 bits have to be set in the end.
|
||||||
|
neighbourMask |= (1
|
||||||
|
<< (4.0 + Point::dot(Point::bresenhamDirection(cur.p() - center), point(1.0, 3.0)))
|
||||||
|
as u32);
|
||||||
|
|
||||||
|
if (!cur.stepAlongEdge(edgeDir, None)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate
|
||||||
|
if (Point::maxAbsComponent(cur.p - center) > radius as f32
|
||||||
|
|| center == cur.p
|
||||||
|
|| n > 4 * 2 * range)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(cur.p != start) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} //while (cur.p != start);
|
||||||
|
|
||||||
|
if (requireCircle && neighbourMask != 0b111101111) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(sum / n as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn CenterOfRings(
|
||||||
|
image: &BitMatrix,
|
||||||
|
center: Point,
|
||||||
|
range: i32,
|
||||||
|
numOfRings: u32,
|
||||||
|
) -> Option<Point> {
|
||||||
|
let mut n = numOfRings;
|
||||||
|
let mut sum = numOfRings * center;
|
||||||
|
for i in 1..numOfRings {
|
||||||
|
// for (int i = 1; i < numOfRings; ++i) {
|
||||||
|
let c = CenterOfRing(image, center, range, i as i32 + 1, false)?;
|
||||||
|
|
||||||
|
// TODO: decide whether this wheighting depending on distance to the center is worth it
|
||||||
|
let weight = numOfRings - i;
|
||||||
|
sum += weight * c;
|
||||||
|
n += weight;
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
range: i32,
|
||||||
|
edgeIndex: i32,
|
||||||
|
backup: bool,
|
||||||
|
) -> Vec<Point> {
|
||||||
|
let centerI = center.round();
|
||||||
|
let radius = range;
|
||||||
|
let mut cur = EdgeTracer::new(image, centerI, point(0.0, 1.0));
|
||||||
|
if (!(cur.stepToEdge(Some(edgeIndex), Some(radius), Some(backup)) != 0)) {
|
||||||
|
return Vec::default();
|
||||||
|
}
|
||||||
|
cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup
|
||||||
|
let edgeDir = if backup {
|
||||||
|
Direction::Left
|
||||||
|
} else {
|
||||||
|
Direction::Right
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut neighbourMask = 0;
|
||||||
|
let start = cur.p();
|
||||||
|
let mut points = Vec::<Point>::with_capacity(4 * range as usize);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// log(cur.p, 4);
|
||||||
|
points.push((cur.p().centered()));
|
||||||
|
|
||||||
|
// find out if we come full circle around the center. 8 bits have to be set in the end.
|
||||||
|
neighbourMask |= (1
|
||||||
|
<< (4.0 + Point::dot(Point::bresenhamDirection(cur.p - centerI), point(1.0, 3.0)))
|
||||||
|
as u32);
|
||||||
|
|
||||||
|
if (!cur.stepAlongEdge(edgeDir, None)) {
|
||||||
|
return Vec::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
// use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate
|
||||||
|
if (Point::maxAbsComponent(cur.p - centerI) > radius as f32
|
||||||
|
|| centerI == cur.p
|
||||||
|
|| (points).len() > 4 * 2 * range as usize)
|
||||||
|
{
|
||||||
|
return Vec::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(cur.p != start) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} //while (cur.p != start);
|
||||||
|
|
||||||
|
if (neighbourMask != 0b111101111) {
|
||||||
|
return Vec::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
points
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn FitQadrilateralToPoints(center: Point, points: &mut [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 max_by_pred = |a: &Point, b: &Point| {
|
||||||
|
if dist2Center(*a, *b) {
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
} else {
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let max = points.iter().copied().max_by(max_by_pred)?;
|
||||||
|
|
||||||
|
let pos = points.iter().position(|e| *e == max)?;
|
||||||
|
|
||||||
|
points.rotate_left(pos);
|
||||||
|
// std::rotate(points.begin(), std::max_element(points.begin(), points.end(), dist2Center), points.end());
|
||||||
|
|
||||||
|
let mut corners = [Point::default(); 4];
|
||||||
|
corners[0] = points[0];
|
||||||
|
// find the oposite corner by looking for the farthest point near the oposite point
|
||||||
|
points[(points.len() * 3 / 8)..=(points.len() * 5 / 8)]
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by(max_by_pred)?;
|
||||||
|
// corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center);
|
||||||
|
// find the two in between corners by looking for the points farthest from the long diagonal
|
||||||
|
let l = DMRegressionLine::with_two_points(corners[0], corners[2]);
|
||||||
|
let dist2Diagonal = /*[l = RegressionLine(*corners[0], *corners[2])]*/| a, b| { l.distance_single(a) < l.distance_single(b) };
|
||||||
|
|
||||||
|
let diagonal_max_by_pred = |p1: &Point, p2: &Point| {
|
||||||
|
if dist2Diagonal(*p1, *p2) {
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
} else {
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
}
|
||||||
|
};
|
||||||
|
corners[1] = points[(points.len() * 1 / 8)..=(points.len() * 3 / 8)]
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by(diagonal_max_by_pred)?;
|
||||||
|
// corners[1] = std::max_element(&points[Size(points) * 1 / 8], &points[Size(points) * 3 / 8], dist2Diagonal);
|
||||||
|
corners[3] = points[(points.len() * 5 / 8)..=(points.len() * 7 / 8)]
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by(diagonal_max_by_pred)?;
|
||||||
|
// corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal);
|
||||||
|
|
||||||
|
let lines = [
|
||||||
|
DMRegressionLine::with_two_points((corners[0] + 1.0), corners[1]),
|
||||||
|
DMRegressionLine::with_two_points((corners[1] + 1.0), corners[2]),
|
||||||
|
DMRegressionLine::with_two_points((corners[2] + 1.0), corners[3]),
|
||||||
|
DMRegressionLine::with_two_points((corners[3] + 1.0), (*points.last()? + 1.0)),
|
||||||
|
];
|
||||||
|
// std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]},
|
||||||
|
// RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}};
|
||||||
|
if lines.iter().any(|line| !line.isValid()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = Quadrilateral::default();
|
||||||
|
for i in 0..4 {
|
||||||
|
// for (int i = 0; i < 4; ++i) {
|
||||||
|
res[i] = DMRegressionLine::intersect(&lines[i], &lines[(i + 1) % 4])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn QuadrilateralIsPlausibleSquare(q: &Quadrilateral, lineIndex: usize) -> bool {
|
||||||
|
let mut m = f64::default();
|
||||||
|
// let mut M = f64::default();
|
||||||
|
|
||||||
|
m = Point::distance(q[0], q[3]) as f64; //M = distance(q[0], q[3]);
|
||||||
|
let mut M = m;
|
||||||
|
|
||||||
|
for i in 1..4 {
|
||||||
|
// for (int i = 1; i < 4; ++i)
|
||||||
|
|
||||||
|
UpdateMinMaxFloat(&mut m, &mut M, Point::distance(q[i - 1], q[i]) as f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
m >= (lineIndex * 2) as f64 && m > M / 3.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn FitSquareToPoints(
|
||||||
|
image: &BitMatrix,
|
||||||
|
center: Point,
|
||||||
|
range: i32,
|
||||||
|
lineIndex: i32,
|
||||||
|
backup: bool,
|
||||||
|
) -> Option<Quadrilateral> {
|
||||||
|
let mut points = CollectRingPoints(image, center, range, lineIndex, backup);
|
||||||
|
if (points.is_empty()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = FitQadrilateralToPoints(center, &mut points)?;
|
||||||
|
if (!QuadrilateralIsPlausibleSquare(&res, (lineIndex - i32::from(backup)) as usize)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn FindConcentricPatternCorners(
|
||||||
|
image: &BitMatrix,
|
||||||
|
center: Point,
|
||||||
|
range: i32,
|
||||||
|
lineIndex: i32,
|
||||||
|
) -> Option<Quadrilateral> {
|
||||||
|
let innerCorners = FitSquareToPoints(image, center, range, lineIndex, false)?;
|
||||||
|
|
||||||
|
let outerCorners = FitSquareToPoints(image, center, range, lineIndex + 1, true)?;
|
||||||
|
|
||||||
|
let res = Quadrilateral::blend(&innerCorners, &outerCorners);
|
||||||
|
|
||||||
|
// for p in innerCorners{
|
||||||
|
// log(p, 3);}
|
||||||
|
|
||||||
|
// for p in outerCorners{
|
||||||
|
// log(p, 3);}
|
||||||
|
|
||||||
|
// for p in res{
|
||||||
|
// log(p, 3);}
|
||||||
|
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ConcentricPattern {
|
||||||
|
p: Point,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn LocateConcentricPattern<const RELAXED_THRESHOLD:bool, const LEN: usize,
|
||||||
|
const SUM: usize,
|
||||||
|
T: BitMatrixCursor>( image:&BitMatrix, pattern:&Pattern<LEN>, center:Point, range:i32) -> Option<ConcentricPattern>
|
||||||
|
{
|
||||||
|
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}}) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#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}
|
||||||
|
|
||||||
|
let newCenter = FinetuneConcentricPatternCenter(image, cur.p(), range, pattern.len() as u32)?;
|
||||||
|
|
||||||
|
Some(ConcentricPattern{*newCenter, (maxSpread + minSpread) / 2})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn UpdateMinMax<T: Ord + Copy>(min: &mut T, max: &mut T, val: T) {
|
||||||
|
*min = std::cmp::min(*min, val);
|
||||||
|
*max = std::cmp::max(*max, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn UpdateMinMaxFloat(min: &mut f64, max: &mut f64, val: f64) {
|
||||||
|
*min = f64::min(*min, val);
|
||||||
|
*max = f64::max(*max, val);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::common::Result;
|
use crate::common::Result;
|
||||||
use crate::{Exceptions, Point};
|
use crate::{Exceptions, Point, point};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
util::{float_max, float_min},
|
util::{float_max, float_min},
|
||||||
@@ -211,9 +211,27 @@ impl RegressionLine for DMRegressionLine {
|
|||||||
Point::dot(self.direction_inward, self.normal()) > 0.5
|
Point::dot(self.direction_inward, self.normal()) > 0.5
|
||||||
// angle between original and new direction is at most 60 degree
|
// 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 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 <typename Container, typename Filter>
|
// template <typename Container, typename Filter>
|
||||||
fn average<T>(c: &[f64], f: T) -> f64
|
fn average<T>(c: &[f64], f: T) -> f64
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -126,6 +126,13 @@ impl BitMatrixCursor for EdgeTracer<'_> {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn turnedBack(&self) -> Self {
|
||||||
|
let mut res = self.clone();
|
||||||
|
res.d = res.back();
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief stepToEdge advances cursor to one step behind the next (or n-th) edge.
|
* @brief stepToEdge advances cursor to one step behind the next (or n-th) edge.
|
||||||
* @param nth number of edges to pass
|
* @param nth number of edges to pass
|
||||||
@@ -134,9 +141,9 @@ impl BitMatrixCursor for EdgeTracer<'_> {
|
|||||||
* @return number of steps taken or 0 if moved outside of range/image
|
* @return number of steps taken or 0 if moved outside of range/image
|
||||||
*/
|
*/
|
||||||
fn stepToEdge(&mut self, nth: Option<i32>, range: Option<i32>, backup: Option<bool>) -> i32 {
|
fn stepToEdge(&mut self, nth: Option<i32>, range: Option<i32>, backup: Option<bool>) -> i32 {
|
||||||
let mut nth = if let Some(nth) = nth { nth } else { 1 };
|
let mut nth = nth.unwrap_or(1); //if let Some(nth) = nth { nth } else { 1 };
|
||||||
let range = if let Some(r) = range { r } else { 0 };
|
let range = range.unwrap_or(0);//if let Some(r) = range { r } else { 0 };
|
||||||
let backup = if let Some(b) = backup { b } else { false };
|
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()
|
// TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt()
|
||||||
let mut steps = 0;
|
let mut steps = 0;
|
||||||
let mut lv = self.testAt(self.p);
|
let mut lv = self.testAt(self.p);
|
||||||
@@ -155,6 +162,11 @@ impl BitMatrixCursor for EdgeTracer<'_> {
|
|||||||
self.p += self.d * steps;
|
self.p += self.d * steps;
|
||||||
steps * i32::from(nth == 0)
|
steps * i32::from(nth == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn p(&self) -> Point {
|
||||||
|
self.p
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EdgeTracer<'_> {
|
impl<'a> EdgeTracer<'_> {
|
||||||
|
|||||||
40
src/common/cpp_essentials/fast_edge_to_edge_counter.rs
Normal file
40
src/common/cpp_essentials/fast_edge_to_edge_counter.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use super::BitMatrixCursor;
|
||||||
|
|
||||||
|
pub struct FastEdgeToEdgeCounter {
|
||||||
|
// const uint8_t* p = nullptr;
|
||||||
|
// int stride = 0;
|
||||||
|
// int stepsToBorder = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FastEdgeToEdgeCounter
|
||||||
|
{
|
||||||
|
pub fn new<T: BitMatrixCursor>(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;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
|
||||||
|
// p += steps * stride;
|
||||||
|
// stepsToBorder -= steps;
|
||||||
|
|
||||||
|
// return steps;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ pub mod regression_line;
|
|||||||
pub mod step_result;
|
pub mod step_result;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
pub mod fast_edge_to_edge_counter;
|
||||||
|
|
||||||
pub use bitmatrix_cursor::*;
|
pub use bitmatrix_cursor::*;
|
||||||
pub use concentric_finder::*;
|
pub use concentric_finder::*;
|
||||||
@@ -19,3 +20,4 @@ pub use regression_line::*;
|
|||||||
pub use step_result::*;
|
pub use step_result::*;
|
||||||
pub use util::*;
|
pub use util::*;
|
||||||
pub use value::*;
|
pub use value::*;
|
||||||
|
pub use fast_edge_to_edge_counter::*;
|
||||||
|
|||||||
@@ -8,17 +8,49 @@ use crate::{common::Result, Exceptions};
|
|||||||
pub type PatternType = u16;
|
pub type PatternType = u16;
|
||||||
pub type Pattern<const N: usize> = [PatternType; N];
|
pub type Pattern<const N: usize> = [PatternType; N];
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default,Debug)]
|
||||||
pub struct PatternRow(Vec<PatternType>);
|
pub struct PatternRow(Vec<PatternType>);
|
||||||
|
|
||||||
// pub struct PatternRow<T: std::iter::Sum + Into<f32> + Into<usize> + Copy>(Vec<T>);
|
// pub struct PatternRow<T: std::iter::Sum + Into<f32> + Into<usize> + Copy>(Vec<T>);
|
||||||
|
|
||||||
impl PatternRow {
|
impl PatternRow {
|
||||||
|
pub fn new( v: Vec<PatternType>) -> Self {
|
||||||
|
Self(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_pattern_view(&self) -> PatternView {
|
pub fn into_pattern_view(&self) -> PatternView {
|
||||||
PatternView::new(self)
|
PatternView::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for PatternRow {
|
||||||
|
type Item = PatternType;
|
||||||
|
|
||||||
|
type IntoIter = std::vec::IntoIter<PatternType>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<usize> for PatternRow {
|
||||||
|
type Output = PatternType;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<usize> for PatternRow {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for PatternView<'_> {
|
impl<'a> Iterator for PatternView<'_> {
|
||||||
type Item = PatternType;
|
type Item = PatternType;
|
||||||
|
|
||||||
@@ -33,6 +65,7 @@ impl<'a> Iterator for PatternView<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct PatternView<'a> {
|
pub struct PatternView<'a> {
|
||||||
data: &'a PatternRow,
|
data: &'a PatternRow,
|
||||||
start: usize,
|
start: usize,
|
||||||
@@ -96,7 +129,7 @@ impl<'a> PatternView<'_> {
|
|||||||
|
|
||||||
// index is the number of bars and spaces from the first bar to the current position
|
// index is the number of bars and spaces from the first bar to the current position
|
||||||
pub fn index(&self) -> usize {
|
pub fn index(&self) -> usize {
|
||||||
self.current - self.start - 1 /*return narrow_cast<int>(_data - _base) - 1;*/
|
self.current - self.start /*return narrow_cast<int>(_data - _base) - 1;*/
|
||||||
}
|
}
|
||||||
pub fn pixelsInFront(&self) -> PatternType {
|
pub fn pixelsInFront(&self) -> PatternType {
|
||||||
self.data
|
self.data
|
||||||
@@ -183,7 +216,7 @@ impl<'a> PatternView<'_> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn shift(&mut self, n: usize) -> bool {
|
pub fn shift(&mut self, n: usize) -> bool {
|
||||||
self.start += n;
|
self.current += n;
|
||||||
!self.data.0.is_empty() && self.start + self.count <= (self.start + self.count)
|
!self.data.0.is_empty() && self.start + self.count <= (self.start + self.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +267,15 @@ impl<'a> std::ops::Index<usize> for PatternView<'_> {
|
|||||||
if index > self.data.0.len() {
|
if index > self.data.0.len() {
|
||||||
panic!("array index out of bounds")
|
panic!("array index out of bounds")
|
||||||
}
|
}
|
||||||
self.data.0.get(self.start + self.current).unwrap()
|
self.data.0.get(self.start + self.current + index).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::ops::Index<i32> for PatternView<'_> {
|
||||||
|
type Output = PatternType;
|
||||||
|
|
||||||
|
fn index(&self, index: i32) -> &Self::Output {
|
||||||
|
std::ops::Index::<isize>::index(self, index as isize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +324,7 @@ impl<T: Default + std::cmp::PartialEq> std::ops::IndexMut<usize> for BarAndSpace
|
|||||||
// bool isValid() const { return bar != T{} && space != T{}; }
|
// bool isValid() const { return bar != T{} && space != T{}; }
|
||||||
// };
|
// };
|
||||||
|
|
||||||
type BarAndSpaceI = BarAndSpace<u16>;
|
type BarAndSpaceI = BarAndSpace<PatternType>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief FixedPattern describes a compile-time constant (start/stop) pattern.
|
* @brief FixedPattern describes a compile-time constant (start/stop) pattern.
|
||||||
@@ -293,15 +334,19 @@ type BarAndSpaceI = BarAndSpace<u16>;
|
|||||||
* @param IS_SPARCE whether or not the pattern contains '0's denoting 'wide' bars/spaces
|
* @param IS_SPARCE whether or not the pattern contains '0's denoting 'wide' bars/spaces
|
||||||
*/
|
*/
|
||||||
pub struct FixedPattern<const N: usize, const SUM: usize, const IS_SPARCE: bool = false> {
|
pub struct FixedPattern<const N: usize, const SUM: usize, const IS_SPARCE: bool = false> {
|
||||||
data: [u16; N],
|
data: [PatternType; N],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> FixedPattern<N, SUM, IS_SPARCE> {
|
impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> FixedPattern<N, SUM, IS_SPARCE> {
|
||||||
fn new(data: [u16; N]) -> Self {
|
pub fn new(data: [PatternType; N]) -> Self {
|
||||||
FixedPattern { data }
|
FixedPattern { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_slice(&self) -> &[u16] {
|
pub fn with_reference(data: &[PatternType; N]) -> Self {
|
||||||
|
FixedPattern { data: data.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_slice(&self) -> &[PatternType] {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +358,7 @@ impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> FixedPattern<N, SU
|
|||||||
impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> std::ops::Index<usize>
|
impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> std::ops::Index<usize>
|
||||||
for FixedPattern<N, SUM, IS_SPARCE>
|
for FixedPattern<N, SUM, IS_SPARCE>
|
||||||
{
|
{
|
||||||
type Output = u16;
|
type Output = PatternType;
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
&self.data[index]
|
&self.data[index]
|
||||||
@@ -419,10 +464,6 @@ pub fn FindLeftGuardBy<'a, const LEN: usize, Pred: Fn(&PatternView, Option<f32>)
|
|||||||
|
|
||||||
window.skipPair();
|
window.skipPair();
|
||||||
}
|
}
|
||||||
// for (auto end = view.end() - minSize; window.data() < end; window.skipPair())
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
Err(Exceptions::ILLEGAL_STATE)
|
Err(Exceptions::ILLEGAL_STATE)
|
||||||
}
|
}
|
||||||
@@ -483,7 +524,7 @@ pub fn NormalizedPattern<'a, const LEN: usize, const SUM: usize>(
|
|||||||
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||||
};
|
};
|
||||||
let mi = mi.ok_or(Exceptions::ILLEGAL_STATE)?;
|
let mi = mi.ok_or(Exceptions::ILLEGAL_STATE)?;
|
||||||
is[*mi as usize] += err as u16;
|
is[*mi as usize] += err as PatternType;
|
||||||
rs[*mi as usize] -= err as f32;
|
rs[*mi as usize] -= err as f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,17 +537,16 @@ enum Color {
|
|||||||
Black = 1
|
Black = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<u16>> From<T> for Color {
|
impl<T: Into<PatternType>> From<T> for Color {
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
match value.into() {
|
match value.into() {
|
||||||
0 => Color::White,
|
0 => Color::White,
|
||||||
_ => Color::Black,
|
_ => Color::Black,
|
||||||
_=>Color::Black
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetPatternRow<T: Into<u16> + Copy + Default + From<T>>(b_row: &[T], p_row: &mut PatternRow) {
|
fn GetPatternRow<T: Into<PatternType> + Copy + Default + From<T>>(b_row: &[T], p_row: &mut PatternRow) {
|
||||||
p_row.0.clear();
|
p_row.0.clear();
|
||||||
|
|
||||||
if Color::from(p_row.0.first().copied().unwrap_or_default()) == Color::Black {
|
if Color::from(p_row.0.first().copied().unwrap_or_default()) == Color::Black {
|
||||||
@@ -542,20 +582,22 @@ fn GetPatternRow<T: Into<u16> + Copy + Default + From<T>>(b_row: &[T], p_row: &m
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{GetPatternRow, PatternRow};
|
use crate::common::cpp_essentials::PatternType;
|
||||||
|
|
||||||
|
use super::{GetPatternRow, PatternRow, PatternView};
|
||||||
const N: usize = 33;
|
const N: usize = 33;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn all_white() {
|
fn all_white() {
|
||||||
for s in 1..=N {
|
for s in 1..=N {
|
||||||
// for (int s = 1; s <= N; ++s) {
|
// for (int s = 1; s <= N; ++s) {
|
||||||
let t_in = vec![0_u16; s];
|
let t_in:Vec<PatternType> = vec![0; s];
|
||||||
// std::vector<uint8_t> in(s, 0);
|
// std::vector<uint8_t> in(s, 0);
|
||||||
let mut pr = PatternRow::default();
|
let mut pr = PatternRow::default();
|
||||||
GetPatternRow(&t_in, &mut pr);
|
GetPatternRow(&t_in, &mut pr);
|
||||||
|
|
||||||
assert_eq!(pr.0.len(), 1);
|
assert_eq!(pr.0.len(), 1);
|
||||||
assert_eq!(pr.0[0], s as u16);
|
assert_eq!(pr.0[0], s as PatternType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,13 +605,13 @@ mod tests {
|
|||||||
fn all_black() {
|
fn all_black() {
|
||||||
for s in 1..=N {
|
for s in 1..=N {
|
||||||
// for (int s = 1; s <= N; ++s) {
|
// for (int s = 1; s <= N; ++s) {
|
||||||
let t_in: Vec<u16> = vec![0xff; s];
|
let t_in: Vec<PatternType> = vec![0xff; s];
|
||||||
let mut pr = PatternRow::default();
|
let mut pr = PatternRow::default();
|
||||||
GetPatternRow(&t_in, &mut pr);
|
GetPatternRow(&t_in, &mut pr);
|
||||||
|
|
||||||
assert_eq!(pr.0.len(), 3);
|
assert_eq!(pr.0.len(), 3);
|
||||||
assert_eq!(pr.0[0], 0);
|
assert_eq!(pr.0[0], 0);
|
||||||
assert_eq!(pr.0[1], s as u16);
|
assert_eq!(pr.0[1], s as PatternType);
|
||||||
assert_eq!(pr.0[2], 0);
|
assert_eq!(pr.0[2], 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,7 +620,7 @@ mod tests {
|
|||||||
fn black_white() {
|
fn black_white() {
|
||||||
for s in 1..=N {
|
for s in 1..=N {
|
||||||
// for (int s = 1; s <= N; ++s) {
|
// for (int s = 1; s <= N; ++s) {
|
||||||
let mut t_in = vec![0_u16; N];
|
let mut t_in : Vec<PatternType> = vec![0; N];
|
||||||
t_in[..s].copy_from_slice(&vec![1; s]);
|
t_in[..s].copy_from_slice(&vec![1; s]);
|
||||||
// std::fill_n(in.data(), s, 0xff);
|
// std::fill_n(in.data(), s, 0xff);
|
||||||
let mut pr = PatternRow::default();
|
let mut pr = PatternRow::default();
|
||||||
@@ -586,8 +628,8 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(pr.0.len(), 3);
|
assert_eq!(pr.0.len(), 3);
|
||||||
assert_eq!(pr.0[0], 0);
|
assert_eq!(pr.0[0], 0);
|
||||||
assert_eq!(pr.0[1], s as u16);
|
assert_eq!(pr.0[1], s as PatternType);
|
||||||
assert_eq!(pr.0[2], (N - s) as u16);
|
assert_eq!(pr.0[2], (N - s) as PatternType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,15 +637,36 @@ mod tests {
|
|||||||
fn white_black() {
|
fn white_black() {
|
||||||
for s in 0..N {
|
for s in 0..N {
|
||||||
// for (int s = 0; s < N; ++s) {
|
// for (int s = 0; s < N; ++s) {
|
||||||
let mut t_in: Vec<u16> = vec![0xff; N];
|
let mut t_in: Vec<PatternType> = vec![0xff; N];
|
||||||
t_in[..s].copy_from_slice(&vec![0; s]);
|
t_in[..s].copy_from_slice(&vec![0; s]);
|
||||||
let mut pr = PatternRow::default();
|
let mut pr = PatternRow::default();
|
||||||
GetPatternRow(&t_in, &mut pr);
|
GetPatternRow(&t_in, &mut pr);
|
||||||
|
|
||||||
assert_eq!(pr.0.len(), 3);
|
assert_eq!(pr.0.len(), 3);
|
||||||
assert_eq!(pr.0[0], s as u16);
|
assert_eq!(pr.0[0], s as PatternType);
|
||||||
assert_eq!(pr.0[1], (N - s) as u16);
|
assert_eq!(pr.0[1], (N - s) as PatternType);
|
||||||
assert_eq!(pr.0[2], 0);
|
assert_eq!(pr.0[2], 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
|
||||||
|
let mut pv = PatternView::new(&p_row);
|
||||||
|
|
||||||
|
assert_eq!(pv.data().0,p_row.0);
|
||||||
|
|
||||||
|
assert_eq!(pv[0], 1_u16);
|
||||||
|
assert_eq!(pv[1], 1_u16);
|
||||||
|
assert_eq!(pv[4], 2_u16);
|
||||||
|
assert_eq!(pv[7], 6_u16);
|
||||||
|
|
||||||
|
assert_eq!(pv.index(), 0);
|
||||||
|
assert!(pv.shift(1));
|
||||||
|
assert_eq!(pv.index(), 1);
|
||||||
|
assert!(pv.skipPair());
|
||||||
|
assert_eq!(pv.index(),3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::common::Result;
|
use crate::common::Result;
|
||||||
use crate::Point;
|
use crate::{Point, point};
|
||||||
|
|
||||||
pub trait RegressionLine {
|
pub trait RegressionLine {
|
||||||
// points: Vec<Point>,
|
// points: Vec<Point>,
|
||||||
@@ -11,8 +11,18 @@ pub trait RegressionLine {
|
|||||||
// PointF _directionInward;
|
// PointF _directionInward;
|
||||||
// PointF::value_t a = NAN, b = NAN, c = NAN;
|
// PointF::value_t a = NAN, b = NAN, c = NAN;
|
||||||
|
|
||||||
// fn intersect<T: RegressionLine, T2: RegressionLine>(&self, l1: &T, l2: &T2)
|
fn intersect<T: RegressionLine, T2: RegressionLine>( l1: &T, l2: &T2)
|
||||||
// -> Point;
|
-> Option<Point>{
|
||||||
|
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;// {
|
// fn evaluate_begin_end(&self, begin: Point, end: Point) -> bool;// {
|
||||||
// {
|
// {
|
||||||
@@ -131,4 +141,7 @@ pub trait RegressionLine {
|
|||||||
// // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal
|
// // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal
|
||||||
// return steps > 2 || len > 50;
|
// return steps > 2 || len > 50;
|
||||||
// }
|
// }
|
||||||
|
fn a(&self) -> f32;
|
||||||
|
fn b(&self) -> f32;
|
||||||
|
fn c(&self) -> f32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,28 @@ impl Quadrilateral {
|
|||||||
|
|
||||||
!(x || y)
|
!(x || y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}).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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Quadrilateral {
|
impl Default for Quadrilateral {
|
||||||
@@ -215,3 +237,17 @@ impl Default for Quadrilateral {
|
|||||||
Self([Point { x: 0.0, y: 0.0 }; 4])
|
Self([Point { x: 0.0, y: 0.0 }; 4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<usize> for Quadrilateral {
|
||||||
|
type Output = Point;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<usize> for Quadrilateral {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,6 +117,22 @@ impl std::ops::Add for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add<f32> for Point {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: f32) -> Self::Output {
|
||||||
|
Self::new(self.x + rhs, self.y + rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add<Point> for f32 {
|
||||||
|
type Output = Point;
|
||||||
|
|
||||||
|
fn add(self, rhs: Point) -> Self::Output {
|
||||||
|
Point::new(rhs.x + self, rhs.y + self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Mul for Point {
|
impl std::ops::Mul for Point {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@@ -141,6 +157,14 @@ impl std::ops::Mul<i32> for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<u32> for Point {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: u32) -> Self::Output {
|
||||||
|
Self::new(self.x * rhs as f32, self.y * rhs as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Mul<Point> for i32 {
|
impl std::ops::Mul<Point> for i32 {
|
||||||
type Output = Point;
|
type Output = Point;
|
||||||
|
|
||||||
@@ -165,6 +189,14 @@ impl std::ops::Div<f32> for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<Point> for u32 {
|
||||||
|
type Output = Point;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Point) -> Self::Output {
|
||||||
|
Self::Output::new(rhs.x * self as f32, rhs.y * self as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
pub fn dot(self, p: Self) -> f32 {
|
pub fn dot(self, p: Self) -> f32 {
|
||||||
self.x * p.x + self.y * p.y
|
self.x * p.x + self.y * p.y
|
||||||
|
|||||||
Reference in New Issue
Block a user