Promote Quadrilateral to default

The library now uses the `Quadrilateral` struct when handling sets of four points in `GridSampler` and `PerspectiveTransform`
This commit is contained in:
Henry Schimke
2023-03-08 11:17:43 -06:00
parent d5e6a5d0a7
commit 2b7d053646
13 changed files with 348 additions and 422 deletions

View File

@@ -18,7 +18,7 @@ use crate::{
common::{
detector::WhiteRectangleDetector,
reedsolomon::{self, ReedSolomonDecoder},
BitMatrix, DefaultGridSampler, GridSampler, Result,
BitMatrix, DefaultGridSampler, GridSampler, Result, Quadrilateral,
},
exceptions::Exceptions,
point, Point,
@@ -89,13 +89,15 @@ impl<'a> Detector<'_> {
// 3. Get the size of the matrix and other parameters from the bull's eye
self.extractParameters(&bulls_eye_corners)?;
let src_quad = Quadrilateral::new(bulls_eye_corners[self.shift as usize % 4],
bulls_eye_corners[(self.shift as usize + 1) % 4],
bulls_eye_corners[(self.shift as usize + 2) % 4],
bulls_eye_corners[(self.shift as usize + 3) % 4],);
// 4. Sample the grid
let bits = self.sample_grid(
self.image,
bulls_eye_corners[self.shift as usize % 4],
bulls_eye_corners[(self.shift as usize + 1) % 4],
bulls_eye_corners[(self.shift as usize + 2) % 4],
bulls_eye_corners[(self.shift as usize + 3) % 4],
src_quad
)?;
// 5. Get the corners of the matrix.
@@ -457,10 +459,7 @@ impl<'a> Detector<'_> {
fn sample_grid(
&self,
image: &BitMatrix,
top_left: Point,
top_right: Point,
bottom_right: Point,
bottom_left: Point,
quad: Quadrilateral,
) -> Result<BitMatrix> {
let sampler = DefaultGridSampler::default();
let dimension = self.get_dimension();
@@ -468,26 +467,13 @@ impl<'a> Detector<'_> {
let low = dimension as f32 / 2.0 - self.nb_center_layers as f32;
let high = dimension as f32 / 2.0 + self.nb_center_layers as f32;
let dst = Quadrilateral::new(point(low,low), point(high,low), point(high,high), point(low,high));
sampler.sample_grid_detailed(
image,
dimension,
dimension,
low,
low, // topleft
high,
low, // topright
high,
high, // bottomright
low,
high, // bottomleft
top_left.x,
top_left.y,
top_right.x,
top_right.y,
bottom_right.x,
bottom_right.y,
bottom_left.x,
bottom_left.y,
dst, quad
)
}

View File

@@ -19,17 +19,20 @@
// import org.junit.Assert;
// import org.junit.Test;
use crate::point;
/**
* @author Sean Owen
*/
// public final class PerspectiveTransformTestCase extends Assert {
use super::PerspectiveTransform;
use super::{PerspectiveTransform, Quadrilateral};
static EPSILON: f32 = 1.0E-4f32;
#[test]
fn test_square_to_quadrilateral() {
let pt = PerspectiveTransform::squareToQuadrilateral(2.0, 3.0, 10.0, 4.0, 16.0, 15.0, 4.0, 9.0);
let square = Quadrilateral::new(point(2.0, 3.0), point(10.0, 3.0), point(16.0, 15.0), point(4.0,9.0));
let pt = PerspectiveTransform::squareToQuadrilateral(square);
assert_point_equals(2.0, 3.0, 0.0, 0.0, &pt);
assert_point_equals(10.0, 4.0, 1.0, 0.0, &pt);
assert_point_equals(4.0, 9.0, 0.0, 1.0, &pt);
@@ -40,10 +43,10 @@ fn test_square_to_quadrilateral() {
#[test]
fn test_quadrilateral_to_quadrilateral() {
let pt = PerspectiveTransform::quadrilateralToQuadrilateral(
2.0, 3.0, 10.0, 4.0, 16.0, 15.0, 4.0, 9.0, 103.0, 110.0, 300.0, 120.0, 290.0, 270.0, 150.0,
280.0,
);
let quad1 = Quadrilateral::new(point(2.0, 3.0), point(10.0, 4.0), point(16.0, 15.0), point(4.0, 9.0));
let quad2 = Quadrilateral::new(point(103.0, 110.0), point(300.0, 120.0), point(290.0, 270.0), point(150.0,280.0));
let pt = PerspectiveTransform::quadrilateralToQuadrilateral(quad1, quad2);
assert_point_equals(103.0, 110.0, 2.0, 3.0, &pt);
assert_point_equals(300.0, 120.0, 10.0, 4.0, &pt);
assert_point_equals(290.0, 270.0, 16.0, 15.0, &pt);

View File

@@ -18,10 +18,10 @@
// import com.google.zxing.NotFoundException;
use crate::common::Result;
use crate::{common::Result};
use crate::Exceptions;
use super::{BitMatrix, GridSampler, PerspectiveTransform};
use super::{BitMatrix, GridSampler, PerspectiveTransform, Quadrilateral, SamplerControl};
/**
* @author Sean Owen
@@ -35,29 +35,14 @@ impl GridSampler for DefaultGridSampler {
image: &BitMatrix,
dimensionX: u32,
dimensionY: u32,
p1ToX: f32,
p1ToY: f32,
p2ToX: f32,
p2ToY: f32,
p3ToX: f32,
p3ToY: f32,
p4ToX: f32,
p4ToY: f32,
p1FromX: f32,
p1FromY: f32,
p2FromX: f32,
p2FromY: f32,
p3FromX: f32,
p3FromY: f32,
p4FromX: f32,
p4FromY: f32,
dst: Quadrilateral,
src:Quadrilateral
) -> Result<BitMatrix> {
let transform = PerspectiveTransform::quadrilateralToQuadrilateral(
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX,
p2FromY, p3FromX, p3FromY, p4FromX, p4FromY,
dst, src,
);
self.sample_grid(image, dimensionX, dimensionY, &transform)
self.sample_grid(image, dimensionX, dimensionY, &[SamplerControl::new(dimensionX, dimensionY, transform)])
}
fn sample_grid(
@@ -65,7 +50,7 @@ impl GridSampler for DefaultGridSampler {
image: &BitMatrix,
dimensionX: u32,
dimensionY: u32,
transform: &PerspectiveTransform,
controls: &[SamplerControl]
) -> Result<BitMatrix> {
if dimensionX == 0 || dimensionY == 0 {
return Err(Exceptions::NOT_FOUND);
@@ -83,7 +68,7 @@ impl GridSampler for DefaultGridSampler {
points[x + 1] = i_value;
x += 2;
}
transform.transform_points_single(&mut points);
controls.first().unwrap().transform.transform_points_single(&mut points);
// Quick check to see if points transformed to something inside the image;
// sufficient to check the endpoints
self.checkAndNudgePoints(image, &mut points)?;

View File

@@ -18,10 +18,10 @@
// import com.google.zxing.NotFoundException;
use crate::common::Result;
use crate::Exceptions;
use crate::{common::Result, Point};
use crate::{Exceptions, point};
use super::{BitMatrix, PerspectiveTransform};
use super::{BitMatrix, PerspectiveTransform, Quadrilateral};
/**
* Implementations of this class can, given locations of finder patterns for a QR code in an
@@ -93,22 +93,8 @@ pub trait GridSampler {
image: &BitMatrix,
dimensionX: u32,
dimensionY: u32,
p1ToX: f32,
p1ToY: f32,
p2ToX: f32,
p2ToY: f32,
p3ToX: f32,
p3ToY: f32,
p4ToX: f32,
p4ToY: f32,
p1FromX: f32,
p1FromY: f32,
p2FromX: f32,
p2FromY: f32,
p3FromX: f32,
p3FromY: f32,
p4FromX: f32,
p4FromY: f32,
dst: Quadrilateral,
src:Quadrilateral
) -> Result<BitMatrix>;
fn sample_grid(
@@ -116,7 +102,7 @@ pub trait GridSampler {
image: &BitMatrix,
dimensionX: u32,
dimensionY: u32,
transform: &PerspectiveTransform,
controls: &[SamplerControl]
) -> Result<BitMatrix>;
/**
@@ -195,3 +181,15 @@ pub trait GridSampler {
Ok(())
}
}
pub struct SamplerControl {
pub p0: Point,
pub p1: Point,
pub transform: PerspectiveTransform
}
impl SamplerControl {
pub fn new( width: u32, height: u32, transform: PerspectiveTransform ) -> Self {
Self{ p0: point(0.0, 0.0), p1: point(width as f32, height as f32), transform }
}
}

View File

@@ -109,6 +109,9 @@ pub use hybrid_binarizer::*;
mod eci;
pub use eci::*;
mod quad;
pub use quad::*;
#[cfg(feature = "otsu_level")]
mod otsu_level_binarizer;
#[cfg(feature = "otsu_level")]

View File

@@ -16,6 +16,12 @@
// package com.google.zxing.common;
use std::ops::Mul;
use crate::point;
use super::Quadrilateral;
/**
* <p>This class implements a perspective transform in two dimensions. Given four source and four
* destination points, it will compute the transformation implied between them. The code is based
@@ -63,27 +69,18 @@ impl PerspectiveTransform {
#[allow(clippy::too_many_arguments)]
pub fn quadrilateralToQuadrilateral(
x0: f32,
y0: f32,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x3: f32,
y3: f32,
x0p: f32,
y0p: f32,
x1p: f32,
y1p: f32,
x2p: f32,
y2p: f32,
x3p: f32,
y3p: f32,
dst: Quadrilateral,
src: Quadrilateral
) -> Self {
let q_to_s = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
// let q_to_s = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
// let s_to_q =
// PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
let q_to_s = PerspectiveTransform::quadrilateralToSquare(dst);
let s_to_q =
PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
s_to_q.times(&q_to_s)
PerspectiveTransform::squareToQuadrilateral(src);
s_to_q * q_to_s
}
pub fn transform_points_single(&self, points: &mut [f32]) {
@@ -122,35 +119,29 @@ impl PerspectiveTransform {
#[allow(clippy::too_many_arguments)]
pub fn squareToQuadrilateral(
x0: f32,
y0: f32,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x3: f32,
y3: f32,
square: Quadrilateral
) -> Self {
let dx3 = x0 - x1 + x2 - x3;
let dy3 = y0 - y1 + y2 - y3;
if dx3 == 0.0 && dy3 == 0.0 {
let [p0, p1, p2, p3 ] = square.0;
let d3 = p0 - p1 + p2 - p3;
if d3 == point(0.0,0.0) {
// Affine
PerspectiveTransform::new(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0, 0.0, 1.0)
PerspectiveTransform::new(p1.x - p0.x, p2.x - p1.x, p0.x, p1.y - p0.y, p2.y - p1.y, p0.y, 0.0, 0.0, 1.0)
} else {
let dx1 = x1 - x2;
let dx2 = x3 - x2;
let dy1 = y1 - y2;
let dy2 = y3 - y2;
let denominator = dx1 * dy2 - dx2 * dy1;
let a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
let a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
let d1 = p1 - p2;
let d2 = p3 - p2;
let denominator = d1.cross(d2);
let a13 = (d3.x * d2.y - d2.x * d3.y) / denominator;
let a23 = (d1.x * d3.y - d3.x * d1.y) / denominator;
PerspectiveTransform::new(
x1 - x0 + a13 * x1,
x3 - x0 + a23 * x3,
x0,
y1 - y0 + a13 * y1,
y3 - y0 + a23 * y3,
y0,
p1.x - p0.x + a13 * p1.x,
p3.x - p0.x + a23 * p3.x,
p0.x,
p1.y - p0.y + a13 * p1.y,
p3.y - p0.y + a23 * p3.y,
p0.y,
a13,
a23,
1.0,
@@ -160,17 +151,10 @@ impl PerspectiveTransform {
#[allow(clippy::too_many_arguments)]
pub fn quadrilateralToSquare(
x0: f32,
y0: f32,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x3: f32,
y3: f32,
quad: Quadrilateral
) -> Self {
// Here, the adjoint serves as the inverse
PerspectiveTransform::squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint()
PerspectiveTransform::squareToQuadrilateral(quad).buildAdjoint()
}
fn buildAdjoint(&self) -> Self {
@@ -187,18 +171,22 @@ impl PerspectiveTransform {
self.a11 * self.a22 - self.a12 * self.a21,
)
}
}
fn times(&self, other: &Self) -> Self {
impl Mul for PerspectiveTransform {
type Output = PerspectiveTransform;
fn mul(self, rhs: Self) -> Self::Output {
PerspectiveTransform::new(
self.a11 * other.a11 + self.a21 * other.a12 + self.a31 * other.a13,
self.a11 * other.a21 + self.a21 * other.a22 + self.a31 * other.a23,
self.a11 * other.a31 + self.a21 * other.a32 + self.a31 * other.a33,
self.a12 * other.a11 + self.a22 * other.a12 + self.a32 * other.a13,
self.a12 * other.a21 + self.a22 * other.a22 + self.a32 * other.a23,
self.a12 * other.a31 + self.a22 * other.a32 + self.a32 * other.a33,
self.a13 * other.a11 + self.a23 * other.a12 + self.a33 * other.a13,
self.a13 * other.a21 + self.a23 * other.a22 + self.a33 * other.a23,
self.a13 * other.a31 + self.a23 * other.a32 + self.a33 * other.a33,
self.a11 * rhs.a11 + self.a21 * rhs.a12 + self.a31 * rhs.a13,
self.a11 * rhs.a21 + self.a21 * rhs.a22 + self.a31 * rhs.a23,
self.a11 * rhs.a31 + self.a21 * rhs.a32 + self.a31 * rhs.a33,
self.a12 * rhs.a11 + self.a22 * rhs.a12 + self.a32 * rhs.a13,
self.a12 * rhs.a21 + self.a22 * rhs.a22 + self.a32 * rhs.a23,
self.a12 * rhs.a31 + self.a22 * rhs.a32 + self.a32 * rhs.a33,
self.a13 * rhs.a11 + self.a23 * rhs.a12 + self.a33 * rhs.a13,
self.a13 * rhs.a21 + self.a23 * rhs.a22 + self.a33 * rhs.a23,
self.a13 * rhs.a31 + self.a23 * rhs.a32 + self.a33 * rhs.a33,
)
}
}

217
src/common/quad.rs Normal file
View File

@@ -0,0 +1,217 @@
use crate::Point;
#[derive(Clone, Copy, Debug)]
pub struct Quadrilateral(pub [Point; 4]);
impl Quadrilateral {
// using Base = std::array<T, 4>;
// using Base::at;
// public:
// using Point = T;
#[allow(dead_code)]
pub fn new(tl:Point, tr: Point, br: Point, bl: Point) -> Self {
Self([tl,tr,br,bl])
}
// pub fn with_f32( tl:f32, tr:f32, br:f32, bl:f32) -> Self {
// Self([tl, tr,br, bl ])
// }
pub fn with_points(tl: Point, tr: Point, br: Point, bl: Point) -> Self {
Self([tl, tr, br, bl])
}
pub fn top_left(&self) -> &Point {
&self.0[0]
} //const noexcept { return at(0); }
pub fn top_right(&self) -> &Point {
&self.0[1]
} //const noexcept { return at(1); }
pub fn bottom_right(&self) -> &Point {
&self.0[2]
} //const noexcept { return at(2); }
pub fn bottom_left(&self) -> &Point {
&self.0[3]
} //const noexcept { return at(3); }
#[allow(dead_code)]
pub fn orientation(&self) -> f64 {
let centerLine =
(*self.top_right() + *self.bottom_right()) - (*self.top_left() + *self.bottom_left());
if (centerLine == Point { x: 0.0, y: 0.0 }) {
return 0.0;
}
let centerLineF = Point::normalized(centerLine);
f32::atan2(centerLineF.y, centerLineF.x).into()
}
pub fn points(&self) -> &[Point] {
&self.0
}
}
impl Quadrilateral {
#[allow(dead_code)]
pub fn rectangle(width: i32, height: i32, margin: Option<i32>) -> Quadrilateral {
let margin = margin.unwrap_or(0);
Quadrilateral([
Point {
x: margin as f32,
y: margin as f32,
},
Point {
x: width as f32 - margin as f32,
y: margin as f32,
},
Point {
x: width as f32 - margin as f32,
y: height as f32 - margin as f32,
},
Point {
x: margin as f32,
y: height as f32 - margin as f32,
},
])
}
#[allow(dead_code)]
pub fn centered_square(size: i32) -> Quadrilateral {
Self::scale(
&Quadrilateral([
Point { x: -1.0, y: -1.0 },
Point { x: 1.0, y: -1.0 },
Point { x: 1.0, y: 1.0 },
Point { x: -1.0, y: 1.0 },
]),
size / 2,
)
}
#[allow(dead_code)]
pub fn line(y: i32, xStart: i32, xStop: i32) -> Quadrilateral {
Quadrilateral([
Point {
x: xStart as f32,
y: y as f32,
},
Point {
x: xStop as f32,
y: y as f32,
},
Point {
x: xStop as f32,
y: y as f32,
},
Point {
x: xStart as f32,
y: y as f32,
},
])
}
#[allow(dead_code)]
pub fn is_convex(&self) -> bool {
let N = self.0.len();
let mut sign = false;
let mut m = f32::INFINITY;
let mut M = 0.0_f32;
for i in 0..N
// for(int i = 0; i < N; i++)
{
let d1 = self.0[(i + 2) % N] - self.0[(i + 1) % N];
let d2 = self.0[i] - self.0[(i + 1) % N];
let cp = d1.cross(d2);
m = if m.abs() > cp { cp } else { m.abs() };
M = if M.abs() > cp { M.abs() } else { cp };
// m = std::cmp::min((m).abs(), cp);
// M = std::cmp::max((M).abs(), cp);
if i == 0 {
sign = cp > 0.0;
} else if sign != (cp > 0.0) {
return false;
}
}
// It turns out being convex is not enough to prevent a "numerical instability"
// that can cause the corners being projected inside the image boundaries but
// some points near the corners being projected outside. This has been observed
// where one corner is almost in line with two others. The M/m ratio is below 2
// for the complete existing sample set. For very "skewed" QRCodes a value of
// around 3 is realistic. A value of 14 has been observed to trigger the
// instability.
M / m < 4.0
}
#[allow(dead_code)]
pub fn scale(&self, factor: i32) -> Quadrilateral {
Quadrilateral([
self.0[0] * factor as f32,
self.0[1] * factor as f32,
self.0[2] * factor as f32,
self.0[3] * factor as f32,
])
}
#[allow(dead_code)]
pub fn center(&self) -> Point {
let reduced: Point = self.0.iter().sum();
let size = self.0.len() as f32;
reduced / size
// return Reduce(q) / Size(q);
}
#[allow(dead_code)]
pub fn rotated_corners(&self, n: Option<i32>, mirror: Option<bool>) -> Quadrilateral {
let n = if let Some(n) = n { n } else { 1 };
let mirror = if let Some(m) = mirror { m } else { false };
let mut res = self.clone();
res.0.rotate_left(((n + 4) % 4) as usize);
// std::rotate_copy(q.begin(), q.begin() + ((n + 4) % 4), q.end(), res.begin());
if mirror {
res.0.swap(1, 3);
}
// {std::swap(res[1], res[3]);}
res
}
#[allow(dead_code)]
pub fn is_inside(&self, p: Point) -> bool {
// Test if p is on the same side (right or left) of all polygon segments
let mut pos = 0;
let mut neg = 0;
for i in 0..self.0.len()
// for (int i = 0; i < Size(q); ++i)
{
if Point::cross(p - self.0[i], self.0[(i + 1) % self.0.len()] - self.0[i]) < 0.0 {
neg += 1;
} else {
pos += 1;
}
// (cross(p - q[i], q[(i + 1) % Size(q)] - q[i]) < 0 ? neg : pos)++;
}
pos == 0 || neg == 0
}
#[allow(dead_code)]
pub fn have_intersecting_bounding_boxes(&self, b: &Quadrilateral) -> bool {
// TODO: this is only a quick and dirty approximation that works for the trivial standard cases
let x = b.top_right().x < self.top_left().x || b.top_left().x > self.top_right().x;
let y = b.bottom_left().y < self.top_left().y || b.top_left().y > self.bottom_left().y;
!(x || y)
}
}
impl Default for Quadrilateral {
fn default() -> Self {
Self([Point { x: 0.0, y: 0.0 }; 4])
}
}

View File

@@ -16,7 +16,7 @@
use crate::{
common::{
detector::WhiteRectangleDetector, BitMatrix, DefaultGridSampler, GridSampler, Result,
detector::WhiteRectangleDetector, BitMatrix, DefaultGridSampler, GridSampler, Result, Quadrilateral,
},
point, Exceptions, Point,
};
@@ -334,26 +334,14 @@ impl<'a> Detector<'_> {
) -> Result<BitMatrix> {
let sampler = DefaultGridSampler::default();
let dst = Quadrilateral::new(point(0.5, 0.5), point(dimensionX as f32 - 0.5, 0.5), point(dimensionX as f32 - 0.5,dimensionY as f32 - 0.5), point(0.5, dimensionY as f32 - 0.5));
let src = Quadrilateral::new(topRight, topLeft,bottomRight, bottomLeft);
sampler.sample_grid_detailed(
image,
dimensionX,
dimensionY,
0.5,
0.5,
dimensionX as f32 - 0.5,
0.5,
dimensionX as f32 - 0.5,
dimensionY as f32 - 0.5,
0.5,
dimensionY as f32 - 0.5,
topLeft.x,
topLeft.y,
topRight.x,
topRight.y,
bottomRight.x,
bottomRight.y,
bottomLeft.x,
bottomLeft.y,
dst, src
)
}

View File

@@ -14,13 +14,13 @@ macro_rules! CHECK {
use std::{cell::RefCell, rc::Rc};
use crate::{
common::{BitMatrix, DefaultGridSampler, GridSampler, Result},
common::{BitMatrix, DefaultGridSampler, GridSampler, Result, Quadrilateral},
datamatrix::detector::{
zxing_cpp_detector::{util::intersect, BitMatrixCursor, Quadrilateral, RegressionLine},
zxing_cpp_detector::{util::intersect, BitMatrixCursor, RegressionLine},
DatamatrixDetectorResult,
},
qrcode::encoder::ByteMatrix,
Exceptions, Point,
Exceptions, Point, point,
};
use super::{DMRegressionLine, EdgeTracer};
@@ -212,27 +212,14 @@ fn Scan(
let grid_sampler = DefaultGridSampler::default();
// let transform = PerspectiveTransform::quadrilateralToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3, x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
let dst = Quadrilateral::new(point(0.0,0.0), point(dimT as f32, 0.0), point(dimT as f32, dimR as f32), point(0.0, dimR as f32));
let src = sourcePoints;
let res = grid_sampler.sample_grid_detailed(
startTracer.img,
dimT as u32,
dimR as u32,
0.0,
0.0,
dimT as f32,
0.0,
dimT as f32,
dimR as f32,
0.0,
dimR as f32,
sourcePoints.topLeft().x,
sourcePoints.topLeft().y,
sourcePoints.topRight().x,
sourcePoints.topRight().y,
sourcePoints.bottomRight().x,
sourcePoints.bottomRight().y,
sourcePoints.bottomLeft().x,
sourcePoints.bottomLeft().y,
dst, src
);
// let res = grid_sampler.sample_grid(startTracer.img, dimT as u32, dimR as u32, &transform);

View File

@@ -3,7 +3,6 @@ mod cpp_new_detector;
mod direction;
mod dm_regression_line;
mod edge_tracer;
mod quad;
mod regression_line;
mod step_result;
pub(self) mod util;
@@ -14,7 +13,6 @@ pub use cpp_new_detector::detect;
pub(self) use direction::*;
pub(self) use dm_regression_line::*;
pub(self) use edge_tracer::*;
pub(self) use quad::*;
pub(self) use regression_line::*;
pub(self) use step_result::*;
pub(self) use value::*;

View File

@@ -1,209 +0,0 @@
use crate::Point;
#[derive(Clone, Debug)]
pub struct Quadrilateral([Point; 4]);
impl Quadrilateral {
// using Base = std::array<T, 4>;
// using Base::at;
// public:
// using Point = T;
#[allow(dead_code)]
pub fn new() -> Self {
Self([Point { x: 0.0, y: 0.0 }; 4])
}
// pub fn with_f32( tl:f32, tr:f32, br:f32, bl:f32) -> Self {
// Self([tl, tr,br, bl ])
// }
pub fn with_points(tl: Point, tr: Point, br: Point, bl: Point) -> Self {
Self([tl, tr, br, bl])
}
pub fn topLeft(&self) -> &Point {
&self.0[0]
} //const noexcept { return at(0); }
pub fn topRight(&self) -> &Point {
&self.0[1]
} //const noexcept { return at(1); }
pub fn bottomRight(&self) -> &Point {
&self.0[2]
} //const noexcept { return at(2); }
pub fn bottomLeft(&self) -> &Point {
&self.0[3]
} //const noexcept { return at(3); }
#[allow(dead_code)]
pub fn orientation(&self) -> f64 {
let centerLine =
(*self.topRight() + *self.bottomRight()) - (*self.topLeft() + *self.bottomLeft());
if (centerLine == Point { x: 0.0, y: 0.0 }) {
return 0.0;
}
let centerLineF = Point::normalized(centerLine);
f32::atan2(centerLineF.y, centerLineF.x).into()
}
pub fn points(&self) -> &[Point] {
&self.0
}
}
#[allow(dead_code)]
pub fn Rectangle(width: i32, height: i32, margin: Option<i32>) -> Quadrilateral {
let margin = if let Some(m) = margin { m } else { 0 };
Quadrilateral([
Point {
x: margin as f32,
y: margin as f32,
},
Point {
x: width as f32 - margin as f32,
y: margin as f32,
},
Point {
x: width as f32 - margin as f32,
y: height as f32 - margin as f32,
},
Point {
x: margin as f32,
y: height as f32 - margin as f32,
},
])
}
#[allow(dead_code)]
pub fn CenteredSquare(size: i32) -> Quadrilateral {
Scale(
&Quadrilateral([
Point { x: -1.0, y: -1.0 },
Point { x: 1.0, y: -1.0 },
Point { x: 1.0, y: 1.0 },
Point { x: -1.0, y: 1.0 },
]),
size / 2,
)
}
#[allow(dead_code)]
pub fn Line(y: i32, xStart: i32, xStop: i32) -> Quadrilateral {
Quadrilateral([
Point {
x: xStart as f32,
y: y as f32,
},
Point {
x: xStop as f32,
y: y as f32,
},
Point {
x: xStop as f32,
y: y as f32,
},
Point {
x: xStart as f32,
y: y as f32,
},
])
}
#[allow(dead_code)]
pub fn IsConvex(poly: &Quadrilateral) -> bool {
let N = poly.0.len();
let mut sign = false;
let mut m = f32::INFINITY;
let mut M = 0.0_f32;
for i in 0..N
// for(int i = 0; i < N; i++)
{
let d1 = poly.0[(i + 2) % N] - poly.0[(i + 1) % N];
let d2 = poly.0[i] - poly.0[(i + 1) % N];
let cp = d1.cross(d2);
m = if m.abs() > cp { cp } else { m.abs() };
M = if M.abs() > cp { M.abs() } else { cp };
// m = std::cmp::min((m).abs(), cp);
// M = std::cmp::max((M).abs(), cp);
if i == 0 {
sign = cp > 0.0;
} else if sign != (cp > 0.0) {
return false;
}
}
// It turns out being convex is not enough to prevent a "numerical instability"
// that can cause the corners being projected inside the image boundaries but
// some points near the corners being projected outside. This has been observed
// where one corner is almost in line with two others. The M/m ratio is below 2
// for the complete existing sample set. For very "skewed" QRCodes a value of
// around 3 is realistic. A value of 14 has been observed to trigger the
// instability.
M / m < 4.0
}
#[allow(dead_code)]
pub fn Scale(q: &Quadrilateral, factor: i32) -> Quadrilateral {
Quadrilateral([
q.0[0] * factor as f32,
q.0[1] * factor as f32,
q.0[2] * factor as f32,
q.0[3] * factor as f32,
])
}
#[allow(dead_code)]
pub fn Center(q: &Quadrilateral) -> Point {
let reduced: Point = q.0.iter().sum();
let size = q.0.len() as f32;
reduced / size
// return Reduce(q) / Size(q);
}
#[allow(dead_code)]
pub fn RotatedCorners(q: &Quadrilateral, n: Option<i32>, mirror: Option<bool>) -> Quadrilateral {
let n = if let Some(n) = n { n } else { 1 };
let mirror = if let Some(m) = mirror { m } else { false };
let mut res = q.clone();
res.0.rotate_left(((n + 4) % 4) as usize);
// std::rotate_copy(q.begin(), q.begin() + ((n + 4) % 4), q.end(), res.begin());
if mirror {
res.0.swap(1, 3);
}
// {std::swap(res[1], res[3]);}
res
}
#[allow(dead_code)]
pub fn IsInside(p: Point, q: &Quadrilateral) -> bool {
// Test if p is on the same side (right or left) of all polygon segments
let mut pos = 0;
let mut neg = 0;
for i in 0..q.0.len()
// for (int i = 0; i < Size(q); ++i)
{
if Point::cross(p - q.0[i], q.0[(i + 1) % q.0.len()] - q.0[i]) < 0.0 {
neg += 1;
} else {
pos += 1;
}
// (cross(p - q[i], q[(i + 1) % Size(q)] - q[i]) < 0 ? neg : pos)++;
}
pos == 0 || neg == 0
}
#[allow(dead_code)]
pub fn HaveIntersectingBoundingBoxes(a: &Quadrilateral, b: &Quadrilateral) -> bool {
// TODO: this is only a quick and dirty approximation that works for the trivial standard cases
let x = b.topRight().x < a.topLeft().x || b.topLeft().x > a.topRight().x;
let y = b.bottomLeft().y < a.topLeft().y || b.topLeft().y > a.bottomLeft().y;
!(x || y)
}

View File

@@ -2,7 +2,7 @@
use num::integer::Roots;
use crate::{
common::{BitMatrix, DefaultGridSampler, DetectorRXingResult, GridSampler, Result},
common::{BitMatrix, DefaultGridSampler, DetectorRXingResult, GridSampler, Result, Quadrilateral},
point, Exceptions, Point,
};
@@ -346,26 +346,16 @@ pub fn detect(image: &BitMatrix, try_harder: bool) -> Result<MaxicodeDetectionRe
// let target_width = (tr.0 - tl.0).round().abs() as u32;
// let target_height = (br.1 - tr.1).round().abs() as u32;
let dst = Quadrilateral::new(point(0.0,0.0), point(target_width,0.0), point(target_width,
target_height), point(0.0,
target_height));
let src = Quadrilateral::new(tl, tr, br, bl);
let Ok(bits) = grid_sampler.sample_grid_detailed(
image,
target_width.round() as u32,
target_height.round() as u32,
0.0,
0.0,
target_width ,
0.0,
target_width,
target_height,
0.0,
target_height,
tl.x,
tl.y,
tr.x,
tr.y,
br.x,
br.y,
bl.x,
bl.y,
dst, src
) else {
if try_harder {
continue;

View File

@@ -17,7 +17,7 @@
use std::collections::HashMap;
use crate::{
common::{BitMatrix, DefaultGridSampler, GridSampler, PerspectiveTransform, Result},
common::{BitMatrix, DefaultGridSampler, GridSampler, PerspectiveTransform, Result, Quadrilateral, SamplerControl},
point,
qrcode::decoder::Version,
DecodeHintType, DecodeHintValue, DecodingHintDictionary, Exceptions, Point, PointCallback,
@@ -147,7 +147,7 @@ impl<'a> Detector<'_> {
)
.ok_or(Exceptions::NOT_FOUND)?;
let bits = Detector::sampleGrid(self.image, &transform, dimension)?;
let bits = Detector::sampleGrid(self.image, transform, dimension)?;
let mut points = vec![
Point::from(bottomLeft),
@@ -193,33 +193,25 @@ impl<'a> Detector<'_> {
sourceBottomRightY = dimMinusThree;
}
let dst = Quadrilateral::new(point(3.5,
3.5), point(dimMinusThree,
3.5), point(sourceBottomRightX,
sourceBottomRightY), point(3.5,
dimMinusThree));
let src = Quadrilateral::new( topLeft, topRight, point(bottomRightX, bottomRightY), bottomLeft);
Some(PerspectiveTransform::quadrilateralToQuadrilateral(
3.5,
3.5,
dimMinusThree,
3.5,
sourceBottomRightX,
sourceBottomRightY,
3.5,
dimMinusThree,
topLeft.x,
topLeft.y,
topRight.x,
topRight.y,
bottomRightX,
bottomRightY,
bottomLeft.x,
bottomLeft.y,
dst, src
))
}
fn sampleGrid(
image: &BitMatrix,
transform: &PerspectiveTransform,
transform: PerspectiveTransform,
dimension: u32,
) -> Result<BitMatrix> {
let sampler = DefaultGridSampler::default();
sampler.sample_grid(image, dimension, dimension, transform)
sampler.sample_grid(image, dimension, dimension, &[SamplerControl::new(dimension, dimension, transform)])
}
/**