mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
incomplete port of detector
This commit is contained in:
@@ -31,6 +31,7 @@ svg = {version = "0.13", optional = true}
|
|||||||
resvg = {version = "0.28.0", optional = true, default-features=false}
|
resvg = {version = "0.28.0", optional = true, default-features=false}
|
||||||
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
|
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
|
||||||
thiserror = "1.0.38"
|
thiserror = "1.0.38"
|
||||||
|
multimap = "0.8.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
java-properties = "1.4.1"
|
java-properties = "1.4.1"
|
||||||
|
|||||||
@@ -401,3 +401,15 @@ impl Default for BitArray {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<Vec<u8>> for BitArray {
|
||||||
|
fn into(self) -> Vec<u8> {
|
||||||
|
let mut arr = vec![0; self.get_size()];
|
||||||
|
for x in 0..self.get_size() {
|
||||||
|
if self.get(x) {
|
||||||
|
arr[x] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -408,6 +408,21 @@ impl BitMatrix {
|
|||||||
rw
|
rw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method returns a column of the bitmatrix.
|
||||||
|
///
|
||||||
|
/// The current implementation may be very slow.
|
||||||
|
pub fn getCol(&self, x: u32) -> BitArray {
|
||||||
|
let mut cw = BitArray::with_size(self.height as usize);
|
||||||
|
|
||||||
|
for y in 0..self.height {
|
||||||
|
if self.get(x, y) {
|
||||||
|
cw.set(y as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cw
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param y row to set
|
* @param y row to set
|
||||||
* @param row {@link BitArray} to copy from
|
* @param row {@link BitArray} to copy from
|
||||||
@@ -595,6 +610,10 @@ impl BitMatrix {
|
|||||||
* @return The width of the matrix
|
* @return The width of the matrix
|
||||||
*/
|
*/
|
||||||
pub fn getWidth(&self) -> u32 {
|
pub fn getWidth(&self) -> u32 {
|
||||||
|
self.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> u32 {
|
||||||
self.width
|
self.width
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,6 +621,10 @@ impl BitMatrix {
|
|||||||
* @return The height of the matrix
|
* @return The height of the matrix
|
||||||
*/
|
*/
|
||||||
pub fn getHeight(&self) -> u32 {
|
pub fn getHeight(&self) -> u32 {
|
||||||
|
self.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,182 +1 @@
|
|||||||
use crate::Point;
|
|
||||||
|
|
||||||
use super::{util::opposite, Direction, Value};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The BitMatrixCursor represents a current position inside an image and current direction it can advance towards.
|
|
||||||
*
|
|
||||||
* The current position and direction is a PointT<T>. So depending on the type it can be used to traverse the image
|
|
||||||
* in a Bresenham style (PointF) or in a discrete way (step only horizontal/vertical/diagonal (PointI)).
|
|
||||||
*/
|
|
||||||
pub trait BitMatrixCursor {
|
|
||||||
// const BitMatrix* img;
|
|
||||||
|
|
||||||
// POINT p; // current position
|
|
||||||
// POINT d; // current direction
|
|
||||||
|
|
||||||
// BitMatrixCursor(const BitMatrix& image, POINT p, POINT d) : img(&image), p(p) { setDirection(d); }
|
|
||||||
|
|
||||||
fn testAt(&self, p: Point) -> Value; //const
|
|
||||||
// {
|
|
||||||
// return img->isIn(p) ? Value{img->get(p)} : Value{};
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn blackAt(&self, pos: Point) -> bool {
|
|
||||||
self.testAt(pos).isBlack()
|
|
||||||
}
|
|
||||||
fn whiteAt(&self, pos: Point) -> bool {
|
|
||||||
self.testAt(pos).isWhite()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isIn(&self, p: Point) -> bool; // { return img->isIn(p); }
|
|
||||||
fn isInSelf(&self) -> bool; // { return self.isIn(p); }
|
|
||||||
fn isBlack(&self) -> bool; // { return blackAt(p); }
|
|
||||||
fn isWhite(&self) -> bool; // { return whiteAt(p); }
|
|
||||||
|
|
||||||
fn front(&self) -> &Point; //{ return d; }
|
|
||||||
fn back(&self) -> Point; // { return {-d.x, -d.y}; }
|
|
||||||
fn left(&self) -> Point; //{ return {d.y, -d.x}; }
|
|
||||||
fn right(&self) -> Point; //{ return {-d.y, d.x}; }
|
|
||||||
fn direction(&self, dir: Direction) -> Point {
|
|
||||||
self.right() * Into::<i32>::into(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn turnBack(&mut self); // noexcept { d = back(); }
|
|
||||||
fn turnLeft(&mut self); //noexcept { d = left(); }
|
|
||||||
fn turnRight(&mut self); //noexcept { d = right(); }
|
|
||||||
fn turn(&mut self, dir: Direction); //noexcept { d = direction(dir); }
|
|
||||||
|
|
||||||
fn edgeAt_point(&self, d: Point) -> Value;
|
|
||||||
// {
|
|
||||||
// Value v = testAt(p);
|
|
||||||
// return testAt(p + d) != v ? v : Value();
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn edgeAtFront(&self) -> Value {
|
|
||||||
return self.edgeAt_point(*self.front());
|
|
||||||
}
|
|
||||||
fn edgeAtBack(&self) -> Value {
|
|
||||||
self.edgeAt_point(self.back())
|
|
||||||
}
|
|
||||||
fn edgeAtLeft(&self) -> Value {
|
|
||||||
self.edgeAt_point(self.left())
|
|
||||||
}
|
|
||||||
fn edgeAtRight(&self) -> Value {
|
|
||||||
self.edgeAt_point(self.right())
|
|
||||||
}
|
|
||||||
fn edgeAt_direction(&self, dir: Direction) -> Value {
|
|
||||||
self.edgeAt_point(self.direction(dir))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setDirection(&mut self, dir: Point); // { d = bresenhamDirection(dir); }
|
|
||||||
// fn setDirection(&self, dir: Point);// { d = dir; }
|
|
||||||
|
|
||||||
fn step(&mut self, s: Option<f32>) -> bool; // DEF to 1
|
|
||||||
// {
|
|
||||||
// p += s * d;
|
|
||||||
// return isIn(p);
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn movedBy<T: BitMatrixCursor>(self, d: Point) -> Self;
|
|
||||||
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.
|
|
||||||
* @param nth number of edges to pass
|
|
||||||
* @param range max number of steps to take
|
|
||||||
* @param backup whether or not to backup one step so we land in front of the edge
|
|
||||||
* @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(&self, int nth = 1, int range = 0, bool backup = false) -> i32
|
|
||||||
// {
|
|
||||||
// // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt()
|
|
||||||
// int steps = 0;
|
|
||||||
// auto lv = testAt(p);
|
|
||||||
|
|
||||||
// while (nth && (!range || steps < range) && lv.isValid()) {
|
|
||||||
// ++steps;
|
|
||||||
// auto v = testAt(p + steps * d);
|
|
||||||
// if (lv != v) {
|
|
||||||
// lv = v;
|
|
||||||
// --nth;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (backup)
|
|
||||||
// --steps;
|
|
||||||
// p += steps * d;
|
|
||||||
// return steps * (nth == 0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn stepAlongEdge(&mut self, dir: Direction, skipCorner: Option<bool>) -> bool
|
|
||||||
// fn stepAlongEdge(&self, dir:Direction, skipCorner:Option<bool> = false) -> bool
|
|
||||||
{
|
|
||||||
let skipCorner = if let Some(sc) = skipCorner { sc } else { false };
|
|
||||||
|
|
||||||
if !self.edgeAt_direction(dir).isValid() {
|
|
||||||
self.turn(dir);
|
|
||||||
} else if self.edgeAtFront().isValid() {
|
|
||||||
self.turn(opposite(dir));
|
|
||||||
if self.edgeAtFront().isValid() {
|
|
||||||
self.turn(opposite(dir));
|
|
||||||
if self.edgeAtFront().isValid() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ret = self.step(None);
|
|
||||||
|
|
||||||
if ret && skipCorner && !self.edgeAt_direction(dir).isValid() {
|
|
||||||
self.turn(dir);
|
|
||||||
ret = self.step(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn countEdges(&mut self, range: i32) -> i32 {
|
|
||||||
let mut res = 0;
|
|
||||||
let mut range = range;
|
|
||||||
|
|
||||||
let mut steps;
|
|
||||||
|
|
||||||
while {
|
|
||||||
steps = if range == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.stepToEdge(Some(1), Some(range), None)
|
|
||||||
};
|
|
||||||
steps > 0
|
|
||||||
} {
|
|
||||||
range -= steps;
|
|
||||||
res += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn p(&self) -> Point;
|
|
||||||
|
|
||||||
// template<typename ARRAY>
|
|
||||||
// ARRAY readPattern(int range = 0)
|
|
||||||
// {
|
|
||||||
// ARRAY res;
|
|
||||||
// for (auto& i : res)
|
|
||||||
// i = stepToEdge(1, range);
|
|
||||||
// return res;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// template<typename ARRAY>
|
|
||||||
// ARRAY readPatternFromBlack(int maxWhitePrefix, int range = 0)
|
|
||||||
// {
|
|
||||||
// if (maxWhitePrefix && isWhite() && !stepToEdge(1, maxWhitePrefix))
|
|
||||||
// return {};
|
|
||||||
// return readPattern<ARRAY>(range);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|||||||
184
src/common/cpp_essentials/bitmatrix_cursor_trait.rs
Normal file
184
src/common/cpp_essentials/bitmatrix_cursor_trait.rs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
use crate::Point;
|
||||||
|
|
||||||
|
use super::{util::opposite, Direction, Value};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The BitMatrixCursor represents a current position inside an image and current direction it can advance towards.
|
||||||
|
*
|
||||||
|
* The current position and direction is a PointT<T>. So depending on the type it can be used to traverse the image
|
||||||
|
* in a Bresenham style (PointF) or in a discrete way (step only horizontal/vertical/diagonal (PointI)).
|
||||||
|
*/
|
||||||
|
pub trait BitMatrixCursorTrait {
|
||||||
|
// const BitMatrix* img;
|
||||||
|
|
||||||
|
// POINT p; // current position
|
||||||
|
// POINT d; // current direction
|
||||||
|
|
||||||
|
// BitMatrixCursor(const BitMatrix& image, POINT p, POINT d) : img(&image), p(p) { setDirection(d); }
|
||||||
|
|
||||||
|
fn testAt(&self, p: Point) -> Value; //const
|
||||||
|
// {
|
||||||
|
// return img->isIn(p) ? Value{img->get(p)} : Value{};
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn blackAt(&self, pos: Point) -> bool {
|
||||||
|
self.testAt(pos).isBlack()
|
||||||
|
}
|
||||||
|
fn whiteAt(&self, pos: Point) -> bool {
|
||||||
|
self.testAt(pos).isWhite()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn isIn(&self, p: Point) -> bool; // { return img->isIn(p); }
|
||||||
|
fn isInSelf(&self) -> bool; // { return self.isIn(p); }
|
||||||
|
fn isBlack(&self) -> bool; // { return blackAt(p); }
|
||||||
|
fn isWhite(&self) -> bool; // { return whiteAt(p); }
|
||||||
|
|
||||||
|
fn front(&self) -> &Point; //{ return d; }
|
||||||
|
fn back(&self) -> Point; // { return {-d.x, -d.y}; }
|
||||||
|
fn left(&self) -> Point; //{ return {d.y, -d.x}; }
|
||||||
|
fn right(&self) -> Point; //{ return {-d.y, d.x}; }
|
||||||
|
fn direction(&self, dir: Direction) -> Point {
|
||||||
|
self.right() * Into::<i32>::into(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turnBack(&mut self); // noexcept { d = back(); }
|
||||||
|
fn turnLeft(&mut self); //noexcept { d = left(); }
|
||||||
|
fn turnRight(&mut self); //noexcept { d = right(); }
|
||||||
|
fn turn(&mut self, dir: Direction); //noexcept { d = direction(dir); }
|
||||||
|
|
||||||
|
fn edgeAt_point(&self, d: Point) -> Value;
|
||||||
|
// {
|
||||||
|
// Value v = testAt(p);
|
||||||
|
// return testAt(p + d) != v ? v : Value();
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn edgeAtFront(&self) -> Value {
|
||||||
|
return self.edgeAt_point(*self.front());
|
||||||
|
}
|
||||||
|
fn edgeAtBack(&self) -> Value {
|
||||||
|
self.edgeAt_point(self.back())
|
||||||
|
}
|
||||||
|
fn edgeAtLeft(&self) -> Value {
|
||||||
|
self.edgeAt_point(self.left())
|
||||||
|
}
|
||||||
|
fn edgeAtRight(&self) -> Value {
|
||||||
|
self.edgeAt_point(self.right())
|
||||||
|
}
|
||||||
|
fn edgeAt_direction(&self, dir: Direction) -> Value {
|
||||||
|
self.edgeAt_point(self.direction(dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setDirection(&mut self, dir: Point); // { d = bresenhamDirection(dir); }
|
||||||
|
// fn setDirection(&self, dir: Point);// { d = dir; }
|
||||||
|
|
||||||
|
fn step(&mut self, s: Option<f32>) -> bool; // DEF to 1
|
||||||
|
// {
|
||||||
|
// p += s * d;
|
||||||
|
// return isIn(p);
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn movedBy<T: BitMatrixCursorTrait>(self, d: Point) -> Self;
|
||||||
|
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.
|
||||||
|
* @param nth number of edges to pass
|
||||||
|
* @param range max number of steps to take
|
||||||
|
* @param backup whether or not to backup one step so we land in front of the edge
|
||||||
|
* @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(&self, int nth = 1, int range = 0, bool backup = false) -> i32
|
||||||
|
// {
|
||||||
|
// // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt()
|
||||||
|
// int steps = 0;
|
||||||
|
// auto lv = testAt(p);
|
||||||
|
|
||||||
|
// while (nth && (!range || steps < range) && lv.isValid()) {
|
||||||
|
// ++steps;
|
||||||
|
// auto v = testAt(p + steps * d);
|
||||||
|
// if (lv != v) {
|
||||||
|
// lv = v;
|
||||||
|
// --nth;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (backup)
|
||||||
|
// --steps;
|
||||||
|
// p += steps * d;
|
||||||
|
// return steps * (nth == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn stepAlongEdge(&mut self, dir: Direction, skipCorner: Option<bool>) -> bool
|
||||||
|
// fn stepAlongEdge(&self, dir:Direction, skipCorner:Option<bool> = false) -> bool
|
||||||
|
{
|
||||||
|
let skipCorner = if let Some(sc) = skipCorner { sc } else { false };
|
||||||
|
|
||||||
|
if !self.edgeAt_direction(dir).isValid() {
|
||||||
|
self.turn(dir);
|
||||||
|
} else if self.edgeAtFront().isValid() {
|
||||||
|
self.turn(opposite(dir));
|
||||||
|
if self.edgeAtFront().isValid() {
|
||||||
|
self.turn(opposite(dir));
|
||||||
|
if self.edgeAtFront().isValid() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = self.step(None);
|
||||||
|
|
||||||
|
if ret && skipCorner && !self.edgeAt_direction(dir).isValid() {
|
||||||
|
self.turn(dir);
|
||||||
|
ret = self.step(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn countEdges(&mut self, range: i32) -> i32 {
|
||||||
|
let mut res = 0;
|
||||||
|
let mut range = range;
|
||||||
|
|
||||||
|
let mut steps;
|
||||||
|
|
||||||
|
while {
|
||||||
|
steps = if range == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.stepToEdge(Some(1), Some(range), None)
|
||||||
|
};
|
||||||
|
steps > 0
|
||||||
|
} {
|
||||||
|
range -= steps;
|
||||||
|
res += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p(&self) -> Point;
|
||||||
|
|
||||||
|
fn d(&self) -> Point;
|
||||||
|
|
||||||
|
// template<typename ARRAY>
|
||||||
|
// ARRAY readPattern(int range = 0)
|
||||||
|
// {
|
||||||
|
// ARRAY res;
|
||||||
|
// for (auto& i : res)
|
||||||
|
// i = stepToEdge(1, range);
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// template<typename ARRAY>
|
||||||
|
// ARRAY readPatternFromBlack(int maxWhitePrefix, int range = 0)
|
||||||
|
// {
|
||||||
|
// if (maxWhitePrefix && isWhite() && !stepToEdge(1, maxWhitePrefix))
|
||||||
|
// return {};
|
||||||
|
// return readPattern<ARRAY>(range);
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -9,8 +9,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
BitMatrixCursor, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine,
|
BitMatrixCursorTrait, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine,
|
||||||
RegressionLineTrait,
|
RegressionLineTrait, UpdateMinMax, UpdateMinMaxFloat,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn CenterFromEnd<const N: usize, T: Into<f32> + std::iter::Sum<T> + Copy>(
|
pub fn CenterFromEnd<const N: usize, T: Into<f32> + std::iter::Sum<T> + Copy>(
|
||||||
@@ -41,7 +41,7 @@ pub fn CenterFromEnd<const N: usize, T: Into<f32> + std::iter::Sum<T> + Copy>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ReadSymmetricPattern<const N: usize, Cursor: BitMatrixCursor>(
|
pub fn ReadSymmetricPattern<const N: usize, Cursor: BitMatrixCursorTrait>(
|
||||||
cur: &mut Cursor,
|
cur: &mut Cursor,
|
||||||
range: i32,
|
range: i32,
|
||||||
) -> Option<Pattern<N>> {
|
) -> Option<Pattern<N>> {
|
||||||
@@ -82,7 +82,7 @@ pub fn CheckSymmetricPattern<
|
|||||||
const RELAXED_THRESHOLD: bool,
|
const RELAXED_THRESHOLD: bool,
|
||||||
const LEN: usize,
|
const LEN: usize,
|
||||||
const SUM: usize,
|
const SUM: usize,
|
||||||
T: BitMatrixCursor,
|
T: BitMatrixCursorTrait,
|
||||||
>(
|
>(
|
||||||
cur: &mut T,
|
cur: &mut T,
|
||||||
pattern: &Pattern<LEN>,
|
pattern: &Pattern<LEN>,
|
||||||
@@ -143,7 +143,7 @@ pub fn CheckSymmetricPattern<
|
|||||||
res.into_iter().sum::<PatternType>() as i32
|
res.into_iter().sum::<PatternType>() as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn AverageEdgePixels<T: BitMatrixCursor>(
|
pub fn AverageEdgePixels<T: BitMatrixCursorTrait>(
|
||||||
cur: &mut T,
|
cur: &mut T,
|
||||||
range: i32,
|
range: i32,
|
||||||
numOfEdges: u32,
|
numOfEdges: u32,
|
||||||
@@ -481,17 +481,42 @@ pub fn FindConcentricPatternCorners(
|
|||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
pub struct ConcentricPattern {
|
pub struct ConcentricPattern {
|
||||||
p: Point,
|
pub p: Point,
|
||||||
size: i32,
|
pub size: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub for ConcentricPattern {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
let new_p = self.p - rhs.p;
|
||||||
|
Self {
|
||||||
|
p: new_p,
|
||||||
|
size: self.size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConcentricPattern {
|
||||||
|
pub fn dot(self, other: ConcentricPattern) -> f32 {
|
||||||
|
Point::dot(self.p, other.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cross(self, other: ConcentricPattern) -> f32 {
|
||||||
|
Point::cross(self.p, other.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distance(self, other: ConcentricPattern) -> f32 {
|
||||||
|
Point::distance(self.p, other.p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn LocateConcentricPattern<
|
pub fn LocateConcentricPattern<
|
||||||
const RELAXED_THRESHOLD: bool,
|
const RELAXED_THRESHOLD: bool,
|
||||||
const LEN: usize,
|
const LEN: usize,
|
||||||
const SUM: usize,
|
const SUM: usize,
|
||||||
T: BitMatrixCursor,
|
|
||||||
>(
|
>(
|
||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
pattern: &Pattern<LEN>,
|
pattern: &Pattern<LEN>,
|
||||||
@@ -540,13 +565,3 @@ pub fn LocateConcentricPattern<
|
|||||||
size: (maxSpread + minSpread) / 2,
|
size: (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);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
Exceptions, Point,
|
Exceptions, Point,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{BitMatrixCursor, Direction, RegressionLineTrait, StepResult, Value};
|
use super::{BitMatrixCursorTrait, Direction, RegressionLineTrait, StepResult, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EdgeTracer<'a> {
|
pub struct EdgeTracer<'a> {
|
||||||
@@ -34,7 +34,7 @@ pub struct EdgeTracer<'a> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl BitMatrixCursor for EdgeTracer<'_> {
|
impl BitMatrixCursorTrait for EdgeTracer<'_> {
|
||||||
fn testAt(&self, p: Point) -> Value {
|
fn testAt(&self, p: Point) -> Value {
|
||||||
if self.img.isIn(p, 0) {
|
if self.img.isIn(p, 0) {
|
||||||
Value::from(self.img.get_point(p))
|
Value::from(self.img.get_point(p))
|
||||||
@@ -119,7 +119,7 @@ impl BitMatrixCursor for EdgeTracer<'_> {
|
|||||||
self.isIn(self.p)
|
self.isIn(self.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn movedBy<T: BitMatrixCursor>(self, d: Point) -> Self {
|
fn movedBy<T: BitMatrixCursorTrait>(self, d: Point) -> Self {
|
||||||
let mut res = self;
|
let mut res = self;
|
||||||
res.p += d;
|
res.p += d;
|
||||||
|
|
||||||
@@ -166,6 +166,10 @@ impl BitMatrixCursor for EdgeTracer<'_> {
|
|||||||
fn p(&self) -> Point {
|
fn p(&self) -> Point {
|
||||||
self.p
|
self.p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn d(&self) -> Point {
|
||||||
|
self.d
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EdgeTracer<'_> {
|
impl<'a> EdgeTracer<'_> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::BitMatrixCursor;
|
use super::BitMatrixCursorTrait;
|
||||||
|
|
||||||
pub struct FastEdgeToEdgeCounter {
|
pub struct FastEdgeToEdgeCounter {
|
||||||
// const uint8_t* p = nullptr;
|
// const uint8_t* p = nullptr;
|
||||||
@@ -7,7 +7,7 @@ pub struct FastEdgeToEdgeCounter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FastEdgeToEdgeCounter {
|
impl FastEdgeToEdgeCounter {
|
||||||
pub fn new<T: BitMatrixCursor>(_cur: &T) -> Self {
|
pub fn new<T: BitMatrixCursorTrait>(_cur: &T) -> Self {
|
||||||
todo!()
|
todo!()
|
||||||
// stride = cur.d.y * cur.img->width() + cur.d.x;
|
// 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;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod bitmatrix_cursor;
|
pub mod bitmatrix_cursor;
|
||||||
|
pub mod bitmatrix_cursor_trait;
|
||||||
pub mod concentric_finder;
|
pub mod concentric_finder;
|
||||||
pub mod direction;
|
pub mod direction;
|
||||||
pub mod dm_regression_line;
|
pub mod dm_regression_line;
|
||||||
@@ -12,6 +13,7 @@ pub mod util;
|
|||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
pub use bitmatrix_cursor::*;
|
pub use bitmatrix_cursor::*;
|
||||||
|
pub use bitmatrix_cursor_trait::*;
|
||||||
pub use concentric_finder::*;
|
pub use concentric_finder::*;
|
||||||
pub use direction::*;
|
pub use direction::*;
|
||||||
pub use dm_regression_line::*;
|
pub use dm_regression_line::*;
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::{common::Result, Exceptions};
|
use crate::{
|
||||||
|
common::{BitMatrix, 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];
|
||||||
@@ -337,8 +340,16 @@ pub struct FixedPattern<const N: usize, const SUM: usize, const IS_SPARCE: bool
|
|||||||
data: [PatternType; N],
|
data: [PatternType; N],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const N: usize, const SUM: usize, const IS_SPARCE: bool> Into<Pattern<N>>
|
||||||
|
for FixedPattern<N, SUM, IS_SPARCE>
|
||||||
|
{
|
||||||
|
fn into(self) -> Pattern<N> {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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> {
|
||||||
pub fn new(data: [PatternType; N]) -> Self {
|
pub const fn new(data: [PatternType; N]) -> Self {
|
||||||
FixedPattern { data }
|
FixedPattern { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,7 +557,15 @@ impl<T: Into<PatternType>> From<T> for Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetPatternRow<T: Into<PatternType> + Copy + Default + From<T>>(
|
pub fn GetPatternRowTP(matrix: &BitMatrix, r: u32, pr: &mut PatternRow, transpose: bool) {
|
||||||
|
if (transpose) {
|
||||||
|
GetPatternRow(&Into::<Vec<u8>>::into(matrix.getCol(r)), pr)
|
||||||
|
} else {
|
||||||
|
GetPatternRow(&Into::<Vec<u8>>::into(matrix.getRow(r)), pr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn GetPatternRow<T: Into<PatternType> + Copy + Default + From<T>>(
|
||||||
b_row: &[T],
|
b_row: &[T],
|
||||||
p_row: &mut PatternRow,
|
p_row: &mut PatternRow,
|
||||||
) {
|
) {
|
||||||
@@ -655,7 +674,9 @@ mod tests {
|
|||||||
fn basic_pattern_view() {
|
fn basic_pattern_view() {
|
||||||
let mut p_row = PatternRow::default();
|
let mut p_row = PatternRow::default();
|
||||||
GetPatternRow(
|
GetPatternRow(
|
||||||
&[0_u16, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1],
|
&[
|
||||||
|
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,
|
&mut p_row,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -26,3 +26,15 @@ pub fn opposite(dir: Direction) -> Direction {
|
|||||||
Direction::Left
|
Direction::Left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn UpdateMinMaxFloat(min: &mut f64, max: &mut f64, val: f64) {
|
||||||
|
*min = f64::min(*min, val);
|
||||||
|
*max = f64::max(*max, val);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
Quadrilateral, Result,
|
Quadrilateral, Result,
|
||||||
},
|
},
|
||||||
datamatrix::detector::{
|
datamatrix::detector::{
|
||||||
zxing_cpp_detector::{util::intersect, BitMatrixCursor},
|
zxing_cpp_detector::{util::intersect, BitMatrixCursorTrait},
|
||||||
DatamatrixDetectorResult,
|
DatamatrixDetectorResult,
|
||||||
},
|
},
|
||||||
point,
|
point,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
mod cpp_new_detector;
|
mod cpp_new_detector;
|
||||||
|
|
||||||
pub(self) use crate::common::cpp_essentials::bitmatrix_cursor::*;
|
pub(self) use crate::common::cpp_essentials::bitmatrix_cursor_trait::*;
|
||||||
pub(self) use crate::common::cpp_essentials::direction::*;
|
pub(self) use crate::common::cpp_essentials::direction::*;
|
||||||
pub(self) use crate::common::cpp_essentials::dm_regression_line::*;
|
pub(self) use crate::common::cpp_essentials::dm_regression_line::*;
|
||||||
pub(self) use crate::common::cpp_essentials::edge_tracer::*;
|
pub(self) use crate::common::cpp_essentials::edge_tracer::*;
|
||||||
|
|||||||
327
src/qrcode/cpp_port/detector.rs
Normal file
327
src/qrcode/cpp_port/detector.rs
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
use multimap::MultiMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
common::{
|
||||||
|
cpp_essentials::{
|
||||||
|
BitMatrixCursorTrait, ConcentricPattern, Direction, EdgeTracer, FindLeftGuard,
|
||||||
|
FixedPattern, GetPatternRow, GetPatternRowTP, IsPattern, LocateConcentricPattern,
|
||||||
|
PatternRow, PatternType, PatternView, ReadSymmetricPattern, RegressionLine,
|
||||||
|
RegressionLineTrait,
|
||||||
|
},
|
||||||
|
BitMatrix,
|
||||||
|
},
|
||||||
|
point, Point,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||||
|
pub struct FinderPatternSet {
|
||||||
|
bl: ConcentricPattern,
|
||||||
|
tl: ConcentricPattern,
|
||||||
|
tr: ConcentricPattern,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type FinderPatterns = Vec<ConcentricPattern>;
|
||||||
|
pub type FinderPatternSets = Vec<FinderPatternSet>;
|
||||||
|
|
||||||
|
const PATTERN: FixedPattern<5, 7, false> = FixedPattern::new([1, 1, 3, 1, 1]);
|
||||||
|
|
||||||
|
pub fn FindFinderPatterns(image: &BitMatrix, tryHarder: bool) -> FinderPatterns {
|
||||||
|
const MIN_SKIP: u32 = 3; // 1 pixel/module times 3 modules/center
|
||||||
|
const MAX_MODULES_FAST: u32 = 20 * 4 + 17; // support up to version 20 for mobile clients
|
||||||
|
|
||||||
|
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
||||||
|
// image, and then account for the center being 3 modules in size. This gives the smallest
|
||||||
|
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
||||||
|
// QR versions regardless of how dense they are.
|
||||||
|
let height = image.height();
|
||||||
|
let mut skip = (3 * height) / (4 * MAX_MODULES_FAST);
|
||||||
|
if (skip < MIN_SKIP || tryHarder) {
|
||||||
|
skip = MIN_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res: Vec<ConcentricPattern> = Vec::new();
|
||||||
|
let mut y = skip - 1;
|
||||||
|
|
||||||
|
while y < height {
|
||||||
|
// for (int y = skip - 1; y < height; y += skip) {
|
||||||
|
let mut row = PatternRow::default();
|
||||||
|
GetPatternRowTP(image, y, &mut row, false);
|
||||||
|
let mut next: PatternView = PatternView::new(&row);
|
||||||
|
|
||||||
|
while {
|
||||||
|
let next = FindLeftGuard(&next, 0, &PATTERN, 0.5).unwrap();
|
||||||
|
next.isValid()
|
||||||
|
} {
|
||||||
|
let p = point(
|
||||||
|
next.pixelsInFront() as f32
|
||||||
|
+ next[0] as f32
|
||||||
|
+ next[1] as f32
|
||||||
|
+ next[2] as f32 / 2.0,
|
||||||
|
y as f32 + 0.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
// make sure p is not 'inside' an already found pattern area
|
||||||
|
if res
|
||||||
|
.iter()
|
||||||
|
.find(|old| Point::distance(p, old.p) < (old.size as f32) / 2.0)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
// if (FindIf(res, [p](const auto& old) { return distance(p, old) < old.size / 2; }) == res.end()) {
|
||||||
|
let pattern = LocateConcentricPattern::<false, 5, 7>(
|
||||||
|
image,
|
||||||
|
&PATTERN.into(),
|
||||||
|
p,
|
||||||
|
next.sum::<u16>() as i32 * 3,
|
||||||
|
); // 3 for very skewed samples
|
||||||
|
// Reduce(next) * 3); // 3 for very skewed samples
|
||||||
|
if (pattern.is_some()) {
|
||||||
|
// log(*pattern, 3);
|
||||||
|
assert!(image.get_point(pattern.as_ref().unwrap().p));
|
||||||
|
res.push(pattern.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next.skipPair();
|
||||||
|
next.skipPair();
|
||||||
|
next.extend();
|
||||||
|
}
|
||||||
|
|
||||||
|
y += skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief GenerateFinderPatternSets
|
||||||
|
* @param patterns list of ConcentricPattern objects, i.e. found finder pattern squares
|
||||||
|
* @return list of plausible finder pattern sets, sorted by decreasing plausibility
|
||||||
|
*/
|
||||||
|
pub fn GenerateFinderPatternSets(patterns: &mut FinderPatterns) -> FinderPatternSets {
|
||||||
|
patterns.sort_by_key(|p| p.size);
|
||||||
|
// std::sort(patterns.begin(), patterns.end(), [](const auto& a, const auto& b) { return a.size < b.size; });
|
||||||
|
|
||||||
|
let mut sets: MultiMap<String, FinderPatternSet> = MultiMap::new();
|
||||||
|
let squaredDistance = |a: ConcentricPattern, b: ConcentricPattern| {
|
||||||
|
// The scaling of the distance by the b/a size ratio is a very coarse compensation for the shortening effect of
|
||||||
|
// the camera projection on slanted symbols. The fact that the size of the finder pattern is proportional to the
|
||||||
|
// distance from the camera is used here. This approximation only works if a < b < 2*a (see below).
|
||||||
|
// Test image: fix-finderpattern-order.jpg
|
||||||
|
ConcentricPattern::dot((a - b), (a - b)) as f64
|
||||||
|
* (((b).size as f64) / ((a).size as f64)).powi(2) //std::pow(double(b.size) / a.size, 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
let cosUpper: f64 = (45.0_f64 / 180.0 * 3.1415).cos(); // TODO: use c++20 std::numbers::pi_v
|
||||||
|
let cosLower: f64 = (135.0_f64 / 180.0 * 3.1415).cos();
|
||||||
|
|
||||||
|
let nbPatterns = (patterns).len();
|
||||||
|
for i in 0..(nbPatterns - 2) {
|
||||||
|
// for (int i = 0; i < nbPatterns - 2; i++) {
|
||||||
|
for j in (i + 1)..(nbPatterns - 1) {
|
||||||
|
// for (int j = i + 1; j < nbPatterns - 1; j++) {
|
||||||
|
for k in (j + 1)..(nbPatterns - 0) {
|
||||||
|
// for (int k = j + 1; k < nbPatterns - 0; k++) {
|
||||||
|
let mut a = &patterns[i];
|
||||||
|
let mut b = &patterns[j];
|
||||||
|
let mut c = &patterns[k];
|
||||||
|
// if the pattern sizes are too different to be part of the same symbol, skip this
|
||||||
|
// and the rest of the innermost loop (sorted list)
|
||||||
|
if (c.size > a.size * 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orders the three points in an order [A,B,C] such that AB is less than AC
|
||||||
|
// and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
||||||
|
|
||||||
|
let mut distAB2 = squaredDistance(*a, *b);
|
||||||
|
let mut distBC2 = squaredDistance(*b, *c);
|
||||||
|
let mut distAC2 = squaredDistance(*a, *c);
|
||||||
|
|
||||||
|
if (distBC2 >= distAB2 && distBC2 >= distAC2) {
|
||||||
|
std::mem::swap(&mut a, &mut b);
|
||||||
|
std::mem::swap(&mut distBC2, &mut distAC2);
|
||||||
|
} else if (distAB2 >= distAC2 && distAB2 >= distBC2) {
|
||||||
|
std::mem::swap(&mut b, &mut c);
|
||||||
|
std::mem::swap(&mut distAB2, &mut distAC2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let distAB = (distAB2.sqrt());
|
||||||
|
let distBC = (distBC2).sqrt();
|
||||||
|
|
||||||
|
// Make sure distAB and distBC don't differ more than reasonable
|
||||||
|
// TODO: make sure the constant 2 is not to conservative for reasonably tilted symbols
|
||||||
|
if (distAB > 2.0 * distBC || distBC > 2.0 * distAB) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate the module count and ignore this set if it can not result in a valid decoding
|
||||||
|
let moduleCount = (distAB + distBC)
|
||||||
|
/ (2.0 * (a.size + b.size + c.size) as f64 / (3.0 * 7.0))
|
||||||
|
+ 7.0;
|
||||||
|
if (moduleCount < 21.0 * 0.9 || moduleCount > 177.0 * 1.5)
|
||||||
|
// moduleCount may be overestimated, see above
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the angle between AB and BC does not deviate from 90° by more than 45°
|
||||||
|
let cosAB_BC = (distAB2 + distBC2 - distAC2) / (2.0 * distAB * distBC);
|
||||||
|
if ((cosAB_BC.is_nan()) || cosAB_BC > cosUpper || cosAB_BC < cosLower) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle).
|
||||||
|
// Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0,
|
||||||
|
// we need to check both two equal sides separately.
|
||||||
|
// The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity
|
||||||
|
// from isosceles right triangle.
|
||||||
|
let d: f64 = ((distAC2 - 2.0 * distAB2).abs() + (distAC2 - 2.0 * distBC2).abs());
|
||||||
|
|
||||||
|
// Use cross product to figure out whether A and C are correct or flipped.
|
||||||
|
// This asks whether BC x BA has a positive z component, which is the arrangement
|
||||||
|
// we want for A, B, C. If it's negative then swap A and C.
|
||||||
|
if (ConcentricPattern::cross(*c - *b, *a - *b) < 0.0) {
|
||||||
|
std::mem::swap(&mut a, &mut c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// arbitrarily limit the number of potential sets
|
||||||
|
// (this has performance implications while limiting the maximal number of detected symbols)
|
||||||
|
sets.insert(
|
||||||
|
d.to_string(),
|
||||||
|
FinderPatternSet {
|
||||||
|
bl: *a,
|
||||||
|
tl: *b,
|
||||||
|
tr: *c,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// const setSizeLimit : usize = 256;
|
||||||
|
// if (sets.len() < setSizeLimit || sets.crbegin().first > d) {
|
||||||
|
// sets.emplace(d, FinderPatternSet{a, b, c});
|
||||||
|
// if (sets.len() > setSizeLimit)
|
||||||
|
// {sets.erase(std::prev(sets.end()));}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert from multimap to vector
|
||||||
|
let mut res: FinderPatternSets = Vec::with_capacity(sets.len());
|
||||||
|
|
||||||
|
for (k, v) in sets {
|
||||||
|
// for (auto& [d, s] : sets)
|
||||||
|
res.extend(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn EstimateModuleSize(image: &BitMatrix, a: ConcentricPattern, b: ConcentricPattern) -> f64 {
|
||||||
|
let mut cur = EdgeTracer::new(image, a.p, b.p - a.p);
|
||||||
|
assert!(cur.isBlack());
|
||||||
|
|
||||||
|
let pattern = ReadSymmetricPattern::<5, _>(&mut cur, a.size * 2);
|
||||||
|
|
||||||
|
if pattern.is_none() {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pattern = pattern.unwrap();
|
||||||
|
|
||||||
|
if (!(IsPattern(
|
||||||
|
&PatternView::new(&PatternRow::new(pattern.to_vec())),
|
||||||
|
&PATTERN,
|
||||||
|
None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Some(true),
|
||||||
|
) != 0.0))
|
||||||
|
{
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
(2 * pattern.iter().sum::<PatternType>() - pattern[0] - pattern[4]) as f64 / 12.0
|
||||||
|
* cur.d().length() as f64
|
||||||
|
// (2 * Reduce(*pattern) - (*pattern)[0] - (*pattern)[4]) / 12.0 * length(cur.d)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DimensionEstimate {
|
||||||
|
dim: i32,
|
||||||
|
ms: f64,
|
||||||
|
err: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DimensionEstimate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
dim: 0,
|
||||||
|
ms: 0.0,
|
||||||
|
err: 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn EstimateDimension(
|
||||||
|
image: &BitMatrix,
|
||||||
|
a: ConcentricPattern,
|
||||||
|
b: ConcentricPattern,
|
||||||
|
) -> DimensionEstimate {
|
||||||
|
let ms_a = EstimateModuleSize(image, a, b);
|
||||||
|
let ms_b = EstimateModuleSize(image, b, a);
|
||||||
|
|
||||||
|
if (ms_a < 0.0 || ms_b < 0.0) {
|
||||||
|
return DimensionEstimate::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let moduleSize = (ms_a + ms_b) / 2.0;
|
||||||
|
|
||||||
|
let dimension = ((ConcentricPattern::distance(a, b) as f64 / moduleSize).round() as i32 + 7);
|
||||||
|
let error = 1 - (dimension % 4);
|
||||||
|
|
||||||
|
DimensionEstimate {
|
||||||
|
dim: dimension + error,
|
||||||
|
ms: moduleSize,
|
||||||
|
err: (error).abs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn TraceLine(image: &BitMatrix, p: Point, d: Point, edge: i32) -> impl RegressionLineTrait {
|
||||||
|
let mut cur = EdgeTracer::new(image, p, d - p);
|
||||||
|
let mut line = RegressionLine::default();
|
||||||
|
line.setDirectionInward(cur.back());
|
||||||
|
|
||||||
|
// collect points inside the black line -> backup on 3rd edge
|
||||||
|
cur.stepToEdge(Some(edge), Some(0), Some(edge == 3));
|
||||||
|
if (edge == 3) {
|
||||||
|
cur.turnBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut curI = EdgeTracer::new(image, (cur.p), (Point::mainDirection(cur.d())));
|
||||||
|
// make sure curI positioned such that the white->black edge is directly behind
|
||||||
|
// Test image: fix-traceline.jpg
|
||||||
|
while (!bool::from(curI.edgeAtBack())) {
|
||||||
|
if (curI.edgeAtLeft().into()) {
|
||||||
|
curI.turnRight();
|
||||||
|
} else if (curI.edgeAtRight().into()) {
|
||||||
|
curI.turnLeft();
|
||||||
|
} else {
|
||||||
|
curI.step(Some(-1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir in [Direction::Left, Direction::Right] {
|
||||||
|
// for (auto dir : {Direction::LEFT, Direction::RIGHT}) {
|
||||||
|
let mut c = EdgeTracer::new(image, curI.p, curI.direction(dir));
|
||||||
|
let stepCount = (Point::maxAbsComponent(cur.p - p)) as i32;
|
||||||
|
loop {
|
||||||
|
line.add(Point::centered(c.p));
|
||||||
|
|
||||||
|
if !(--stepCount > 0 && c.stepAlongEdge(dir, Some(true))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} //while (--stepCount > 0 && c.stepAlongEdge(dir, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
line.evaluate_max_distance(Some(1.0), Some(true));
|
||||||
|
|
||||||
|
line
|
||||||
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
// pub mod detector;
|
pub mod detector;
|
||||||
|
|||||||
Reference in New Issue
Block a user