diff --git a/src/common/bit_matrix.rs b/src/common/bit_matrix.rs index 0868d7b..975219b 100644 --- a/src/common/bit_matrix.rs +++ b/src/common/bit_matrix.rs @@ -559,6 +559,33 @@ 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_cursor_trait.rs b/src/common/cpp_essentials/bitmatrix_cursor_trait.rs index c19ac87..133e19a 100644 --- a/src/common/cpp_essentials/bitmatrix_cursor_trait.rs +++ b/src/common/cpp_essentials/bitmatrix_cursor_trait.rs @@ -165,20 +165,27 @@ pub trait BitMatrixCursorTrait { fn d(&self) -> Point; - // template - // ARRAY readPattern(int range = 0) - // { - // ARRAY res; - // for (auto& i : res) - // i = stepToEdge(1, range); - // return res; - // } + + fn readPattern(&self, range : Option) -> Option + { + // let range = range.unwrap_or(0); + // 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; + todo!() + } - // template - // ARRAY readPatternFromBlack(int maxWhitePrefix, int range = 0) - // { - // if (maxWhitePrefix && isWhite() && !stepToEdge(1, maxWhitePrefix)) - // return {}; - // return readPattern(range); - // } + + 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); + self.readPattern(Some(range)) + } } diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 550e821..4f8997b 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -743,3 +743,167 @@ pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result Result +{ + type Pattern = Vec; + +// #ifdef PRINT_DEBUG +// SaveAsPBM(image, "weg.pbm"); +// #endif + + const MIN_MODULES: u32 = Version::DimensionOfVersion(1, false); + const MAX_MODULES:u32 = Version::DimensionOfVersion(40, false); + + 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; + + 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 fpWidth = Reduce(diagonal); + let dimension = + EstimateDimension(image, {tl + fpWidth / 2 * PointF(1, 1), fpWidth}, {tr + fpWidth / 2 * PointF(-1, 1), 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);} + +// #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}}}; +} + +pub fn DetectPureMQR( image:&BitMatrix) -> Result +{ + using Pattern = std::array; + + constexpr int MIN_MODULES = Version::DimensionOfVersion(1, true); + constexpr int MAX_MODULES = Version::DimensionOfVersion(4, true); + + int left, top, width, height; + if (!image.findBoundingBox(left, top, width, height, MIN_MODULES) || std::abs(width - height) > 1) + return {}; + int right = left + width - 1; + int bottom = top + height - 1; + + // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts + auto diagonal = BitMatrixCursorI(image, {left, top}, {1, 1}).readPatternFromBlack(1); + if (!IsPattern(diagonal, PATTERN)) + return {}; + + auto fpWidth = Reduce(diagonal); + float moduleSize = float(fpWidth) / 7; + int dimension = narrow_cast(std::lround(width / moduleSize)); + + if (dimension < MIN_MODULES || dimension > MAX_MODULES || + !image.isIn(PointF{left + moduleSize / 2 + (dimension - 1) * moduleSize, + top + moduleSize / 2 + (dimension - 1) * moduleSize})) + return {}; + +// #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}}}; +} + +pub fn SampleMQR( image:&BitMatrix, fp:ConcentricPattern) -> Result +{ + auto fpQuad = FindConcentricPatternCorners(image, fp, fp.size, 2); + if (!fpQuad) + return {}; + + auto srcQuad = Rectangle(7, 7, 0.5); + +// #if defined(_MSVC_LANG) // TODO: see MSVC issue https://developercommunity.visualstudio.com/t/constexpr-object-is-unable-to-be-used-as/10035065 +// static +// #else +// constexpr +// #endif + const PointI FORMAT_INFO_COORDS[] = {{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {6, 8}, {7, 8}, {8, 8}, + {8, 7}, {8, 6}, {8, 5}, {8, 4}, {8, 3}, {8, 2}, {8, 1}, {8, 0}}; + + FormatInformation bestFI; + PerspectiveTransform bestPT; + + for (int i = 0; i < 4; ++i) { + auto mod2Pix = PerspectiveTransform(srcQuad, RotatedCorners(*fpQuad, i)); + + auto check = [&](int i, bool checkOne) { + auto p = mod2Pix(centered(FORMAT_INFO_COORDS[i])); + return image.isIn(p) && (!checkOne || image.get(p)); + }; + + // check that we see both innermost timing pattern modules + if (!check(0, true) || !check(8, false) || !check(16, true)) + continue; + + int formatInfoBits = 0; + for (int i = 1; i <= 15; ++i) + AppendBit(formatInfoBits, image.get(mod2Pix(centered(FORMAT_INFO_COORDS[i])))); + + auto fi = FormatInformation::DecodeMQR(formatInfoBits); + if (fi.hammingDistance < bestFI.hammingDistance) { + bestFI = fi; + bestPT = mod2Pix; + } + } + + if (!bestFI.isValid()) + return {}; + + const int dim = Version::DimensionOfVersion(bestFI.microVersion, true); + + // check that we are in fact not looking at a corner of a non-micro QRCode symbol + // we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2). + int blackPixels = 0; + for (int i = 0; i < dim; ++i) { + auto px = bestPT(centered(PointI{i, dim})); + auto py = bestPT(centered(PointI{dim, i})); + blackPixels += (image.isIn(px) && image.get(px)) + (image.isIn(py) && image.get(py)); + } + if (blackPixels > 2 * dim / 3) + return {}; + + return SampleGrid(image, dim, dim, bestPT); +} \ No newline at end of file diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index cb9828c..d739fd8 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -89,15 +89,15 @@ impl Version { 17 + 4 * self.versionNumber } - pub fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 { + pub const fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 { Self::DimensionOffset(is_micro) + Self::DimensionStep(is_micro) * version } - pub fn DimensionOffset(is_micro: bool) -> u32 { + pub const fn DimensionOffset(is_micro: bool) -> u32 { todo!() } - pub fn DimensionStep(is_micro: bool) -> u32 { + pub const fn DimensionStep(is_micro: bool) -> u32 { todo!() }