mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
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:
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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")]
|
||||
|
||||
@@ -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
217
src/common/quad.rs
Normal 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])
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)])
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user