diff --git a/src/aztec/detector.rs b/src/aztec/detector.rs index 61b38d2..c4192e7 100644 --- a/src/aztec/detector.rs +++ b/src/aztec/detector.rs @@ -23,7 +23,7 @@ use crate::{ BitMatrix, DefaultGridSampler, GridSampler, }, exceptions::Exceptions, - RXingResultPoint, + RXingResultPoint, ResultPoint, }; use super::AztecDetectorResult::AztecDetectorRXingResult; diff --git a/src/common/detector/mod.rs b/src/common/detector/mod.rs index f78d45a..cd93b16 100644 --- a/src/common/detector/mod.rs +++ b/src/common/detector/mod.rs @@ -1,6 +1,6 @@ pub mod MathUtils; use crate::common::BitMatrix; -use crate::{Exceptions, RXingResultPoint}; +use crate::{Exceptions, RXingResultPoint, ResultPoint}; /* * Copyright 2009 ZXing authors diff --git a/src/lib.rs b/src/lib.rs index cd9e4e1..15b8bb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ pub mod aztec; -pub mod qrcode; pub mod client; pub mod common; mod exceptions; +pub mod qrcode; pub use exceptions::Exceptions; @@ -26,10 +26,9 @@ mod PlanarYUVLuminanceSourceTestCase; #[cfg(test)] mod RGBLuminanceSourceTestCase; - -pub type EncodingHintDictionary = HashMap; -pub type DecodingHintDictionary= HashMap; -pub type MetadataDictionary = HashMap; +pub type EncodingHintDictionary = HashMap; +pub type DecodingHintDictionary = HashMap; +pub type MetadataDictionary = HashMap; /* * Copyright 2007 ZXing authors @@ -54,7 +53,7 @@ pub type MetadataDictionary = HashMap for RXingResultMetadataType { "OTHER" => RXingResultMetadataType::OTHER, "ORIENTATION" => RXingResultMetadataType::ORIENTATION, "BYTE_SEGMENTS" => RXingResultMetadataType::BYTE_SEGMENTS, - "ERROR_CORRECTION_LEVEL"=> RXingResultMetadataType::ERROR_CORRECTION_LEVEL, - "ISSUE_NUMBER"=> RXingResultMetadataType::ISSUE_NUMBER, - "SUGGESTED_PRICE"=> RXingResultMetadataType::SUGGESTED_PRICE, - "POSSIBLE_COUNTRY"=> RXingResultMetadataType::POSSIBLE_COUNTRY, - "UPC_EAN_EXTENSION"=>RXingResultMetadataType::UPC_EAN_EXTENSION, - "PDF417_EXTRA_METADATA"=> RXingResultMetadataType::PDF417_EXTRA_METADATA, - "STRUCTURED_APPEND_SEQUENCE"=> RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE, - "STRUCTURED_APPEND_PARITY"=>RXingResultMetadataType::STRUCTURED_APPEND_PARITY, - "SYMBOLOGY_IDENTIFIER"=>RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, + "ERROR_CORRECTION_LEVEL" => RXingResultMetadataType::ERROR_CORRECTION_LEVEL, + "ISSUE_NUMBER" => RXingResultMetadataType::ISSUE_NUMBER, + "SUGGESTED_PRICE" => RXingResultMetadataType::SUGGESTED_PRICE, + "POSSIBLE_COUNTRY" => RXingResultMetadataType::POSSIBLE_COUNTRY, + "UPC_EAN_EXTENSION" => RXingResultMetadataType::UPC_EAN_EXTENSION, + "PDF417_EXTRA_METADATA" => RXingResultMetadataType::PDF417_EXTRA_METADATA, + "STRUCTURED_APPEND_SEQUENCE" => RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE, + "STRUCTURED_APPEND_PARITY" => RXingResultMetadataType::STRUCTURED_APPEND_PARITY, + "SYMBOLOGY_IDENTIFIER" => RXingResultMetadataType::SYMBOLOGY_IDENTIFIER, _ => RXingResultMetadataType::OTHER, } } } -#[derive(Debug,PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub enum RXingResultMetadataValue { /** * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. @@ -1174,6 +1173,107 @@ impl fmt::Display for RXingResult { //package com.google.zxing; use crate::common::detector::MathUtils; +pub trait ResultPoint { + fn getX(&self) -> f32; + fn getY(&self) -> f32; +} + +pub mod result_point_utils { + use crate::{common::detector::MathUtils, ResultPoint}; + + /** + * Orders an array of three RXingResultPoints in an order [A,B,C] such that AB is less than AC + * and BC is less than AC, and the angle between BC and BA is less than 180 degrees. + * + * @param patterns array of three {@code RXingResultPoint} to order + */ + pub fn orderBestPatterns(patterns: &mut [T; 3]) { + // Find distances between pattern centers + let zeroOneDistance = MathUtils::distance_float( + patterns[0].getX(), + patterns[0].getY(), + patterns[1].getX(), + patterns[1].getY(), + ); + let oneTwoDistance = MathUtils::distance_float( + patterns[1].getX(), + patterns[1].getY(), + patterns[2].getX(), + patterns[2].getY(), + ); + let zeroTwoDistance = MathUtils::distance_float( + patterns[0].getX(), + patterns[0].getY(), + patterns[2].getX(), + patterns[2].getY(), + ); + + let mut pointA; //: &RXingResultPoint; + let mut pointB; //: &RXingResultPoint; + let mut pointC; //: &RXingResultPoint; + // Assume one closest to other two is B; A and C will just be guesses at first + if oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance { + pointB = patterns[0]; + pointA = patterns[1]; + pointC = patterns[2]; + } else if zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance { + pointB = patterns[1]; + pointA = patterns[0]; + pointC = patterns[2]; + } else { + pointB = patterns[2]; + pointA = patterns[0]; + pointC = patterns[1]; + } + + // Use cross product to figure out whether A and C are correct or flipped. + // This asks whether BC x BA has a positive z component, which is the arrangement + // we want for A, B, C. If it's negative, then we've got it flipped around and + // should swap A and C. + if crossProductZ(pointA, pointB, pointC) < 0.0f32 { + let temp = pointA; + pointA = pointC; + pointC = temp; + } + + let pa = pointA; + let pb = pointB; + let pc = pointC; + + patterns[0] = pa; + patterns[1] = pb; + patterns[2] = pc; + } + + /** + * @param pattern1 first pattern + * @param pattern2 second pattern + * @return distance between two points + */ + pub fn distance(pattern1: T, pattern2: T) -> f32 { + return MathUtils::distance_float( + pattern1.getX(), + pattern1.getY(), + pattern2.getX(), + pattern2.getY(), + ); + } + + /** + * Returns the z component of the cross product between vectors BC and BA. + */ + pub fn crossProductZ( + pointA: T, + pointB: T, + pointC: T, + ) -> f32 { + let bX = pointB.getX(); + let bY = pointB.getY(); + return ((pointC.getX() - bX) * (pointA.getY() - bY)) + - ((pointC.getY() - bY) * (pointA.getX() - bX)); + } +} + /** *

Encapsulates a point of interest in an image containing a barcode. Typically, this * would be the location of a finder pattern or the corner of the barcode, for example.

@@ -1201,100 +1301,16 @@ impl RXingResultPoint { pub const fn new(x: f32, y: f32) -> Self { Self { x, y } } +} - pub fn getX(&self) -> f32 { +impl ResultPoint for RXingResultPoint { + fn getX(&self) -> f32 { return self.x; } - pub fn getY(&self) -> f32 { + fn getY(&self) -> f32 { return self.y; } - - /** - * Orders an array of three RXingResultPoints in an order [A,B,C] such that AB is less than AC - * and BC is less than AC, and the angle between BC and BA is less than 180 degrees. - * - * @param patterns array of three {@code RXingResultPoint} to order - */ - pub fn orderBestPatterns(patterns: &mut Vec) { - // Find distances between pattern centers - let zeroOneDistance = MathUtils::distance_float( - patterns[0].getX(), - patterns[0].getY(), - patterns[1].getX(), - patterns[1].getY(), - ); - let oneTwoDistance = MathUtils::distance_float( - patterns[1].getX(), - patterns[1].getY(), - patterns[2].getX(), - patterns[2].getY(), - ); - let zeroTwoDistance = MathUtils::distance_float( - patterns[0].getX(), - patterns[0].getY(), - patterns[2].getX(), - patterns[2].getY(), - ); - - let mut pointA: &RXingResultPoint; - let mut pointB: &RXingResultPoint; - let mut pointC: &RXingResultPoint; - // Assume one closest to other two is B; A and C will just be guesses at first - if oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance { - pointB = &patterns[0]; - pointA = &patterns[1]; - pointC = &patterns[2]; - } else if zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance { - pointB = &patterns[1]; - pointA = &patterns[0]; - pointC = &patterns[2]; - } else { - pointB = &patterns[2]; - pointA = &patterns[0]; - pointC = &patterns[1]; - } - - // Use cross product to figure out whether A and C are correct or flipped. - // This asks whether BC x BA has a positive z component, which is the arrangement - // we want for A, B, C. If it's negative, then we've got it flipped around and - // should swap A and C. - if RXingResultPoint::crossProductZ(&pointA, &pointB, &pointC) < 0.0f32 { - let temp = pointA; - pointA = pointC; - pointC = temp; - } - - let pa = (*pointA).clone(); - let pb = (*pointB).clone(); - let pc = (*pointC).clone(); - - patterns[0] = pa; - patterns[1] = pb; - patterns[2] = pc; - } - - /** - * @param pattern1 first pattern - * @param pattern2 second pattern - * @return distance between two points - */ - pub fn distance(pattern1: &RXingResultPoint, pattern2: &RXingResultPoint) -> f32 { - return MathUtils::distance_float(pattern1.x, pattern1.y, pattern2.x, pattern2.y); - } - - /** - * Returns the z component of the cross product between vectors BC and BA. - */ - pub fn crossProductZ( - pointA: &RXingResultPoint, - pointB: &RXingResultPoint, - pointC: &RXingResultPoint, - ) -> f32 { - let bX = pointB.x; - let bY = pointB.y; - return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX)); - } } impl fmt::Display for RXingResultPoint { diff --git a/src/qrcode/detector/alignment_pattern.rs b/src/qrcode/detector/alignment_pattern.rs index 8a1aef3..8dc7ca8 100644 --- a/src/qrcode/detector/alignment_pattern.rs +++ b/src/qrcode/detector/alignment_pattern.rs @@ -16,7 +16,7 @@ //RXingResultPoint -use crate::RXingResultPoint; +use crate::{RXingResultPoint, ResultPoint}; /** *

Encapsulates an alignment pattern, which are the smaller square patterns found in @@ -24,17 +24,27 @@ use crate::RXingResultPoint; * * @author Sean Owen */ -#[derive(Clone)] +#[derive(Debug,Clone, Copy,PartialEq)] pub struct AlignmentPattern { estimatedModuleSize: f32, - internal_result_point: RXingResultPoint, + point: (f32, f32), +} + +impl ResultPoint for AlignmentPattern { + fn getX(&self) -> f32 { + self.point.0 + } + + fn getY(&self) -> f32 { + self.point.1 + } } impl AlignmentPattern { pub fn new(posX: f32, posY: f32, estimatedModuleSize: f32) -> Self { Self { estimatedModuleSize, - internal_result_point: RXingResultPoint { x: posX, y: posY }, + point: (posX, posY), } } @@ -43,9 +53,7 @@ impl AlignmentPattern { * position and size -- meaning, it is at nearly the same center with nearly the same size.

*/ pub fn aboutEquals(&self, moduleSize: f32, i: f32, j: f32) -> bool { - if (i - self.internal_result_point.getY()).abs() <= moduleSize - && (j - self.internal_result_point.getX()).abs() <= moduleSize - { + if (i - self.getY()).abs() <= moduleSize && (j - self.getX()).abs() <= moduleSize { let moduleSizeDiff = (moduleSize - self.estimatedModuleSize).abs(); return moduleSizeDiff <= 1.0 || moduleSizeDiff <= self.estimatedModuleSize; } @@ -57,13 +65,9 @@ impl AlignmentPattern { * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two. */ pub fn combineEstimate(&self, i: f32, j: f32, newModuleSize: f32) -> AlignmentPattern { - let combinedX = (self.internal_result_point.getX() + j) / 2.0; - let combinedY = (self.internal_result_point.getY() + i) / 2.0; + let combinedX = (self.getX() + j) / 2.0; + let combinedY = (self.getY() + i) / 2.0; let combinedModuleSize = (self.estimatedModuleSize + newModuleSize) / 2.0; AlignmentPattern::new(combinedX, combinedY, combinedModuleSize) } - - pub fn as_RXingResultPoint(&self) -> &RXingResultPoint { -&self.internal_result_point - } } diff --git a/src/qrcode/detector/alignment_pattern_finder.rs b/src/qrcode/detector/alignment_pattern_finder.rs index 8b6fdbe..290537e 100644 --- a/src/qrcode/detector/alignment_pattern_finder.rs +++ b/src/qrcode/detector/alignment_pattern_finder.rs @@ -306,7 +306,7 @@ impl AlignmentPatternFinder { // Hadn't found this before; save it let point = AlignmentPattern::new(centerJ, centerI, estimatedModuleSize); if self.resultPointCallback.is_some() { - self.resultPointCallback.as_ref().unwrap()(point.as_RXingResultPoint()); + self.resultPointCallback.as_ref().unwrap()(&point); } self.possibleCenters.push(point); // if self.resultPointCallback.is_some() { diff --git a/src/qrcode/detector/finder_pattern.rs b/src/qrcode/detector/finder_pattern.rs index 2f82baf..57bbd8b 100644 --- a/src/qrcode/detector/finder_pattern.rs +++ b/src/qrcode/detector/finder_pattern.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::RXingResultPoint; +use crate::{RXingResultPoint, ResultPoint}; /** *

Encapsulates a finder pattern, which are the three square patterns found in @@ -23,10 +23,21 @@ use crate::RXingResultPoint; * * @author Sean Owen */ -pub struct FinderPattern { +#[derive(Debug,Clone, Copy,PartialEq)] + pub struct FinderPattern { estimatedModuleSize: f32, count: usize, - internal_result_point: RXingResultPoint, + point: (f32, f32), +} + +impl ResultPoint for FinderPattern { + fn getX(&self) -> f32 { + self.point.0 + } + + fn getY(&self) -> f32 { + self.point.1 + } } impl FinderPattern { @@ -38,7 +49,7 @@ impl FinderPattern { Self { estimatedModuleSize, count, - internal_result_point: RXingResultPoint::new(posX, posY), + point: (posX, posY), } } @@ -55,9 +66,7 @@ impl FinderPattern { * position and size -- meaning, it is at nearly the same center with nearly the same size.

*/ pub fn aboutEquals(&self, moduleSize: f32, i: f32, j: f32) -> bool { - if (i - self.internal_result_point.getY()).abs() <= moduleSize - && (j - self.internal_result_point.getX()).abs() <= moduleSize - { + if (i - self.getY()).abs() <= moduleSize && (j - self.getX()).abs() <= moduleSize { let moduleSizeDiff = (moduleSize - self.estimatedModuleSize).abs(); return moduleSizeDiff <= 1.0 || moduleSizeDiff <= self.estimatedModuleSize; } @@ -71,8 +80,8 @@ impl FinderPattern { */ pub fn combineEstimate(&self, i: f32, j: f32, newModuleSize: f32) -> FinderPattern { let combinedCount = self.count as f32 + 1.0; - let combinedX = (self.count as f32 * self.internal_result_point.getX() + j) / combinedCount; - let combinedY = (self.count as f32 * self.internal_result_point.getY() + i) / combinedCount; + let combinedX = (self.count as f32 * self.getX() + j) / combinedCount; + let combinedY = (self.count as f32 * self.getY() + i) / combinedCount; let combinedModuleSize = (self.count as f32 * self.estimatedModuleSize + newModuleSize) / combinedCount; FinderPattern::private_new( diff --git a/src/qrcode/detector/finder_pattern_finder.rs b/src/qrcode/detector/finder_pattern_finder.rs index a9799d5..77bc11b 100755 --- a/src/qrcode/detector/finder_pattern_finder.rs +++ b/src/qrcode/detector/finder_pattern_finder.rs @@ -14,11 +14,13 @@ * limitations under the License. */ -use crate::{common::BitMatrix, RXingResultPointCallback, DecodingHintDictionary}; +use crate::{ + common::BitMatrix, result_point_utils, DecodeHintType, DecodingHintDictionary, Exceptions, + RXingResultPoint, RXingResultPointCallback, ResultPoint, +}; use super::{FinderPattern, FinderPatternInfo}; - /** *

This class attempts to find finder patterns in a QR Code. Finder patterns are the square * markers at three corners of a QR Code.

@@ -28,674 +30,767 @@ use super::{FinderPattern, FinderPatternInfo}; * @author Sean Owen */ pub struct FinderPatternFinder { - - image:BitMatrix, - possibleCenters:Vec, - hasSkipped:bool, - crossCheckStateCount:Vec, - resultPointCallback:Option, + image: BitMatrix, + possibleCenters: Vec, + hasSkipped: bool, + crossCheckStateCount: [u32; 5], + resultPointCallback: Option, } -impl FinderPatternFinder{ - const CENTER_QUORUM :u32= 2; - // private static final EstimatedModuleComparator moduleComparator = new EstimatedModuleComparator(); - const MIN_SKIP :u32 = 3; // 1 pixel/module times 3 modules/center - const MAX_MODULES:u32 = 97; // support up to version 20 for mobile clients +impl FinderPatternFinder { + const CENTER_QUORUM: usize = 2; + // private static final EstimatedModuleComparator moduleComparator = new EstimatedModuleComparator(); + const MIN_SKIP: u32 = 3; // 1 pixel/module times 3 modules/center + const MAX_MODULES: u32 = 97; // support up to version 20 for mobile clients - /** - *

Creates a finder that will search the image for three finder patterns.

- * - * @param image image to search - */ - pub fn new( image:BitMatrix) -> Self{ - Self::with_callback(image, None) - } - - pub fn with_callback( image:BitMatrix, resultPointCallback:Option) -> Self{ - Self { - image, - possibleCenters: Vec::new(), - hasSkipped: false, - crossCheckStateCount: [0u32;5], - resultPointCallback, - } - } - - pub fn getImage(&self) -> &BitMatrix{ - &self.image - } - - pub fn getPossibleCenters(&self) -> &Vec{ - &self.possibleCenters - } - - pub fn find(&self, hints:&DecodingHintDictionary) -> Result { - boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); - int maxI = image.getHeight(); - int maxJ = image.getWidth(); - // We are looking for black/white/black/white/black modules in - // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far - - // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the - // image, and then account for the center being 3 modules in size. This gives the smallest - // number of pixels the center could be, so skip this often. When trying harder, look for all - // QR versions regardless of how dense they are. - int iSkip = (3 * maxI) / (4 * MAX_MODULES); - if (iSkip < MIN_SKIP || tryHarder) { - iSkip = MIN_SKIP; + /** + *

Creates a finder that will search the image for three finder patterns.

+ * + * @param image image to search + */ + pub fn new(image: BitMatrix) -> Self { + Self::with_callback(image, None) } - boolean done = false; - int[] stateCount = new int[5]; - for (int i = iSkip - 1; i < maxI && !done; i += iSkip) { - // Get a row of black/white values - doClearCounts(stateCount); - int currentState = 0; - for (int j = 0; j < maxJ; j++) { - if (image.get(j, i)) { - // Black pixel - if ((currentState & 1) == 1) { // Counting white pixels - currentState++; - } - stateCount[currentState]++; - } else { // White pixel - if ((currentState & 1) == 0) { // Counting black pixels - if (currentState == 4) { // A winner? - if (foundPatternCross(stateCount)) { // Yes - boolean confirmed = handlePossibleCenter(stateCount, i, j); - if (confirmed) { - // Start examining every other line. Checking each line turned out to be too - // expensive and didn't improve performance. - iSkip = 2; - if (hasSkipped) { - done = haveMultiplyConfirmedCenters(); - } else { - int rowSkip = findRowSkip(); - if (rowSkip > stateCount[2]) { - // Skip rows between row of lower confirmed center - // and top of presumed third confirmed center - // but back up a bit to get a full chance of detecting - // it, entire width of center of finder pattern + pub fn with_callback( + image: BitMatrix, + resultPointCallback: Option, + ) -> Self { + Self { + image, + possibleCenters: Vec::new(), + hasSkipped: false, + crossCheckStateCount: [0u32; 5], + resultPointCallback, + } + } - // Skip by rowSkip, but back off by stateCount[2] (size of last center - // of pattern we saw) to be conservative, and also back off by iSkip which - // is about to be re-added - i += rowSkip - stateCount[2] - iSkip; - j = maxJ - 1; + pub fn getImage(&self) -> &BitMatrix { + &self.image + } + + pub fn getPossibleCenters(&self) -> &Vec { + &self.possibleCenters + } + + pub fn find( + &mut self, + hints: &DecodingHintDictionary, + ) -> Result { + let tryHarder = hints.contains_key(&DecodeHintType::TRY_HARDER); + let maxI = self.image.getHeight(); + let maxJ = self.image.getWidth(); + // We are looking for black/white/black/white/black modules in + // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far + + // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the + // image, and then account for the center being 3 modules in size. This gives the smallest + // number of pixels the center could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + let mut iSkip = (3 * maxI) / (4 * Self::MAX_MODULES); + if iSkip < Self::MIN_SKIP || tryHarder { + iSkip = Self::MIN_SKIP; + } + + let mut done = false; + let mut stateCount = [0u32; 5]; + let mut i = iSkip - 1; + while i < maxI && !done { + // for (int i = iSkip - 1; i < maxI && !done; i += iSkip) { + // Get a row of black/white values + FinderPatternFinder::doClearCounts(&mut stateCount); + let mut currentState = 0; + let mut j = 0; + while j < maxJ { + // for (int j = 0; j < maxJ; j++) { + if self.image.get(j, i) { + // Black pixel + if (currentState & 1) == 1 { + // Counting white pixels + currentState += 1; } - } + stateCount[currentState] += 1; } else { - doShiftCounts2(stateCount); - currentState = 3; - continue; + // White pixel + if (currentState & 1) == 0 { + // Counting black pixels + if currentState == 4 { + // A winner? + if FinderPatternFinder::foundPatternCross(&stateCount) { + // Yes + let confirmed = self.handlePossibleCenter(&stateCount, i, j); + if confirmed { + // Start examining every other line. Checking each line turned out to be too + // expensive and didn't improve performance. + iSkip = 2; + if self.hasSkipped { + done = self.haveMultiplyConfirmedCenters(); + } else { + let rowSkip = self.findRowSkip(); + if rowSkip > stateCount[2] { + // Skip rows between row of lower confirmed center + // and top of presumed third confirmed center + // but back up a bit to get a full chance of detecting + // it, entire width of center of finder pattern + + // Skip by rowSkip, but back off by stateCount[2] (size of last center + // of pattern we saw) to be conservative, and also back off by iSkip which + // is about to be re-added + i += rowSkip - stateCount[2] - iSkip; + j = maxJ - 1; + } + } + } else { + FinderPatternFinder::doShiftCounts2(&mut stateCount); + currentState = 3; + continue; + } + // Clear state to start looking again + currentState = 0; + FinderPatternFinder::doClearCounts(&mut stateCount); + } else { + // No, shift counts back by two + FinderPatternFinder::doShiftCounts2(&mut stateCount); + currentState = 3; + } + } else { + currentState += 1; + stateCount[currentState] += 1; + } + } else { + // Counting white pixels + stateCount[currentState] += 1; + } } - // Clear state to start looking again - currentState = 0; - doClearCounts(stateCount); - } else { // No, shift counts back by two - doShiftCounts2(stateCount); - currentState = 3; - } - } else { - stateCount[++currentState]++; } - } else { // Counting white pixels - stateCount[currentState]++; - } + if FinderPatternFinder::foundPatternCross(&stateCount) { + let confirmed = self.handlePossibleCenter(&stateCount, i, maxJ); + if confirmed { + iSkip = stateCount[0]; + if self.hasSkipped { + // Found a third one + done = self.haveMultiplyConfirmedCenters(); + } + } + } + + i += iSkip; } - } - if (foundPatternCross(stateCount)) { - boolean confirmed = handlePossibleCenter(stateCount, i, maxJ); - if (confirmed) { - iSkip = stateCount[0]; - if (hasSkipped) { - // Found a third one - done = haveMultiplyConfirmedCenters(); - } + + let mut patternInfo = self.selectBestPatterns()?; + result_point_utils::orderBestPatterns(&mut patternInfo); + + Ok(FinderPatternInfo::new(patternInfo)) + } + + /** + * Given a count of black/white/black/white/black pixels just seen and an end position, + * figures the location of the center of this run. + */ + fn centerFromEnd(stateCount: &[u32], end: u32) -> f32 { + ((end - stateCount[4] - stateCount[3]) - stateCount[2]) as f32 / 2.0 + } + + /** + * @param stateCount count of black/white/black/white/black pixels just read + * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios + * used by finder patterns to be considered a match + */ + pub fn foundPatternCross(stateCount: &[u32]) -> bool { + let mut totalModuleSize = 0; + for i in 0..5 { + // for (int i = 0; i < 5; i++) { + let count = stateCount[i]; + if count == 0 { + return false; + } + totalModuleSize += count; } - } - } - - FinderPattern[] patternInfo = selectBestPatterns(); - RXingResultPoint.orderBestPatterns(patternInfo); - - return new FinderPatternInfo(patternInfo); - } - - /** - * Given a count of black/white/black/white/black pixels just seen and an end position, - * figures the location of the center of this run. - */ - fn centerFromEnd(stateCount:&[u32], end:u32) -> f32 { - return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f; - } - - /** - * @param stateCount count of black/white/black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios - * used by finder patterns to be considered a match - */ - pub fn foundPatternCross( stateCount:&[u32]) -> bool{ - int totalModuleSize = 0; - for (int i = 0; i < 5; i++) { - int count = stateCount[i]; - if (count == 0) { - return false; - } - totalModuleSize += count; - } - if (totalModuleSize < 7) { - return false; - } - float moduleSize = totalModuleSize / 7.0f; - float maxVariance = moduleSize / 2.0f; - // Allow less than 50% variance from 1-1-3-1-1 proportions - return - Math.abs(moduleSize - stateCount[0]) < maxVariance && - Math.abs(moduleSize - stateCount[1]) < maxVariance && - Math.abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance && - Math.abs(moduleSize - stateCount[3]) < maxVariance && - Math.abs(moduleSize - stateCount[4]) < maxVariance; - } - - /** - * @param stateCount count of black/white/black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios - * used by finder patterns to be considered a match - */ - pub fn foundPatternDiagonal( stateCount: &[u32]) -> bool{ - int totalModuleSize = 0; - for (int i = 0; i < 5; i++) { - int count = stateCount[i]; - if (count == 0) { - return false; - } - totalModuleSize += count; - } - if (totalModuleSize < 7) { - return false; - } - float moduleSize = totalModuleSize / 7.0f; - float maxVariance = moduleSize / 1.333f; - // Allow less than 75% variance from 1-1-3-1-1 proportions - return - Math.abs(moduleSize - stateCount[0]) < maxVariance && - Math.abs(moduleSize - stateCount[1]) < maxVariance && - Math.abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance && - Math.abs(moduleSize - stateCount[3]) < maxVariance && - Math.abs(moduleSize - stateCount[4]) < maxVariance; - } - - fn getCrossCheckStateCount(&self) -> &[u32] { - doClearCounts(crossCheckStateCount); - return crossCheckStateCount; - } - - #[deprecated] - pub fn clearCounts(&self, counts:&[u32]) { - doClearCounts(counts); - } - - #[deprecated] - pub fn shiftCounts2(&self, stateCount:&[u32]) { - doShiftCounts2(stateCount); - } - - pub fn doClearCounts(icounts:&[u32]) { - Arrays.fill(counts, 0); - } - - pub fn doShiftCounts2( stateCount:&[u32]) { - stateCount[0] = stateCount[2]; - stateCount[1] = stateCount[3]; - stateCount[2] = stateCount[4]; - stateCount[3] = 1; - stateCount[4] = 0; - } - - /** - * After a vertical and horizontal scan finds a potential finder pattern, this method - * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible - * finder pattern to see if the same proportion is detected. - * - * @param centerI row where a finder pattern was detected - * @param centerJ center of the section that appears to cross a finder pattern - * @return true if proportions are withing expected limits - */ - fn crossCheckDiagonal(&self, centerI:u32, centerJ:u32) -> bool{ - int[] stateCount = getCrossCheckStateCount(); - - // Start counting up, left from center finding black center mass - int i = 0; - while (centerI >= i && centerJ >= i && image.get(centerJ - i, centerI - i)) { - stateCount[2]++; - i++; - } - if (stateCount[2] == 0) { - return false; - } - - // Continue up, left finding white space - while (centerI >= i && centerJ >= i && !image.get(centerJ - i, centerI - i)) { - stateCount[1]++; - i++; - } - if (stateCount[1] == 0) { - return false; - } - - // Continue up, left finding black border - while (centerI >= i && centerJ >= i && image.get(centerJ - i, centerI - i)) { - stateCount[0]++; - i++; - } - if (stateCount[0] == 0) { - return false; - } - - int maxI = image.getHeight(); - int maxJ = image.getWidth(); - - // Now also count down, right from center - i = 1; - while (centerI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, centerI + i)) { - stateCount[2]++; - i++; - } - - while (centerI + i < maxI && centerJ + i < maxJ && !image.get(centerJ + i, centerI + i)) { - stateCount[3]++; - i++; - } - if (stateCount[3] == 0) { - return false; - } - - while (centerI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, centerI + i)) { - stateCount[4]++; - i++; - } - if (stateCount[4] == 0) { - return false; - } - - return foundPatternDiagonal(stateCount); - } - - /** - *

After a horizontal scan finds a potential finder pattern, this method - * "cross-checks" by scanning down vertically through the center of the possible - * finder pattern to see if the same proportion is detected.

- * - * @param startI row where a finder pattern was detected - * @param centerJ center of the section that appears to cross a finder pattern - * @param maxCount maximum reasonable number of modules that should be - * observed in any reading state, based on the results of the horizontal scan - * @return vertical center of finder pattern, or {@link Float#NaN} if not found - */ - fn crossCheckVertical( &self, startI:u32, centerJ:u32, maxCount:u32, - originalStateCountTotal:u32) -> f32{ - BitMatrix image = this.image; - - int maxI = image.getHeight(); - int[] stateCount = getCrossCheckStateCount(); - - // Start counting up from center - int i = startI; - while (i >= 0 && image.get(centerJ, i)) { - stateCount[2]++; - i--; - } - if (i < 0) { - return Float.NaN; - } - while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return Float.NaN; - } - while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return Float.NaN; - } - - // Now also count down from center - i = startI + 1; - while (i < maxI && image.get(centerJ, i)) { - stateCount[2]++; - i++; - } - if (i == maxI) { - return Float.NaN; - } - while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) { - stateCount[3]++; - i++; - } - if (i == maxI || stateCount[3] >= maxCount) { - return Float.NaN; - } - while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) { - stateCount[4]++; - i++; - } - if (stateCount[4] >= maxCount) { - return Float.NaN; - } - - // If we found a finder-pattern-like section, but its size is more than 40% different than - // the original, assume it's a false positive - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return Float.NaN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN; - } - - /** - *

Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, - * except it reads horizontally instead of vertically. This is used to cross-cross - * check a vertical cross check and locate the real center of the alignment pattern.

- */ - fn crossCheckHorizontal(&self, startJ:u32, centerI:u32, maxCount:u32, - originalStateCountTotal:u32) -> f32{ - BitMatrix image = this.image; - - int maxJ = image.getWidth(); - int[] stateCount = getCrossCheckStateCount(); - - int j = startJ; - while (j >= 0 && image.get(j, centerI)) { - stateCount[2]++; - j--; - } - if (j < 0) { - return Float.NaN; - } - while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) { - stateCount[1]++; - j--; - } - if (j < 0 || stateCount[1] > maxCount) { - return Float.NaN; - } - while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) { - stateCount[0]++; - j--; - } - if (stateCount[0] > maxCount) { - return Float.NaN; - } - - j = startJ + 1; - while (j < maxJ && image.get(j, centerI)) { - stateCount[2]++; - j++; - } - if (j == maxJ) { - return Float.NaN; - } - while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) { - stateCount[3]++; - j++; - } - if (j == maxJ || stateCount[3] >= maxCount) { - return Float.NaN; - } - while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) { - stateCount[4]++; - j++; - } - if (stateCount[4] >= maxCount) { - return Float.NaN; - } - - // If we found a finder-pattern-like section, but its size is significantly different than - // the original, assume it's a false positive - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { - return Float.NaN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN; - } - - /** - * @param stateCount reading state module counts from horizontal scan - * @param i row where finder pattern may be found - * @param j end of possible finder pattern in row - * @param pureBarcode ignored - * @return true if a finder pattern candidate was found this time - * @deprecated only exists for backwards compatibility - * @see #handlePossibleCenter(int[], int, int) - */ - #[deprecated] - pub fn handlePossibleCenter(&self, stateCount:&[u32], i:u32, j:u32, pureBarcode:bool) -> bool{ - return handlePossibleCenter(stateCount, i, j); - } - - /** - *

This is called when a horizontal scan finds a possible alignment pattern. It will - * cross check with a vertical scan, and if successful, will, ah, cross-cross-check - * with another horizontal scan. This is needed primarily to locate the real horizontal - * center of the pattern in cases of extreme skew. - * And then we cross-cross-cross check with another diagonal scan.

- * - *

If that succeeds the finder pattern location is added to a list that tracks - * the number of times each location has been nearly-matched as a finder pattern. - * Each additional find is more evidence that the location is in fact a finder - * pattern center - * - * @param stateCount reading state module counts from horizontal scan - * @param i row where finder pattern may be found - * @param j end of possible finder pattern in row - * @return true if a finder pattern candidate was found this time - */ - pub fn handlePossibleCenter(&self, stateCount:&[u32], i:u32, j:u32) -> bool{ - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - float centerJ = centerFromEnd(stateCount, j); - float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal); - if (!Float.isNaN(centerI)) { - // Re-cross check - centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal); - if (!Float.isNaN(centerJ) && crossCheckDiagonal((int) centerI, (int) centerJ)) { - float estimatedModuleSize = stateCountTotal / 7.0f; - boolean found = false; - for (int index = 0; index < possibleCenters.size(); index++) { - FinderPattern center = possibleCenters.get(index); - // Look for about the same center and module size: - if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) { - possibleCenters.set(index, center.combineEstimate(centerI, centerJ, estimatedModuleSize)); - found = true; - break; - } + if totalModuleSize < 7 { + return false; } - if (!found) { - FinderPattern point = new FinderPattern(centerJ, centerI, estimatedModuleSize); - possibleCenters.add(point); - if (resultPointCallback != null) { - resultPointCallback.foundPossibleRXingResultPoint(point); - } - } - return true; - } + let moduleSize = totalModuleSize as f32 / 7.0; + let maxVariance = moduleSize as f32 / 2.0; + // Allow less than 50% variance from 1-1-3-1-1 proportions + return ((moduleSize - stateCount[0] as f32).abs()) < maxVariance + && ((moduleSize - stateCount[1] as f32).abs()) < maxVariance + && ((3.0 * moduleSize - stateCount[2] as f32).abs()) < 3.0 * maxVariance + && (moduleSize - stateCount[3] as f32).abs() < maxVariance + && (moduleSize - stateCount[4] as f32).abs() < maxVariance; } - return false; - } - /** - * @return number of rows we could safely skip during scanning, based on the first - * two finder patterns that have been located. In some cases their position will - * allow us to infer that the third pattern must lie below a certain point farther - * down in the image. - */ - fn findRowSkip(&self) -> u32{ - int max = possibleCenters.size(); - if (max <= 1) { - return 0; + /** + * @param stateCount count of black/white/black/white/black pixels just read + * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios + * used by finder patterns to be considered a match + */ + pub fn foundPatternDiagonal(stateCount: &[u32]) -> bool { + let mut totalModuleSize = 0; + for i in 0..5 { + // for (int i = 0; i < 5; i++) { + let count = stateCount[i]; + if count == 0 { + return false; + } + totalModuleSize += count; + } + if (totalModuleSize < 7) { + return false; + } + let moduleSize = totalModuleSize as f32 / 7.0; + let maxVariance = moduleSize / 1.333; + // Allow less than 75% variance from 1-1-3-1-1 proportions + return (moduleSize - stateCount[0] as f32).abs() < maxVariance + && (moduleSize - stateCount[1] as f32).abs() < maxVariance + && (3.0 * moduleSize - stateCount[2] as f32).abs() < 3.0 * maxVariance + && (moduleSize - stateCount[3] as f32).abs() < maxVariance + && (moduleSize - stateCount[4] as f32).abs() < maxVariance; } - RXingResultPoint firstConfirmedCenter = null; - for (FinderPattern center : possibleCenters) { - if (center.getCount() >= CENTER_QUORUM) { - if (firstConfirmedCenter == null) { - firstConfirmedCenter = center; + + fn getCrossCheckStateCount(&mut self) -> &[u32; 5] { + FinderPatternFinder::doClearCounts(&mut self.crossCheckStateCount); + &self.crossCheckStateCount + } + + #[deprecated] + pub fn clearCounts(&self, counts: &mut [u32; 5]) { + Self::doClearCounts(counts); + } + + #[deprecated] + pub fn shiftCounts2(&self, stateCount: &mut [u32; 5]) { + Self::doShiftCounts2(stateCount); + } + + pub fn doClearCounts(counts: &mut [u32; 5]) { + counts.fill(0) + } + + pub fn doShiftCounts2(stateCount: &mut [u32]) { + stateCount[0] = stateCount[2]; + stateCount[1] = stateCount[3]; + stateCount[2] = stateCount[4]; + stateCount[3] = 1; + stateCount[4] = 0; + } + + /** + * After a vertical and horizontal scan finds a potential finder pattern, this method + * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible + * finder pattern to see if the same proportion is detected. + * + * @param centerI row where a finder pattern was detected + * @param centerJ center of the section that appears to cross a finder pattern + * @return true if proportions are withing expected limits + */ + fn crossCheckDiagonal(&mut self, centerI: u32, centerJ: u32) -> bool { + let _state_count = self.getCrossCheckStateCount(); + + // Start counting up, left from center finding black center mass + let mut i = 0; + while centerI >= i && centerJ >= i && self.image.get(centerJ - i, centerI - i) { + self.crossCheckStateCount[2] += 1; + i += 1; + } + if self.crossCheckStateCount[2] == 0 { + return false; + } + + // Continue up, left finding white space + while centerI >= i && centerJ >= i && !self.image.get(centerJ - i, centerI - i) { + self.crossCheckStateCount[1] += 1; + i += 1; + } + if self.crossCheckStateCount[1] == 0 { + return false; + } + + // Continue up, left finding black border + while centerI >= i && centerJ >= i && self.image.get(centerJ - i, centerI - i) { + self.crossCheckStateCount[0] += 1; + i += 1; + } + if self.crossCheckStateCount[0] == 0 { + return false; + } + + let maxI = self.image.getHeight(); + let maxJ = self.image.getWidth(); + + // Now also count down, right from center + i = 1; + while centerI + i < maxI && centerJ + i < maxJ && self.image.get(centerJ + i, centerI + i) { + self.crossCheckStateCount[2] += 1; + i += 1; + } + + while centerI + i < maxI && centerJ + i < maxJ && !self.image.get(centerJ + i, centerI + i) + { + self.crossCheckStateCount[3] += 1; + i += 1; + } + if self.crossCheckStateCount[3] == 0 { + return false; + } + + while centerI + i < maxI && centerJ + i < maxJ && self.image.get(centerJ + i, centerI + i) { + self.crossCheckStateCount[4] += 1; + i += 1; + } + if self.crossCheckStateCount[4] == 0 { + return false; + } + + Self::foundPatternDiagonal(&self.crossCheckStateCount) + } + + /** + *

After a horizontal scan finds a potential finder pattern, this method + * "cross-checks" by scanning down vertically through the center of the possible + * finder pattern to see if the same proportion is detected.

+ * + * @param startI row where a finder pattern was detected + * @param centerJ center of the section that appears to cross a finder pattern + * @param maxCount maximum reasonable number of modules that should be + * observed in any reading state, based on the results of the horizontal scan + * @return vertical center of finder pattern, or {@link Float#NaN} if not found + */ + fn crossCheckVertical( + &mut self, + startI: u32, + centerJ: u32, + maxCount: u32, + originalStateCountTotal: u32, + ) -> f32 { + // let image = &self.image; + + let maxI = self.image.getHeight(); + let _stateCount = self.getCrossCheckStateCount(); + + // Start counting up from center + let mut i = startI; + while i >= 0 && self.image.get(centerJ, i) { + self.crossCheckStateCount[2] += 1; + i -= 1; + } + if i < 0 { + return f32::NAN; + } + while i >= 0 && !self.image.get(centerJ, i) && self.crossCheckStateCount[1] <= maxCount { + self.crossCheckStateCount[1] += 1; + i -= 1; + } + // If already too many modules in this state or ran off the edge: + if i < 0 || self.crossCheckStateCount[1] > maxCount { + return f32::NAN; + } + while i >= 0 && self.image.get(centerJ, i) && self.crossCheckStateCount[0] <= maxCount { + self.crossCheckStateCount[0] += 1; + i -= 1; + } + if self.crossCheckStateCount[0] > maxCount { + return f32::NAN; + } + + // Now also count down from center + i = startI + 1; + while i < maxI && self.image.get(centerJ, i) { + self.crossCheckStateCount[2] += 1; + i += 1; + } + if i == maxI { + return f32::NAN; + } + while i < maxI && !self.image.get(centerJ, i) && self.crossCheckStateCount[3] < maxCount { + self.crossCheckStateCount[3] += 1; + i += 1; + } + if i == maxI || self.crossCheckStateCount[3] >= maxCount { + return f32::NAN; + } + while i < maxI && self.image.get(centerJ, i) && self.crossCheckStateCount[4] < maxCount { + self.crossCheckStateCount[4] += 1; + i += 1; + } + if self.crossCheckStateCount[4] >= maxCount { + return f32::NAN; + } + + // If we found a finder-pattern-like section, but its size is more than 40% different than + // the original, assume it's a false positive + let stateCountTotal = self.crossCheckStateCount[0] + + self.crossCheckStateCount[1] + + self.crossCheckStateCount[2] + + self.crossCheckStateCount[3] + + self.crossCheckStateCount[4]; + if 5 * (stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal { + return f32::NAN; + } + + if Self::foundPatternCross(&self.crossCheckStateCount) { + Self::centerFromEnd(&self.crossCheckStateCount, i) } else { - // We have two confirmed centers - // How far down can we skip before resuming looking for the next - // pattern? In the worst case, only the difference between the - // difference in the x / y coordinates of the two centers. - // This is the case where you find top left last. - hasSkipped = true; - return (int) (Math.abs(firstConfirmedCenter.getX() - center.getX()) - - Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2; + f32::NAN } - } - } - return 0; - } - - /** - * @return true iff we have found at least 3 finder patterns that have been detected - * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the - * candidates is "pretty similar" - */ - fn haveMultiplyConfirmedCenters(&self) -> bool { - int confirmedCount = 0; - float totalModuleSize = 0.0f; - int max = possibleCenters.size(); - for (FinderPattern pattern : possibleCenters) { - if (pattern.getCount() >= CENTER_QUORUM) { - confirmedCount++; - totalModuleSize += pattern.getEstimatedModuleSize(); - } - } - if (confirmedCount < 3) { - return false; - } - // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" - // and that we need to keep looking. We detect this by asking if the estimated module sizes - // vary too much. We arbitrarily say that when the total deviation from average exceeds - // 5% of the total module size estimates, it's too much. - float average = totalModuleSize / max; - float totalDeviation = 0.0f; - for (FinderPattern pattern : possibleCenters) { - totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average); - } - return totalDeviation <= 0.05f * totalModuleSize; - } - - /** - * Get square of distance between a and b. - */ - fn squaredDistance( a:&FinderPattern, b:&FinderPattern) -> f64{ - double x = a.getX() - b.getX(); - double y = a.getY() - b.getY(); - return x * x + y * y; - } - - /** - * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are - * those have similar module size and form a shape closer to a isosceles right triangle. - * @throws NotFoundException if 3 such finder patterns do not exist - */ - fn selectBestPatterns(&self) -> Result,Exceptions> { - - int startSize = possibleCenters.size(); - if (startSize < 3) { - // Couldn't find enough finder patterns - throw NotFoundException.getNotFoundInstance(); } - possibleCenters.sort(moduleComparator); + /** + *

Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, + * except it reads horizontally instead of vertically. This is used to cross-cross + * check a vertical cross check and locate the real center of the alignment pattern.

+ */ + fn crossCheckHorizontal( + &mut self, + startJ: u32, + centerI: u32, + maxCount: u32, + originalStateCountTotal: u32, + ) -> f32 { + // let image = &self.image; - double distortion = Double.MAX_VALUE; - FinderPattern[] bestPatterns = new FinderPattern[3]; + let maxJ = self.image.getWidth(); + let _stateCount = self.getCrossCheckStateCount(); - for (int i = 0; i < possibleCenters.size() - 2; i++) { - FinderPattern fpi = possibleCenters.get(i); - float minModuleSize = fpi.getEstimatedModuleSize(); + let mut j = startJ; + while j >= 0 && self.image.get(j, centerI) { + self.crossCheckStateCount[2] += 1; + j -= 1; + } + if j < 0 { + return f32::NAN; + } + while j >= 0 && !self.image.get(j, centerI) && self.crossCheckStateCount[1] <= maxCount { + self.crossCheckStateCount[1] += 1; + j -= 1; + } + if j < 0 || self.crossCheckStateCount[1] > maxCount { + return f32::NAN; + } + while j >= 0 && self.image.get(j, centerI) && self.crossCheckStateCount[0] <= maxCount { + self.crossCheckStateCount[0] += 1; + j -= 1; + } + if self.crossCheckStateCount[0] > maxCount { + return f32::NAN; + } - for (int j = i + 1; j < possibleCenters.size() - 1; j++) { - FinderPattern fpj = possibleCenters.get(j); - double squares0 = squaredDistance(fpi, fpj); + j = startJ + 1; + while j < maxJ && self.image.get(j, centerI) { + self.crossCheckStateCount[2] += 1; + j += 1; + } + if j == maxJ { + return f32::NAN; + } + while j < maxJ && !self.image.get(j, centerI) && self.crossCheckStateCount[3] < maxCount { + self.crossCheckStateCount[3] += 1; + j += 1; + } + if j == maxJ || self.crossCheckStateCount[3] >= maxCount { + return f32::NAN; + } + while j < maxJ && self.image.get(j, centerI) && self.crossCheckStateCount[4] < maxCount { + self.crossCheckStateCount[4] += 1; + j += 1; + } + if self.crossCheckStateCount[4] >= maxCount { + return f32::NAN; + } - for (int k = j + 1; k < possibleCenters.size(); k++) { - FinderPattern fpk = possibleCenters.get(k); - float maxModuleSize = fpk.getEstimatedModuleSize(); - if (maxModuleSize > minModuleSize * 1.4f) { - // module size is not similar - continue; - } + // If we found a finder-pattern-like section, but its size is significantly different than + // the original, assume it's a false positive + let stateCountTotal = self.crossCheckStateCount[0] + + self.crossCheckStateCount[1] + + self.crossCheckStateCount[2] + + self.crossCheckStateCount[3] + + self.crossCheckStateCount[4]; + if 5 * (stateCountTotal - originalStateCountTotal) >= originalStateCountTotal { + return f32::NAN; + } - double a = squares0; - double b = squaredDistance(fpj, fpk); - double c = squaredDistance(fpi, fpk); + if Self::foundPatternCross(&self.crossCheckStateCount) { + Self::centerFromEnd(&self.crossCheckStateCount, j) + } else { + f32::NAN + } + } - // sorts ascending - inlined - if (a < b) { - if (b > c) { - if (a < c) { - double temp = b; - b = c; - c = temp; - } else { - double temp = a; - a = c; - c = b; - b = temp; - } + /** + * @param stateCount reading state module counts from horizontal scan + * @param i row where finder pattern may be found + * @param j end of possible finder pattern in row + * @param pureBarcode ignored + * @return true if a finder pattern candidate was found this time + * @deprecated only exists for backwards compatibility + * @see #handlePossibleCenter(int[], int, int) + */ + #[deprecated] + pub fn handlePossibleCenterWithPureBarcodeFlag( + &mut self, + stateCount: &[u32], + i: u32, + j: u32, + _pureBarcode: bool, + ) -> bool { + self.handlePossibleCenter(stateCount, i, j) + } + + /** + *

This is called when a horizontal scan finds a possible alignment pattern. It will + * cross check with a vertical scan, and if successful, will, ah, cross-cross-check + * with another horizontal scan. This is needed primarily to locate the real horizontal + * center of the pattern in cases of extreme skew. + * And then we cross-cross-cross check with another diagonal scan.

+ * + *

If that succeeds the finder pattern location is added to a list that tracks + * the number of times each location has been nearly-matched as a finder pattern. + * Each additional find is more evidence that the location is in fact a finder + * pattern center + * + * @param stateCount reading state module counts from horizontal scan + * @param i row where finder pattern may be found + * @param j end of possible finder pattern in row + * @return true if a finder pattern candidate was found this time + */ + pub fn handlePossibleCenter(&mut self, stateCount: &[u32], i: u32, j: u32) -> bool { + let stateCountTotal = + stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + let mut centerJ = Self::centerFromEnd(stateCount, j); + let centerI = self.crossCheckVertical(i, centerJ as u32, stateCount[2], stateCountTotal); + if !centerI.is_nan() { + // Re-cross check + centerJ = self.crossCheckHorizontal( + centerJ as u32, + centerI as u32, + stateCount[2], + stateCountTotal, + ); + if !centerJ.is_nan() && self.crossCheckDiagonal(centerI as u32, centerJ as u32) { + let estimatedModuleSize = stateCountTotal as f32 / 7.0; + let mut found = false; + for index in 0..self.possibleCenters.len() { + // for (int index = 0; index < possibleCenters.size(); index++) { + let center = self.possibleCenters.get(index).unwrap(); + // Look for about the same center and module size: + if center.aboutEquals(estimatedModuleSize, centerI, centerJ) { + self.possibleCenters[index] = + center.combineEstimate(centerI, centerJ, estimatedModuleSize); + found = true; + break; + } + } + if !found { + let point = FinderPattern::new(centerJ, centerI, estimatedModuleSize); + self.possibleCenters.push(point); + if self.resultPointCallback.is_some() { + self.resultPointCallback.as_ref().unwrap()(&point); + } + } + return true; } - } else { - if (b < c) { - if (a < c) { - double temp = a; - a = b; - b = temp; - } else { - double temp = a; - a = b; - b = c; - c = temp; - } + } + return false; + } + + /** + * @return number of rows we could safely skip during scanning, based on the first + * two finder patterns that have been located. In some cases their position will + * allow us to infer that the third pattern must lie below a certain point farther + * down in the image. + */ + fn findRowSkip(&mut self) -> u32 { + let max = self.possibleCenters.len(); + if max <= 1 { + return 0; + } + let mut firstConfirmedCenter = None; + for center in &self.possibleCenters { + // for (FinderPattern center : possibleCenters) { + if center.getCount() >= Self::CENTER_QUORUM { + if firstConfirmedCenter.is_none() { + firstConfirmedCenter = Some(center); + } else { + // We have two confirmed centers + // How far down can we skip before resuming looking for the next + // pattern? In the worst case, only the difference between the + // difference in the x / y coordinates of the two centers. + // This is the case where you find top left last. + self.hasSkipped = true; + let fnp = firstConfirmedCenter.unwrap(); + return (((fnp.getX() - center.getX().abs()) + - (fnp.getY() - center.getY()).abs()) + / 2.0) + .floor() as u32; + } + } + } + return 0; + } + + /** + * @return true iff we have found at least 3 finder patterns that have been detected + * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the + * candidates is "pretty similar" + */ + fn haveMultiplyConfirmedCenters(&self) -> bool { + let mut confirmedCount = 0; + let mut totalModuleSize = 0.0; + let max = self.possibleCenters.len(); + for pattern in &self.possibleCenters { + // for (FinderPattern pattern : possibleCenters) { + if pattern.getCount() >= Self::CENTER_QUORUM { + confirmedCount += 1; + totalModuleSize += pattern.getEstimatedModuleSize(); + } + } + if confirmedCount < 3 { + return false; + } + // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" + // and that we need to keep looking. We detect this by asking if the estimated module sizes + // vary too much. We arbitrarily say that when the total deviation from average exceeds + // 5% of the total module size estimates, it's too much. + let average = totalModuleSize / max as f32; + let mut totalDeviation = 0.0; + for pattern in &self.possibleCenters { + // for (FinderPattern pattern : possibleCenters) { + totalDeviation += (pattern.getEstimatedModuleSize() - average).abs(); + } + return totalDeviation <= 0.05 * totalModuleSize; + } + + /** + * Get square of distance between a and b. + */ + fn squaredDistance(a: &FinderPattern, b: &FinderPattern) -> f64 { + let x = a.getX() as f64 - b.getX() as f64; + let y = a.getY() as f64 - b.getY() as f64; + + x * x + y * y + } + + /** + * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are + * those have similar module size and form a shape closer to a isosceles right triangle. + * @throws NotFoundException if 3 such finder patterns do not exist + */ + fn selectBestPatterns(&mut self) -> Result<[FinderPattern; 3], Exceptions> { + let startSize = self.possibleCenters.len(); + if startSize < 3 { + // Couldn't find enough finder patterns + return Err(Exceptions::NotFoundException("".to_owned())); + } + + self.possibleCenters.sort_by(|x, y| { + x.getEstimatedModuleSize() + .partial_cmp(&y.getEstimatedModuleSize()) + .unwrap() + // Float.compare(center1.getEstimatedModuleSize(), center2.getEstimatedModuleSize()); + }); + + // self.possibleCenters.sort(self.moduleComparator); + + let mut distortion = f64::MAX; + let mut bestPatterns = [None; 3]; + + for i in 0..self.possibleCenters.len() { + // for (int i = 0; i < possibleCenters.size() - 2; i++) { + let fpi = if let Some(f) = self.possibleCenters.get(i) { + f } else { - double temp = a; - a = c; - c = temp; + return Err(Exceptions::NotFoundException("".to_owned())); + }; + let minModuleSize = fpi.getEstimatedModuleSize(); + + for j in (i + 1)..(self.possibleCenters.len() - 1) { + // for (int j = i + 1; j < possibleCenters.size() - 1; j++) { + let fpj = if let Some(f) = self.possibleCenters.get(j) { + f + } else { + return Err(Exceptions::NotFoundException("".to_owned())); + }; + let squares0 = Self::squaredDistance(fpi, fpj); + + for k in (j + 1)..(self.possibleCenters.len()) { + // for (int k = j + 1; k < possibleCenters.size(); k++) { + let fpk = if let Some(f) = self.possibleCenters.get(k) { + f + } else { + return Err(Exceptions::NotFoundException("".to_owned())); + }; + let maxModuleSize = fpk.getEstimatedModuleSize(); + if maxModuleSize > minModuleSize * 1.4 { + // module size is not similar + continue; + } + + let mut a = squares0; + let mut b = Self::squaredDistance(fpj, fpk); + let mut c = Self::squaredDistance(fpi, fpk); + + // sorts ascending - inlined + if a < b { + if b > c { + if a < c { + let temp = b; + b = c; + c = temp; + } else { + let temp = a; + a = c; + c = b; + b = temp; + } + } + } else { + if b < c { + if a < c { + let temp = a; + a = b; + b = temp; + } else { + let temp = a; + a = b; + b = c; + c = temp; + } + } else { + let temp = a; + a = c; + c = temp; + } + } + + // a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle). + // Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0, + // we need to check both two equal sides separately. + // The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity + // from isosceles right triangle. + let d = (c - 2.0 * b).abs() + (c - 2.0 * a).abs(); + if d < distortion { + distortion = d; + bestPatterns = [Some(*fpi), Some(*fpj), Some(*fpk)]; + // bestPatterns[0] = *fpi; + // bestPatterns[1] = *fpj; + // bestPatterns[2] = *fpk; + } + } } - } - - // a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle). - // Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0, - // we need to check both two equal sides separately. - // The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity - // from isosceles right triangle. - double d = Math.abs(c - 2 * b) + Math.abs(c - 2 * a); - if (d < distortion) { - distortion = d; - bestPatterns[0] = fpi; - bestPatterns[1] = fpj; - bestPatterns[2] = fpk; - } } - } + + if distortion == f64::MAX { + return Err(Exceptions::NotFoundException("".to_owned())); + } + + if bestPatterns[0].is_none() { + return Err(Exceptions::NotFoundException("".to_owned())); + } + + let p1 = bestPatterns[0].unwrap(); + let p2 = bestPatterns[1].unwrap(); + let p3 = bestPatterns[2].unwrap(); + + Ok([p1, p2, p3]) } - - if (distortion == Double.MAX_VALUE) { - throw NotFoundException.getNotFoundInstance(); - } - - return bestPatterns; - } - } // /** @@ -706,4 +801,4 @@ impl FinderPatternFinder{ // public int compare(FinderPattern center1, FinderPattern center2) { // return Float.compare(center1.getEstimatedModuleSize(), center2.getEstimatedModuleSize()); // } -// } \ No newline at end of file +// }