mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-25 20:02:34 +00:00
port DefaultGridSampler
This commit is contained in:
@@ -115,12 +115,18 @@ fn test_error_in_parameter_locator(data: &str) {
|
|||||||
// dbg!(copy.to_string());
|
// dbg!(copy.to_string());
|
||||||
// dbg!(make_larger(©, 3).to_string());
|
// dbg!(make_larger(©, 3).to_string());
|
||||||
// The detector doesn't seem to work when matrix bits are only 1x1. So magnify.
|
// The detector doesn't seem to work when matrix bits are only 1x1. So magnify.
|
||||||
|
// dbg!(isMirror, error1, error2);
|
||||||
let r = Detector::new(&make_larger(©, 3)).detect(isMirror);
|
let r = Detector::new(&make_larger(©, 3)).detect(isMirror);
|
||||||
assert!(r.is_ok());
|
assert!(
|
||||||
|
r.is_ok(),
|
||||||
|
"decode should be ok: {isMirror:?}, {error1}, {error2}"
|
||||||
|
);
|
||||||
let r = r.expect("result already tested as ok");
|
let r = r.expect("result already tested as ok");
|
||||||
assert_eq!(r.getNbLayers(), layers);
|
assert_eq!(r.getNbLayers(), layers);
|
||||||
assert_eq!(r.isCompact(), compact);
|
assert_eq!(r.isCompact(), compact);
|
||||||
let res = decoder::decode(&r).expect("decode should be ok");
|
let res = decoder::decode(&r).expect(&format!(
|
||||||
|
"decode should be ok: {isMirror}, {error1}, {error2}"
|
||||||
|
));
|
||||||
assert_eq!(data, res.getText());
|
assert_eq!(data, res.getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -695,6 +695,10 @@ impl BitMatrix {
|
|||||||
new_bm
|
new_bm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_in(&self, p: Point) -> bool {
|
||||||
|
self.isIn(p, 0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn isIn(&self, p: Point, b: i32) -> bool {
|
pub fn isIn(&self, p: Point, b: i32) -> bool {
|
||||||
b as f32 <= p.x
|
b as f32 <= p.x
|
||||||
&& p.x < self.getWidth() as f32 - b as f32
|
&& p.x < self.getWidth() as f32 - b as f32
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
// import com.google.zxing.NotFoundException;
|
// import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
use crate::common::Result;
|
use crate::common::Result;
|
||||||
use crate::{Exceptions, Point};
|
use crate::{point, Exceptions, Point};
|
||||||
|
|
||||||
use super::{BitMatrix, GridSampler, PerspectiveTransform, Quadrilateral, SamplerControl};
|
use super::{BitMatrix, GridSampler, SamplerControl};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sean Owen
|
* @author Sean Owen
|
||||||
@@ -30,24 +30,6 @@ use super::{BitMatrix, GridSampler, PerspectiveTransform, Quadrilateral, Sampler
|
|||||||
pub struct DefaultGridSampler;
|
pub struct DefaultGridSampler;
|
||||||
|
|
||||||
impl GridSampler for DefaultGridSampler {
|
impl GridSampler for DefaultGridSampler {
|
||||||
fn sample_grid_detailed(
|
|
||||||
&self,
|
|
||||||
image: &BitMatrix,
|
|
||||||
dimensionX: u32,
|
|
||||||
dimensionY: u32,
|
|
||||||
dst: Quadrilateral,
|
|
||||||
src: Quadrilateral,
|
|
||||||
) -> Result<BitMatrix> {
|
|
||||||
let transform = PerspectiveTransform::quadrilateralToQuadrilateral(dst, src)?;
|
|
||||||
|
|
||||||
self.sample_grid(
|
|
||||||
image,
|
|
||||||
dimensionX,
|
|
||||||
dimensionY,
|
|
||||||
&[SamplerControl::new(dimensionX, dimensionY, transform)],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_grid(
|
fn sample_grid(
|
||||||
&self,
|
&self,
|
||||||
image: &BitMatrix,
|
image: &BitMatrix,
|
||||||
@@ -55,62 +37,64 @@ impl GridSampler for DefaultGridSampler {
|
|||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
controls: &[SamplerControl],
|
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);
|
||||||
}
|
}
|
||||||
let mut bits = BitMatrix::new(dimensionX, dimensionY)?;
|
|
||||||
let mut points = vec![Point::default(); dimensionX as usize];
|
for SamplerControl { p0, p1, transform } in controls {
|
||||||
for y in 0..dimensionY {
|
// To deal with remaining examples (see #251 and #267) of "numercial instabilities" that have not been
|
||||||
// for (int y = 0; y < dimensionY; y++) {
|
// prevented with the Quadrilateral.h:IsConvex() check, we check for all boundary points of the grid to
|
||||||
let max = points.len();
|
// be inside.
|
||||||
let i_value = y as f32 + 0.5;
|
let isInside = |p: Point| -> bool {
|
||||||
let mut x = 0;
|
return image.is_in(transform.transform_point(p.centered()));
|
||||||
while x < max {
|
};
|
||||||
// for (int x = 0; x < max; x += 2) {
|
for y in (p0.y as i32)..(p1.y as i32) {
|
||||||
points[x].x = (x as f32) + 0.5;
|
// for (int y = y0; y < y1; ++y)
|
||||||
points[x].y = i_value;
|
if !isInside(point(p0.x, y as f32)) || !isInside(point(p1.x - 1.0, y as f32)) {
|
||||||
x += 1;
|
return Err(Exceptions::NOT_FOUND);
|
||||||
}
|
}
|
||||||
controls
|
}
|
||||||
.first()
|
for x in (p0.x as i32)..(p1.x as i32) {
|
||||||
.unwrap()
|
// for (int x = x0; x < x1; ++x)
|
||||||
.transform
|
if !isInside(point(x as f32, p0.y)) || !isInside(point(x as f32, p1.y - 1.0)) {
|
||||||
.transform_points_single(&mut points);
|
return Err(Exceptions::NOT_FOUND);
|
||||||
// Quick check to see if points transformed to something inside the image;
|
|
||||||
// sufficient to check the endpoints
|
|
||||||
self.checkAndNudgePoints(image, &mut points)?;
|
|
||||||
// try {
|
|
||||||
let mut x = 0;
|
|
||||||
while x < max {
|
|
||||||
// for (int x = 0; x < max; x += 2) {
|
|
||||||
// if points[x] as u32 >= image.getWidth() || points[x + 1] as u32 >= image.getHeight()
|
|
||||||
// {
|
|
||||||
// return Err(Exceptions::notFound(
|
|
||||||
// "index out of bounds, see documentation in file for explanation".to_owned(),
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
if image
|
|
||||||
.try_get(points[x].x as u32, points[x].y as u32)
|
|
||||||
.ok_or(Exceptions::not_found_with(
|
|
||||||
"index out of bounds, see documentation in file for explanation",
|
|
||||||
))?
|
|
||||||
{
|
|
||||||
// Black(-ish) pixel
|
|
||||||
bits.set(x as u32, y);
|
|
||||||
}
|
}
|
||||||
x += 1;
|
|
||||||
}
|
}
|
||||||
// } catch (ArrayIndexOutOfBoundsException aioobe) {
|
|
||||||
// // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
|
|
||||||
// // transform gets "twisted" such that it maps a straight line of points to a set of points
|
|
||||||
// // whose endpoints are in bounds, but others are not. There is probably some mathematical
|
|
||||||
// // way to detect this about the transformation that I don't know yet.
|
|
||||||
// // This results in an ugly runtime exception despite our clever checks above -- can't have
|
|
||||||
// // that. We could check each point's coordinates but that feels duplicative. We settle for
|
|
||||||
// // catching and wrapping ArrayIndexOutOfBoundsException.
|
|
||||||
// throw NotFoundException.getNotFoundInstance();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut bits = BitMatrix::new(dimensionX, dimensionY)?;
|
||||||
|
for SamplerControl { p0, p1, transform } in controls {
|
||||||
|
// for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) {
|
||||||
|
for y in (p0.y as i32)..(p1.y as i32) {
|
||||||
|
// for (int y = y0; y < y1; ++y)
|
||||||
|
for x in (p0.x as i32)..(p1.x as i32) {
|
||||||
|
// for (int x = x0; x < x1; ++x) {
|
||||||
|
let p = transform.transform_point(Point::from((x, y)).centered()); //mod2Pix(centered(PointI{x, y}));
|
||||||
|
|
||||||
|
if image.get_point(p) {
|
||||||
|
bits.set(x as u32, y as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbg!(image.to_string());
|
||||||
|
// dbg!(bits.to_string());
|
||||||
|
|
||||||
|
let projectCorner = |p: Point| -> Point {
|
||||||
|
for SamplerControl { p0, p1, transform } in controls {
|
||||||
|
if p0.x <= p.x && p.x <= p1.x && p0.y <= p.y && p.y <= p1.y {
|
||||||
|
return transform.transform_point(p) + point(0.5, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Point::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let _tl = projectCorner(point(0.0, 0.0));
|
||||||
|
let _tr = projectCorner(Point::from((dimensionX, 0)));
|
||||||
|
let _bl = projectCorner(Point::from((dimensionX, dimensionY)));
|
||||||
|
let _br = projectCorner(Point::from((0, dimensionX)));
|
||||||
|
|
||||||
Ok(bits)
|
Ok(bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,16 @@ pub trait GridSampler {
|
|||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
dst: Quadrilateral,
|
dst: Quadrilateral,
|
||||||
src: Quadrilateral,
|
src: Quadrilateral,
|
||||||
) -> Result<BitMatrix>;
|
) -> Result<BitMatrix> {
|
||||||
|
let transform = PerspectiveTransform::quadrilateralToQuadrilateral(dst, src)?;
|
||||||
|
|
||||||
|
self.sample_grid(
|
||||||
|
image,
|
||||||
|
dimensionX,
|
||||||
|
dimensionY,
|
||||||
|
&[SamplerControl::new(dimensionX, dimensionY, transform)],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn sample_grid(
|
fn sample_grid(
|
||||||
&self,
|
&self,
|
||||||
@@ -103,7 +112,68 @@ pub trait GridSampler {
|
|||||||
dimensionX: u32,
|
dimensionX: u32,
|
||||||
dimensionY: u32,
|
dimensionY: u32,
|
||||||
controls: &[SamplerControl],
|
controls: &[SamplerControl],
|
||||||
) -> Result<BitMatrix>;
|
) -> Result<BitMatrix> {
|
||||||
|
if dimensionX == 0 || dimensionY == 0 {
|
||||||
|
return Err(Exceptions::NOT_FOUND);
|
||||||
|
}
|
||||||
|
let mut bits = BitMatrix::new(dimensionX, dimensionY)?;
|
||||||
|
let mut points = vec![Point::default(); dimensionX as usize];
|
||||||
|
for y in 0..dimensionY {
|
||||||
|
// for (int y = 0; y < dimensionY; y++) {
|
||||||
|
let max = points.len();
|
||||||
|
let i_value = y as f32 + 0.5;
|
||||||
|
let mut x = 0;
|
||||||
|
while x < max {
|
||||||
|
// for (int x = 0; x < max; x += 2) {
|
||||||
|
points[x].x = (x as f32) + 0.5;
|
||||||
|
points[x].y = i_value;
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
// try {
|
||||||
|
let mut x = 0;
|
||||||
|
while x < max {
|
||||||
|
// for (int x = 0; x < max; x += 2) {
|
||||||
|
// if points[x] as u32 >= image.getWidth() || points[x + 1] as u32 >= image.getHeight()
|
||||||
|
// {
|
||||||
|
// return Err(Exceptions::notFound(
|
||||||
|
// "index out of bounds, see documentation in file for explanation".to_owned(),
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
if image
|
||||||
|
.try_get(points[x].x as u32, points[x].y as u32)
|
||||||
|
.ok_or(Exceptions::not_found_with(
|
||||||
|
"index out of bounds, see documentation in file for explanation",
|
||||||
|
))?
|
||||||
|
{
|
||||||
|
// Black(-ish) pixel
|
||||||
|
bits.set(x as u32, y);
|
||||||
|
}
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
// } catch (ArrayIndexOutOfBoundsException aioobe) {
|
||||||
|
// // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
|
||||||
|
// // transform gets "twisted" such that it maps a straight line of points to a set of points
|
||||||
|
// // whose endpoints are in bounds, but others are not. There is probably some mathematical
|
||||||
|
// // way to detect this about the transformation that I don't know yet.
|
||||||
|
// // This results in an ugly runtime exception despite our clever checks above -- can't have
|
||||||
|
// // that. We could check each point's coordinates but that feels duplicative. We settle for
|
||||||
|
// // catching and wrapping ArrayIndexOutOfBoundsException.
|
||||||
|
// throw NotFoundException.getNotFoundInstance();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// dbg!(bits.to_string());
|
||||||
|
|
||||||
|
Ok(bits)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Checks a set of points that have been transformed to sample points on an image against
|
* <p>Checks a set of points that have been transformed to sample points on an image against
|
||||||
|
|||||||
@@ -81,15 +81,28 @@ impl PerspectiveTransform {
|
|||||||
Ok(s_to_q * q_to_s)
|
Ok(s_to_q * q_to_s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transform_point(&self, point: Point) -> Point {
|
||||||
|
let x = point.x;
|
||||||
|
let y = point.y;
|
||||||
|
let denominator = self.a13 * x + self.a23 * y + self.a33;
|
||||||
|
Point::new(
|
||||||
|
(self.a11 * x + self.a21 * y + self.a31) / denominator,
|
||||||
|
(self.a12 * x + self.a22 * y + self.a32) / denominator,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transform_points_single(&self, points: &mut [Point]) {
|
pub fn transform_points_single(&self, points: &mut [Point]) {
|
||||||
for point in points.iter_mut() {
|
for point in points.iter_mut() {
|
||||||
// for (int i = 0; i < maxI; i += 2) {
|
*point = self.transform_point(Point::new(point.x, point.y));
|
||||||
let x = point.x;
|
|
||||||
let y = point.y;
|
|
||||||
let denominator = self.a13 * x + self.a23 * y + self.a33;
|
|
||||||
point.x = (self.a11 * x + self.a21 * y + self.a31) / denominator;
|
|
||||||
point.y = (self.a12 * x + self.a22 * y + self.a32) / denominator;
|
|
||||||
}
|
}
|
||||||
|
// for point in points.iter_mut() {
|
||||||
|
// // for (int i = 0; i < maxI; i += 2) {
|
||||||
|
// let x = point.x;
|
||||||
|
// let y = point.y;
|
||||||
|
// let denominator = self.a13 * x + self.a23 * y + self.a33;
|
||||||
|
// point.x = (self.a11 * x + self.a21 * y + self.a31) / denominator;
|
||||||
|
// point.y = (self.a12 * x + self.a22 * y + self.a32) / denominator;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_points_double(&self, x_values: &mut [f32], y_valuess: &mut [f32]) {
|
pub fn transform_points_double(&self, x_values: &mut [f32], y_valuess: &mut [f32]) {
|
||||||
|
|||||||
@@ -260,6 +260,12 @@ impl From<(i32, i32)> for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(u32, u32)> for Point {
|
||||||
|
fn from(value: (u32, u32)) -> Self {
|
||||||
|
Self::new(value.0 as f32, value.1 as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Point;
|
use super::Point;
|
||||||
|
|||||||
Reference in New Issue
Block a user