/* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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.
* *This class is thread-safe but not reentrant. Each thread must allocate its own object.
*
* @author Sean Owen
*/
pub struct FinderPatternFinder {
image: BitMatrix,
possibleCenters: Vec Creates a finder that will search the image for three finder patterns. 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. 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. 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;
}
}
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)
.round() 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 {
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;
}
}
}
}
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])
}
}
// /**
// * Orders by {@link FinderPattern#getEstimatedModuleSize()}