From 3d75de7183867fa4ef876fa2871974b93b1f6050 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Mon, 20 Mar 2023 11:09:06 -0500 Subject: [PATCH] DetectPureQR ported --- src/common/bit_matrix.rs | 27 ---- src/common/cpp_essentials/bitmatrix.rs | 61 +++++++++ .../cpp_essentials/bitmatrix_cursor_trait.rs | 26 ++-- src/common/cpp_essentials/mod.rs | 1 + src/common/cpp_essentials/pattern.rs | 6 + src/qrcode/cpp_port/detector.rs | 127 ++++++++++++------ src/rxing_result_point.rs | 4 +- tests/cropped_transposed_luma.rs | 2 +- 8 files changed, 168 insertions(+), 86 deletions(-) create mode 100644 src/common/cpp_essentials/bitmatrix.rs diff --git a/src/common/bit_matrix.rs b/src/common/bit_matrix.rs index 975219b..0868d7b 100644 --- a/src/common/bit_matrix.rs +++ b/src/common/bit_matrix.rs @@ -559,33 +559,6 @@ impl BitMatrix { Some([left, top, right - left + 1, bottom - top + 1]) } - pub fn - findBoundingBox(&self, left:&mut u32, top:&mut u32, width:&mut u32, height:&mut u32, minSize:u32) -> bool - { - todo!() - // let right; - // let bottom; - // if (!self.getTopLeftOnBitWithPosition(left, top) || !self.getBottomRightOnBitWithPosition(right, bottom) || bottom - top + 1 < minSize) - // {return false;} - - // for (int y = top; y <= bottom; y++ ) { - // for (int x = 0; x < left; ++x) - // if (get(x, y)) { - // left = x; - // break; - // } - // for (int x = _width-1; x > right; x--) - // if (get(x, y)) { - // right = x; - // break; - // } - // } - - // width = right - left + 1; - // height = bottom - top + 1; - // return width >= minSize && height >= minSize; - } - /** * This is useful in detecting a corner of a 'pure' barcode. * diff --git a/src/common/cpp_essentials/bitmatrix.rs b/src/common/cpp_essentials/bitmatrix.rs new file mode 100644 index 0000000..28619d8 --- /dev/null +++ b/src/common/cpp_essentials/bitmatrix.rs @@ -0,0 +1,61 @@ +use crate::common::BitMatrix; +use crate::common::Result; +use crate::point; + +impl BitMatrix { + pub fn Deflate( + &self, + width: u32, + height: u32, + top: f32, + left: f32, + subSampling: f32, + ) -> Result { + let mut result = BitMatrix::new(width, height)?; + + for y in 0..result.height() { + // for (int y = 0; y < result.height(); y++) { + let yOffset = top + y as f32 * subSampling; + for x in 0..result.width() { + // for (int x = 0; x < result.width(); x++) { + if (self.get_point(point(left + x as f32 * subSampling, yOffset))) { + result.set(x, y); + } + } + } + + Ok(result) + } + + pub fn findBoundingBox( + &self, + left: &mut u32, + top: &mut u32, + width: &mut u32, + height: &mut u32, + minSize: u32, + ) -> bool { + todo!() + // let right; + // let bottom; + // if (!self.getTopLeftOnBitWithPosition(left, top) || !self.getBottomRightOnBitWithPosition(right, bottom) || bottom - top + 1 < minSize) + // {return false;} + + // for (int y = top; y <= bottom; y++ ) { + // for (int x = 0; x < left; ++x) + // if (get(x, y)) { + // left = x; + // break; + // } + // for (int x = _width-1; x > right; x--) + // if (get(x, y)) { + // right = x; + // break; + // } + // } + + // width = right - left + 1; + // height = bottom - top + 1; + // return width >= minSize && height >= minSize; + } +} diff --git a/src/common/cpp_essentials/bitmatrix_cursor_trait.rs b/src/common/cpp_essentials/bitmatrix_cursor_trait.rs index 133e19a..8f68b47 100644 --- a/src/common/cpp_essentials/bitmatrix_cursor_trait.rs +++ b/src/common/cpp_essentials/bitmatrix_cursor_trait.rs @@ -165,27 +165,27 @@ pub trait BitMatrixCursorTrait { fn d(&self) -> Point; - - fn readPattern(&self, range : Option) -> Option - { + fn readPattern(&self, range: Option) -> Option { // let range = range.unwrap_or(0); - // let mut res :T; + // let mut res :T; // for i in res.into_iter() { // i = self.stepToEdge(1, range); // } - // // for (auto& i : res) - // // i = stepToEdge(1, range); - // return res; + // // for (auto& i : res) + // // i = stepToEdge(1, range); + // return res; todo!() } - - fn readPatternFromBlack(&self, maxWhitePrefix:i32, range: Option) -> Option - { + fn readPatternFromBlack(&self, maxWhitePrefix: i32, range: Option) -> Option { let range = range.unwrap_or(0); - if (maxWhitePrefix != 0 && self.isWhite() && !self.stepToEdge(Some(1), Some(maxWhitePrefix), None) > 0) - {return None;} - // return readPattern(range); + if (maxWhitePrefix != 0 + && self.isWhite() + && !self.stepToEdge(Some(1), Some(maxWhitePrefix), None) > 0) + { + return None; + } + // return readPattern(range); self.readPattern(Some(range)) } } diff --git a/src/common/cpp_essentials/mod.rs b/src/common/cpp_essentials/mod.rs index 4d85f66..40ad497 100644 --- a/src/common/cpp_essentials/mod.rs +++ b/src/common/cpp_essentials/mod.rs @@ -1,3 +1,4 @@ +mod bitmatrix; pub mod bitmatrix_cursor; pub mod bitmatrix_cursor_trait; pub mod concentric_finder; diff --git a/src/common/cpp_essentials/pattern.rs b/src/common/cpp_essentials/pattern.rs index b24915d..be4aef6 100644 --- a/src/common/cpp_essentials/pattern.rs +++ b/src/common/cpp_essentials/pattern.rs @@ -54,6 +54,12 @@ impl std::ops::IndexMut for PatternRow { } } +impl From> for PatternRow { + fn from(value: Vec) -> Self { + Self(value) + } +} + impl<'a> Iterator for PatternView<'_> { type Item = PatternType; diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 4f8997b..24c8e5f 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -750,61 +750,102 @@ pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result Result -{ - type Pattern = Vec; +pub fn DetectPureQR(image: &BitMatrix) -> Result { + type Pattern = Vec; -// #ifdef PRINT_DEBUG -// SaveAsPBM(image, "weg.pbm"); -// #endif + // #ifdef PRINT_DEBUG + // SaveAsPBM(image, "weg.pbm"); + // #endif - const MIN_MODULES: u32 = Version::DimensionOfVersion(1, false); - const MAX_MODULES:u32 = Version::DimensionOfVersion(40, false); + const MIN_MODULES: u32 = Version::DimensionOfVersion(1, false); + const MAX_MODULES: u32 = Version::DimensionOfVersion(40, false); - let left; + let left; let top; let width; let height; - if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || std::abs(width - height) > 1) - {return Err(Exceptions::NOT_FOUND)} - let right = left + width - 1; - let bottom = top + height - 1; + if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) + || (*width as i32 - *height as i32).abs() > 1) + { + return Err(Exceptions::NOT_FOUND); + } + let right = *left + *width - 1; + let bottom = *top + *height - 1; - let tl = point_i(*left, *top); - let tr = point_i(*right, *top); - let bl = point_i(*left, *bottom); - let diagonal : Pattern; - // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts - for [p,d] in [[tl, point_i(1,1)], [tr, point(-1.0,1.0)], [bl, point(1.0,-1.0)]] { - // for (auto [p, d] : {std::pair(tl, PointI{1, 1}), {tr, {-1, 1}}, {bl, {1, -1}}}) { - diagonal = EdgeTracer::new(image, p, d).readPatternFromBlack(1, width / 3 + 1).ok_or(Exceptions::NOT_FOUND)?; - // diagonal = BitMatrixCursorI(image, p, d).readPatternFromBlack(1, width / 3 + 1); - if (!IsPattern(diagonal, PATTERN) != 0.0) - {return Err(Exceptions::NOT_FOUND);} - } + let tl = point_i(*left, *top); + let tr = point_i(right, *top); + let bl = point_i(*left, bottom); + let diagonal: Pattern; + // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts + for [p, d] in [ + [tl, point_i(1, 1)], + [tr, point(-1.0, 1.0)], + [bl, point(1.0, -1.0)], + ] { + // for (auto [p, d] : {std::pair(tl, PointI{1, 1}), {tr, {-1, 1}}, {bl, {1, -1}}}) { + diagonal = EdgeTracer::new(image, p, d) + .readPatternFromBlack(1, Some(*width / 3 + 1)) + .ok_or(Exceptions::NOT_FOUND)?; + // diagonal = BitMatrixCursorI(image, p, d).readPatternFromBlack(1, width / 3 + 1); + let view = PatternView::new(&diagonal.into()); + if (!(IsPattern(&view, &PATTERN, None, 0.0, 0.0, None) != 0.0)) { + return Err(Exceptions::NOT_FOUND); + } + } - let fpWidth = Reduce(diagonal); - let dimension = - EstimateDimension(image, {tl + fpWidth / 2 * PointF(1, 1), fpWidth}, {tr + fpWidth / 2 * PointF(-1, 1), fpWidth}).dim; + let fpWidth = diagonal.iter().sum::() as i32; //Reduce(diagonal); + let dimension = EstimateDimension( + image, + ConcentricPattern { + p: tl + fpWidth as f32 / 2.0 * point_i(1, 1), + size: fpWidth, + }, + ConcentricPattern { + p: tr + fpWidth as f32 / 2.0 * point(-1.0, 1.0), + size: fpWidth, + }, + ) + .dim; - let moduleSize:f32 = float(width) / dimension; - if (dimension < MIN_MODULES || dimension > MAX_MODULES || - !image.isIn(PointF{left + moduleSize / 2 + (dimension - 1) * moduleSize, - top + moduleSize / 2 + (dimension - 1) * moduleSize})) - {return Err(Exceptions::NOT_FOUND);} + let moduleSize: f32 = ((*width) as f32) / dimension as f32; + if (dimension < MIN_MODULES as i32 + || dimension > MAX_MODULES as i32 + || !image.is_in(point( + *left as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize as f32, + *top as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, + ))) + { + return Err(Exceptions::NOT_FOUND); + } -// #ifdef PRINT_DEBUG -// LogMatrix log; -// LogMatrixWriter lmw(log, image, 5, "grid2.pnm"); -// for (int y = 0; y < dimension; y++) -// for (int x = 0; x < dimension; x++) -// log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize)); -// #endif + // #ifdef PRINT_DEBUG + // LogMatrix log; + // LogMatrixWriter lmw(log, image, 5, "grid2.pnm"); + // for (int y = 0; y < dimension; y++) + // for (int x = 0; x < dimension; x++) + // log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize)); + // #endif - // Now just read off the bits (this is a crop + subsample) - return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize), - {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; + // Now just read off the bits (this is a crop + subsample) + Ok(QRCodeDetectorResult { + bit_source: image.Deflate( + dimension as u32, + dimension as u32, + *top as f32 + moduleSize / 2.0, + *left as f32 + moduleSize / 2.0, + moduleSize, + )?, + result_points: vec![ + point_i(*left, *top), + point_i(right, *top), + point_i(right, bottom), + point_i(*left, bottom), + ], + }) + + // return {Deflate(image, dimension, dimension, top + moduleSize / 2, left + moduleSize / 2, moduleSize), + // {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; } pub fn DetectPureMQR( image:&BitMatrix) -> Result diff --git a/src/rxing_result_point.rs b/src/rxing_result_point.rs index 14a7db6..e589c8b 100644 --- a/src/rxing_result_point.rs +++ b/src/rxing_result_point.rs @@ -29,8 +29,8 @@ pub fn point_g>(x: T, y: T) -> Option { Some(Point::new(x.try_into().ok()?, y.try_into().ok()?)) } -pub fn point_i(x: u32, y: u32) -> Point { - Point::new(x as f32, y as f32) +pub fn point_i>(x: T, y: T) -> Point { + Point::new(x.into() as f32, y.into() as f32) } /** Currently necessary because the external OneDReader proc macro uses it. */ diff --git a/tests/cropped_transposed_luma.rs b/tests/cropped_transposed_luma.rs index f5747e2..af2382d 100644 --- a/tests/cropped_transposed_luma.rs +++ b/tests/cropped_transposed_luma.rs @@ -3,7 +3,7 @@ fn test_binarizer_init_empty() { assert!(rxing::helpers::detect_multiple_in_luma(DATA.to_vec(), 665, 286).is_ok()) } -static DATA: [u8; 190190] = [ +const DATA: [u8; 190190] = [ 220, 224, 216, 219, 221, 223, 221, 217, 216, 212, 203, 207, 213, 214, 208, 209, 218, 223, 221, 218, 212, 210, 202, 204, 209, 203, 211, 212, 216, 220, 223, 223, 221, 220, 219, 219, 219, 217, 214, 213, 215, 215, 213, 214, 214, 209, 203, 204, 212, 219, 221, 220, 225, 221, 208, 202, 205,