From 7f02bd07254e2ff6a7b2371a7dd4fb10554eae42 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sun, 14 Jan 2024 17:14:55 -0600 Subject: [PATCH] port: rMQR: improve detection rate by using finder sub pattern https://github.com/zxing-cpp/zxing-cpp/commit/677fc21c7d6f9e43e6e1852ab801d16e27d4c563 --- src/qrcode/cpp_port/detector.rs | 118 ++++++++++++++++++ ...ata.txt => R7x43-H_inverted._metadata.txt} | 0 tests/common/abstract_black_box_test_case.rs | 9 -- tests/cpp_qr_code_blackbox_tests.rs | 20 ++- 4 files changed, 132 insertions(+), 15 deletions(-) rename test_resources/blackbox/cpp/rmqrcode-1/{R7x43-H_inverted.metadata.txt => R7x43-H_inverted._metadata.txt} (100%) diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 56b2bde..ee7615a 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -1232,6 +1232,124 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result { + let tl = a.center(); + let br = b.center(); + // rotate points such that topLeft of a is furthest away from b and topLeft of b is closest to a + // let dist2B = /*[c = br]*/| &a, &b| { Some(Point::distance(a, br).partial_cmp(&Point::distance(b, br))) }; + + let offsetATarget = + a.0.iter() + .max_by(|a, b| { + Point::distance(**a, br) + .partial_cmp(&Point::distance(**b, br)) + .unwrap_or(std::cmp::Ordering::Less) + }) + .ok_or(Exceptions::FORMAT)?; + let offsetA = + a.0.iter() + .position(|x| x == offsetATarget) + .ok_or(Exceptions::FORMAT)? as i32; + // let offsetA = std::max_element(a.begin(), a.end(), dist2B) - a.begin(); + // let dist2A = /*[c = tl]*/| a, b| { Point::distance(a, tl) < Point::distance(b, tl) }; + let offsetBTarget = + b.0.iter() + .min_by(|a, b| { + Point::distance(**a, tl) + .partial_cmp(&Point::distance(**b, tl)) + .unwrap_or(std::cmp::Ordering::Less) + }) + .ok_or(Exceptions::FORMAT)?; + let offsetB = + b.0.iter() + .position(|x| x == offsetBTarget) + .ok_or(Exceptions::FORMAT)? as i32; + // let offsetB = std::min_element(b.begin(), b.end(), dist2A) - b.begin(); + + let a = a.rotated_corners(Some(offsetA), None); + let b = b.rotated_corners(Some(offsetB), None); + // a = RotatedCorners(a, offsetA); + // b = RotatedCorners(b, offsetB); + let tr = (RegressionLine::intersect( + &RegressionLine::with_two_points(a[0], a[1]), + &RegressionLine::with_two_points(b[1], b[2]), + ) + .ok_or(Exceptions::FORMAT)? + + RegressionLine::intersect( + &RegressionLine::with_two_points(a[3], a[2]), + &RegressionLine::with_two_points(b[0], b[3]), + ) + .ok_or(Exceptions::FORMAT)?) + / 2.0; + + // let tr = (intersect(RegressionLine(a[0], a[1]), RegressionLine(b[1], b[2])) + // + intersect(RegressionLine(a[3], a[2]), RegressionLine(b[0], b[3]))) + // / 2; + let bl = (RegressionLine::intersect( + &RegressionLine::with_two_points(a[0], a[3]), + &RegressionLine::with_two_points(b[2], b[3]), + ) + .ok_or(Exceptions::FORMAT)? + + RegressionLine::intersect( + &RegressionLine::with_two_points(a[1], a[2]), + &RegressionLine::with_two_points(b[0], b[1]), + ) + .ok_or(Exceptions::FORMAT)?) + / 2.0; + // let bl = (intersect(RegressionLine(a[0], a[3]), RegressionLine(b[2], b[3])) + // + intersect(RegressionLine(a[1], a[2]), RegressionLine(b[0], b[1]))) + // / 2; + + // log(tr, 2); + // log(bl, 2); + + Ok(Quadrilateral::from([tl, tr, br, bl])) + }; + + if let Some(found) = LocateAlignmentPattern( + image, + fp.size / 7, + bestPT.transform_point(Into::::into(dim) - point_f(3.0, 3.0)), + ) { + // if ( found ) { + // log(*found, 2); + if let Some(spQuad) = FindConcentricPatternCorners(image, found, fp.size / 2, 1) { + // if (auto spQuad = FindConcentricPatternCorners(image, *found, fp.size / 2, 1)) { + let mut dest = intersectQuads(&fpQuad, &spQuad)?; + if (dim.y <= 9) { + bestPT = PerspectiveTransform::quadrilateralToQuadrilateral( + Quadrilateral::from([ + point(6.5, 0.5), + point(dim.x as f32 - 1.5, dim.y as f32 - 3.5), + point(dim.x as f32 - 1.5, dim.y as f32 - 1.5), + point(6.5, 6.5), + ]), + Quadrilateral::from([ + *fpQuad.top_right(), + *spQuad.top_right(), + *spQuad.bottom_right(), + *fpQuad.bottom_right(), + ]), + )?; + // bestPT = PerspectiveTransform({{6.5, 0.5}, {dim.x - 1.5, dim.y - 3.5}, {dim.x - 1.5, dim.y - 1.5}, {6.5, 6.5}}, + // {fpQuad->topRight(), spQuad->topRight(), spQuad->bottomRight(), fpQuad->bottomRight()}); + } else { + dest[0] = fp.p; + dest[2] = found; + bestPT = PerspectiveTransform::quadrilateralToQuadrilateral( + Quadrilateral::from([ + point(3.5, 3.5), + point(dim.x as f32 - 2.5, 3.5), + point(dim.x as f32 - 2.5, dim.y as f32 - 2.5), + point(3.5, dim.y as f32 - 2.5), + ]), + dest, + )?; + } + } + } + let grid_sampler = DefaultGridSampler; let (sample, rps) = grid_sampler.sample_grid( image, diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted._metadata.txt similarity index 100% rename from test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt rename to test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted._metadata.txt diff --git a/tests/common/abstract_black_box_test_case.rs b/tests/common/abstract_black_box_test_case.rs index 8bd8a31..951592a 100644 --- a/tests/common/abstract_black_box_test_case.rs +++ b/tests/common/abstract_black_box_test_case.rs @@ -309,15 +309,6 @@ impl AbstractBlackBoxTestCase { log::fine(format!("could not read at rotation {rotation} w/TH: {e:?}")); } } - // try { - // if (decode(bitmap, rotation, expectedText, expectedMetadata, true)) { - // tryHarderCounts[x]+=1; - // } else { - // tryHarderMisreadCounts[x]+=1; - // } - // } catch (ReaderException ignored) { - // log::fine(format!("could not read at rotation {} w/TH", rotation)); - // } } } diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index e2bc761..ac90a81 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -15,7 +15,9 @@ */ #![cfg(feature = "image")] -use rxing::{qrcode::cpp_port::QrReader, BarcodeFormat, MultiUseMultiFormatReader}; +use rxing::{ + qrcode::cpp_port::QrReader, BarcodeFormat, MultiFormatReader, MultiUseMultiFormatReader, +}; mod common; @@ -330,13 +332,19 @@ fn cpp_qrcode_black_box7_test_case() { fn cpp_rmqr_blackbox_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/cpp/rmqrcode-1", - QrReader, + MultiFormatReader::default(), BarcodeFormat::RECTANGULAR_MICRO_QR_CODE, ); - tester.add_test(2, 2, 0.0); - tester.add_test(1, 1, 90.0); - tester.add_test(1, 1, 180.0); - tester.add_test(1, 1, 270.0); + // tester.ignore_pure = true; + tester.add_test(3, 3, 0.0); + tester.add_test(3, 3, 90.0); + tester.add_test(3, 3, 180.0); + tester.add_test(3, 3, 270.0); + + tester.add_hint( + rxing::DecodeHintType::ALSO_INVERTED, + rxing::DecodeHintValue::AlsoInverted(true), + ); tester.test_black_box(); }