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::{
|
common::{
|
||||||
detector::WhiteRectangleDetector,
|
detector::WhiteRectangleDetector,
|
||||||
reedsolomon::{self, ReedSolomonDecoder},
|
reedsolomon::{self, ReedSolomonDecoder},
|
||||||
BitMatrix, DefaultGridSampler, GridSampler, Result,
|
BitMatrix, DefaultGridSampler, GridSampler, Result, Quadrilateral,
|
||||||
},
|
},
|
||||||
exceptions::Exceptions,
|
exceptions::Exceptions,
|
||||||
point, Point,
|
point, Point,
|
||||||
@@ -89,13 +89,15 @@ impl<'a> Detector<'_> {
|
|||||||
// 3. Get the size of the matrix and other parameters from the bull's eye
|
// 3. Get the size of the matrix and other parameters from the bull's eye
|
||||||
self.extractParameters(&bulls_eye_corners)?;
|
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
|
// 4. Sample the grid
|
||||||
let bits = self.sample_grid(
|
let bits = self.sample_grid(
|
||||||
self.image,
|
self.image,
|
||||||
bulls_eye_corners[self.shift as usize % 4],
|
src_quad
|
||||||
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],
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// 5. Get the corners of the matrix.
|
// 5. Get the corners of the matrix.
|
||||||
@@ -457,10 +459,7 @@ impl<'a> Detector<'_> {
|
|||||||
fn sample_grid(
|
fn sample_grid(
|
||||||
&self,
|
&self,
|
||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
top_left: Point,
|
quad: Quadrilateral,
|
||||||
top_right: Point,
|
|
||||||
bottom_right: Point,
|
|
||||||
bottom_left: Point,
|
|
||||||
) -> Result<BitMatrix> {
|
) -> Result<BitMatrix> {
|
||||||
let sampler = DefaultGridSampler::default();
|
let sampler = DefaultGridSampler::default();
|
||||||
let dimension = self.get_dimension();
|
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 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 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(
|
sampler.sample_grid_detailed(
|
||||||
image,
|
image,
|
||||||
dimension,
|
dimension,
|
||||||
dimension,
|
dimension,
|
||||||
low,
|
dst, quad
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,20 @@
|
|||||||
// import org.junit.Assert;
|
// import org.junit.Assert;
|
||||||
// import org.junit.Test;
|
// import org.junit.Test;
|
||||||
|
|
||||||
|
use crate::point;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sean Owen
|
* @author Sean Owen
|
||||||
*/
|
*/
|
||||||
// public final class PerspectiveTransformTestCase extends Assert {
|
// public final class PerspectiveTransformTestCase extends Assert {
|
||||||
use super::PerspectiveTransform;
|
use super::{PerspectiveTransform, Quadrilateral};
|
||||||
|
|
||||||
static EPSILON: f32 = 1.0E-4f32;
|
static EPSILON: f32 = 1.0E-4f32;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_square_to_quadrilateral() {
|
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(2.0, 3.0, 0.0, 0.0, &pt);
|
||||||
assert_point_equals(10.0, 4.0, 1.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);
|
assert_point_equals(4.0, 9.0, 0.0, 1.0, &pt);
|
||||||
@@ -40,10 +43,10 @@ fn test_square_to_quadrilateral() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_quadrilateral_to_quadrilateral() {
|
fn test_quadrilateral_to_quadrilateral() {
|
||||||
let pt = PerspectiveTransform::quadrilateralToQuadrilateral(
|
let quad1 = Quadrilateral::new(point(2.0, 3.0), point(10.0, 4.0), point(16.0, 15.0), point(4.0, 9.0));
|
||||||
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,
|
let quad2 = Quadrilateral::new(point(103.0, 110.0), point(300.0, 120.0), point(290.0, 270.0), point(150.0,280.0));
|
||||||
280.0,
|
let pt = PerspectiveTransform::quadrilateralToQuadrilateral(quad1, quad2);
|
||||||
);
|
|
||||||
assert_point_equals(103.0, 110.0, 2.0, 3.0, &pt);
|
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(300.0, 120.0, 10.0, 4.0, &pt);
|
||||||
assert_point_equals(290.0, 270.0, 16.0, 15.0, &pt);
|
assert_point_equals(290.0, 270.0, 16.0, 15.0, &pt);
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
// import com.google.zxing.NotFoundException;
|
// import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
use crate::common::Result;
|
use crate::{common::Result};
|
||||||
use crate::Exceptions;
|
use crate::Exceptions;
|
||||||
|
|
||||||
use super::{BitMatrix, GridSampler, PerspectiveTransform};
|
use super::{BitMatrix, GridSampler, PerspectiveTransform, Quadrilateral, SamplerControl};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sean Owen
|
* @author Sean Owen
|
||||||
@@ -35,29 +35,14 @@ impl GridSampler for DefaultGridSampler {
|
|||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
dimensionX: u32,
|
dimensionX: u32,
|
||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
p1ToX: f32,
|
dst: Quadrilateral,
|
||||||
p1ToY: f32,
|
src:Quadrilateral
|
||||||
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,
|
|
||||||
) -> Result<BitMatrix> {
|
) -> Result<BitMatrix> {
|
||||||
let transform = PerspectiveTransform::quadrilateralToQuadrilateral(
|
let transform = PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||||
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX,
|
dst, src,
|
||||||
p2FromY, p3FromX, p3FromY, p4FromX, p4FromY,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.sample_grid(image, dimensionX, dimensionY, &transform)
|
self.sample_grid(image, dimensionX, dimensionY, &[SamplerControl::new(dimensionX, dimensionY, transform)])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_grid(
|
fn sample_grid(
|
||||||
@@ -65,7 +50,7 @@ impl GridSampler for DefaultGridSampler {
|
|||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
dimensionX: u32,
|
dimensionX: u32,
|
||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
transform: &PerspectiveTransform,
|
controls: &[SamplerControl]
|
||||||
) -> Result<BitMatrix> {
|
) -> Result<BitMatrix> {
|
||||||
if dimensionX == 0 || dimensionY == 0 {
|
if dimensionX == 0 || dimensionY == 0 {
|
||||||
return Err(Exceptions::NOT_FOUND);
|
return Err(Exceptions::NOT_FOUND);
|
||||||
@@ -83,7 +68,7 @@ impl GridSampler for DefaultGridSampler {
|
|||||||
points[x + 1] = i_value;
|
points[x + 1] = i_value;
|
||||||
x += 2;
|
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;
|
// Quick check to see if points transformed to something inside the image;
|
||||||
// sufficient to check the endpoints
|
// sufficient to check the endpoints
|
||||||
self.checkAndNudgePoints(image, &mut points)?;
|
self.checkAndNudgePoints(image, &mut points)?;
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
// import com.google.zxing.NotFoundException;
|
// import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
use crate::common::Result;
|
use crate::{common::Result, Point};
|
||||||
use crate::Exceptions;
|
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
|
* 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,
|
image: &BitMatrix,
|
||||||
dimensionX: u32,
|
dimensionX: u32,
|
||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
p1ToX: f32,
|
dst: Quadrilateral,
|
||||||
p1ToY: f32,
|
src:Quadrilateral
|
||||||
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,
|
|
||||||
) -> Result<BitMatrix>;
|
) -> Result<BitMatrix>;
|
||||||
|
|
||||||
fn sample_grid(
|
fn sample_grid(
|
||||||
@@ -116,7 +102,7 @@ pub trait GridSampler {
|
|||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
dimensionX: u32,
|
dimensionX: u32,
|
||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
transform: &PerspectiveTransform,
|
controls: &[SamplerControl]
|
||||||
) -> Result<BitMatrix>;
|
) -> Result<BitMatrix>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,3 +181,15 @@ pub trait GridSampler {
|
|||||||
Ok(())
|
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;
|
mod eci;
|
||||||
pub use eci::*;
|
pub use eci::*;
|
||||||
|
|
||||||
|
mod quad;
|
||||||
|
pub use quad::*;
|
||||||
|
|
||||||
#[cfg(feature = "otsu_level")]
|
#[cfg(feature = "otsu_level")]
|
||||||
mod otsu_level_binarizer;
|
mod otsu_level_binarizer;
|
||||||
#[cfg(feature = "otsu_level")]
|
#[cfg(feature = "otsu_level")]
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
|
|
||||||
// package com.google.zxing.common;
|
// 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
|
* <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
|
* 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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn quadrilateralToQuadrilateral(
|
pub fn quadrilateralToQuadrilateral(
|
||||||
x0: f32,
|
dst: Quadrilateral,
|
||||||
y0: f32,
|
src: Quadrilateral
|
||||||
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,
|
|
||||||
) -> Self {
|
) -> 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 =
|
let s_to_q =
|
||||||
PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
|
PerspectiveTransform::squareToQuadrilateral(src);
|
||||||
s_to_q.times(&q_to_s)
|
s_to_q * q_to_s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_points_single(&self, points: &mut [f32]) {
|
pub fn transform_points_single(&self, points: &mut [f32]) {
|
||||||
@@ -122,35 +119,29 @@ impl PerspectiveTransform {
|
|||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn squareToQuadrilateral(
|
pub fn squareToQuadrilateral(
|
||||||
x0: f32,
|
square: Quadrilateral
|
||||||
y0: f32,
|
|
||||||
x1: f32,
|
|
||||||
y1: f32,
|
|
||||||
x2: f32,
|
|
||||||
y2: f32,
|
|
||||||
x3: f32,
|
|
||||||
y3: f32,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let dx3 = x0 - x1 + x2 - x3;
|
let [p0, p1, p2, p3 ] = square.0;
|
||||||
let dy3 = y0 - y1 + y2 - y3;
|
|
||||||
if dx3 == 0.0 && dy3 == 0.0 {
|
let d3 = p0 - p1 + p2 - p3;
|
||||||
|
if d3 == point(0.0,0.0) {
|
||||||
// Affine
|
// 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 {
|
} else {
|
||||||
let dx1 = x1 - x2;
|
|
||||||
let dx2 = x3 - x2;
|
let d1 = p1 - p2;
|
||||||
let dy1 = y1 - y2;
|
let d2 = p3 - p2;
|
||||||
let dy2 = y3 - y2;
|
|
||||||
let denominator = dx1 * dy2 - dx2 * dy1;
|
let denominator = d1.cross(d2);
|
||||||
let a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
|
let a13 = (d3.x * d2.y - d2.x * d3.y) / denominator;
|
||||||
let a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
|
let a23 = (d1.x * d3.y - d3.x * d1.y) / denominator;
|
||||||
PerspectiveTransform::new(
|
PerspectiveTransform::new(
|
||||||
x1 - x0 + a13 * x1,
|
p1.x - p0.x + a13 * p1.x,
|
||||||
x3 - x0 + a23 * x3,
|
p3.x - p0.x + a23 * p3.x,
|
||||||
x0,
|
p0.x,
|
||||||
y1 - y0 + a13 * y1,
|
p1.y - p0.y + a13 * p1.y,
|
||||||
y3 - y0 + a23 * y3,
|
p3.y - p0.y + a23 * p3.y,
|
||||||
y0,
|
p0.y,
|
||||||
a13,
|
a13,
|
||||||
a23,
|
a23,
|
||||||
1.0,
|
1.0,
|
||||||
@@ -160,17 +151,10 @@ impl PerspectiveTransform {
|
|||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn quadrilateralToSquare(
|
pub fn quadrilateralToSquare(
|
||||||
x0: f32,
|
quad: Quadrilateral
|
||||||
y0: f32,
|
|
||||||
x1: f32,
|
|
||||||
y1: f32,
|
|
||||||
x2: f32,
|
|
||||||
y2: f32,
|
|
||||||
x3: f32,
|
|
||||||
y3: f32,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Here, the adjoint serves as the inverse
|
// 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 {
|
fn buildAdjoint(&self) -> Self {
|
||||||
@@ -187,18 +171,22 @@ impl PerspectiveTransform {
|
|||||||
self.a11 * self.a22 - self.a12 * self.a21,
|
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(
|
PerspectiveTransform::new(
|
||||||
self.a11 * other.a11 + self.a21 * other.a12 + self.a31 * other.a13,
|
self.a11 * rhs.a11 + self.a21 * rhs.a12 + self.a31 * rhs.a13,
|
||||||
self.a11 * other.a21 + self.a21 * other.a22 + self.a31 * other.a23,
|
self.a11 * rhs.a21 + self.a21 * rhs.a22 + self.a31 * rhs.a23,
|
||||||
self.a11 * other.a31 + self.a21 * other.a32 + self.a31 * other.a33,
|
self.a11 * rhs.a31 + self.a21 * rhs.a32 + self.a31 * rhs.a33,
|
||||||
self.a12 * other.a11 + self.a22 * other.a12 + self.a32 * other.a13,
|
self.a12 * rhs.a11 + self.a22 * rhs.a12 + self.a32 * rhs.a13,
|
||||||
self.a12 * other.a21 + self.a22 * other.a22 + self.a32 * other.a23,
|
self.a12 * rhs.a21 + self.a22 * rhs.a22 + self.a32 * rhs.a23,
|
||||||
self.a12 * other.a31 + self.a22 * other.a32 + self.a32 * other.a33,
|
self.a12 * rhs.a31 + self.a22 * rhs.a32 + self.a32 * rhs.a33,
|
||||||
self.a13 * other.a11 + self.a23 * other.a12 + self.a33 * other.a13,
|
self.a13 * rhs.a11 + self.a23 * rhs.a12 + self.a33 * rhs.a13,
|
||||||
self.a13 * other.a21 + self.a23 * other.a22 + self.a33 * other.a23,
|
self.a13 * rhs.a21 + self.a23 * rhs.a22 + self.a33 * rhs.a23,
|
||||||
self.a13 * other.a31 + self.a23 * other.a32 + self.a33 * other.a33,
|
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::{
|
use crate::{
|
||||||
common::{
|
common::{
|
||||||
detector::WhiteRectangleDetector, BitMatrix, DefaultGridSampler, GridSampler, Result,
|
detector::WhiteRectangleDetector, BitMatrix, DefaultGridSampler, GridSampler, Result, Quadrilateral,
|
||||||
},
|
},
|
||||||
point, Exceptions, Point,
|
point, Exceptions, Point,
|
||||||
};
|
};
|
||||||
@@ -334,26 +334,14 @@ impl<'a> Detector<'_> {
|
|||||||
) -> Result<BitMatrix> {
|
) -> Result<BitMatrix> {
|
||||||
let sampler = DefaultGridSampler::default();
|
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(
|
sampler.sample_grid_detailed(
|
||||||
image,
|
image,
|
||||||
dimensionX,
|
dimensionX,
|
||||||
dimensionY,
|
dimensionY,
|
||||||
0.5,
|
dst, src
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ macro_rules! CHECK {
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{BitMatrix, DefaultGridSampler, GridSampler, Result},
|
common::{BitMatrix, DefaultGridSampler, GridSampler, Result, Quadrilateral},
|
||||||
datamatrix::detector::{
|
datamatrix::detector::{
|
||||||
zxing_cpp_detector::{util::intersect, BitMatrixCursor, Quadrilateral, RegressionLine},
|
zxing_cpp_detector::{util::intersect, BitMatrixCursor, RegressionLine},
|
||||||
DatamatrixDetectorResult,
|
DatamatrixDetectorResult,
|
||||||
},
|
},
|
||||||
qrcode::encoder::ByteMatrix,
|
qrcode::encoder::ByteMatrix,
|
||||||
Exceptions, Point,
|
Exceptions, Point, point,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{DMRegressionLine, EdgeTracer};
|
use super::{DMRegressionLine, EdgeTracer};
|
||||||
@@ -212,27 +212,14 @@ fn Scan(
|
|||||||
|
|
||||||
let grid_sampler = DefaultGridSampler::default();
|
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 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(
|
let res = grid_sampler.sample_grid_detailed(
|
||||||
startTracer.img,
|
startTracer.img,
|
||||||
dimT as u32,
|
dimT as u32,
|
||||||
dimR as u32,
|
dimR as u32,
|
||||||
0.0,
|
dst, src
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// let res = grid_sampler.sample_grid(startTracer.img, dimT as u32, dimR as u32, &transform);
|
// 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 direction;
|
||||||
mod dm_regression_line;
|
mod dm_regression_line;
|
||||||
mod edge_tracer;
|
mod edge_tracer;
|
||||||
mod quad;
|
|
||||||
mod regression_line;
|
mod regression_line;
|
||||||
mod step_result;
|
mod step_result;
|
||||||
pub(self) mod util;
|
pub(self) mod util;
|
||||||
@@ -14,7 +13,6 @@ pub use cpp_new_detector::detect;
|
|||||||
pub(self) use direction::*;
|
pub(self) use direction::*;
|
||||||
pub(self) use dm_regression_line::*;
|
pub(self) use dm_regression_line::*;
|
||||||
pub(self) use edge_tracer::*;
|
pub(self) use edge_tracer::*;
|
||||||
pub(self) use quad::*;
|
|
||||||
pub(self) use regression_line::*;
|
pub(self) use regression_line::*;
|
||||||
pub(self) use step_result::*;
|
pub(self) use step_result::*;
|
||||||
pub(self) use value::*;
|
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 num::integer::Roots;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{BitMatrix, DefaultGridSampler, DetectorRXingResult, GridSampler, Result},
|
common::{BitMatrix, DefaultGridSampler, DetectorRXingResult, GridSampler, Result, Quadrilateral},
|
||||||
point, Exceptions, Point,
|
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_width = (tr.0 - tl.0).round().abs() as u32;
|
||||||
// let target_height = (br.1 - tr.1).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(
|
let Ok(bits) = grid_sampler.sample_grid_detailed(
|
||||||
image,
|
image,
|
||||||
target_width.round() as u32,
|
target_width.round() as u32,
|
||||||
target_height.round() as u32,
|
target_height.round() as u32,
|
||||||
0.0,
|
dst, src
|
||||||
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,
|
|
||||||
) else {
|
) else {
|
||||||
if try_harder {
|
if try_harder {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{BitMatrix, DefaultGridSampler, GridSampler, PerspectiveTransform, Result},
|
common::{BitMatrix, DefaultGridSampler, GridSampler, PerspectiveTransform, Result, Quadrilateral, SamplerControl},
|
||||||
point,
|
point,
|
||||||
qrcode::decoder::Version,
|
qrcode::decoder::Version,
|
||||||
DecodeHintType, DecodeHintValue, DecodingHintDictionary, Exceptions, Point, PointCallback,
|
DecodeHintType, DecodeHintValue, DecodingHintDictionary, Exceptions, Point, PointCallback,
|
||||||
@@ -147,7 +147,7 @@ impl<'a> Detector<'_> {
|
|||||||
)
|
)
|
||||||
.ok_or(Exceptions::NOT_FOUND)?;
|
.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![
|
let mut points = vec![
|
||||||
Point::from(bottomLeft),
|
Point::from(bottomLeft),
|
||||||
@@ -193,33 +193,25 @@ impl<'a> Detector<'_> {
|
|||||||
sourceBottomRightY = dimMinusThree;
|
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(
|
Some(PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||||
3.5,
|
dst, src
|
||||||
3.5,
|
|
||||||
dimMinusThree,
|
|
||||||
3.5,
|
|
||||||
sourceBottomRightX,
|
|
||||||
sourceBottomRightY,
|
|
||||||
3.5,
|
|
||||||
dimMinusThree,
|
|
||||||
topLeft.x,
|
|
||||||
topLeft.y,
|
|
||||||
topRight.x,
|
|
||||||
topRight.y,
|
|
||||||
bottomRightX,
|
|
||||||
bottomRightY,
|
|
||||||
bottomLeft.x,
|
|
||||||
bottomLeft.y,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sampleGrid(
|
fn sampleGrid(
|
||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
transform: &PerspectiveTransform,
|
transform: PerspectiveTransform,
|
||||||
dimension: u32,
|
dimension: u32,
|
||||||
) -> Result<BitMatrix> {
|
) -> Result<BitMatrix> {
|
||||||
let sampler = DefaultGridSampler::default();
|
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