Try a diagonal cross check in PURE_BARCODE mode for QR codes to avoid false positives. HT fics.danmark

This commit is contained in:
Sean Owen 2014-01-23 20:27:30 +00:00
parent a8772054af
commit be038c7eef
3 changed files with 98 additions and 7 deletions

View file

@ -37,6 +37,7 @@ Emanuele Aina
Eric Kobrin (Velocitude)
evansepdx
Erik Barbara
fics.danmark
Francois B. (Google)
Frank Yellin
Fred Lin (Anobiit)

View file

@ -230,6 +230,7 @@ final class MultiFinderPatternFinder extends FinderPatternFinder {
public FinderPatternInfo[] findMulti(Map<DecodeHintType,?> hints) throws NotFoundException {
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
boolean pureBarcode = hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE);
BitMatrix image = getImage();
int maxI = image.getHeight();
int maxJ = image.getWidth();
@ -264,7 +265,7 @@ final class MultiFinderPatternFinder extends FinderPatternFinder {
} else { // White pixel
if ((currentState & 1) == 0) { // Counting black pixels
if (currentState == 4) { // A winner?
if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j)) { // Yes
if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j, pureBarcode)) { // Yes
// Clear state to start looking again
currentState = 0;
stateCount[0] = 0;
@ -290,7 +291,7 @@ final class MultiFinderPatternFinder extends FinderPatternFinder {
} // for j=...
if (foundPatternCross(stateCount)) {
handlePossibleCenter(stateCount, i, maxJ);
handlePossibleCenter(stateCount, i, maxJ, pureBarcode);
} // end if foundPatternCross
} // for i=iSkip-1 ...
FinderPattern[][] patternInfo = selectMutipleBestPatterns();

View file

@ -76,6 +76,7 @@ public class FinderPatternFinder {
final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
boolean pureBarcode = hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE);
int maxI = image.getHeight();
int maxJ = image.getWidth();
// We are looking for black/white/black/white/black modules in
@ -111,7 +112,7 @@ public class FinderPatternFinder {
if ((currentState & 1) == 0) { // Counting black pixels
if (currentState == 4) { // A winner?
if (foundPatternCross(stateCount)) { // Yes
boolean confirmed = handlePossibleCenter(stateCount, i, j);
boolean confirmed = handlePossibleCenter(stateCount, i, j, pureBarcode);
if (confirmed) {
// Start examining every other line. Checking each line turned out to be too
// expensive and didn't improve performance.
@ -166,7 +167,7 @@ public class FinderPatternFinder {
}
}
if (foundPatternCross(stateCount)) {
boolean confirmed = handlePossibleCenter(stateCount, i, maxJ);
boolean confirmed = handlePossibleCenter(stateCount, i, maxJ, pureBarcode);
if (confirmed) {
iSkip = stateCount[0];
if (hasSkipped) {
@ -227,6 +228,92 @@ public class FinderPatternFinder {
return crossCheckStateCount;
}
/**
* 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 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
* @param originalStateCountTotal The original state count total.
* @return true if proportions are withing expected limits
*/
private boolean crossCheckDiagonal(int startI, int centerJ, int maxCount, int originalStateCountTotal) {
int maxI = image.getHeight();
int maxJ = image.getWidth();
int[] stateCount = getCrossCheckStateCount();
// Start counting up, left from center finding black center mass
int i = 0;
while (startI - i >= 0 && image.get(centerJ - i, startI - i)) {
stateCount[2]++;
i++;
}
if ((startI - i < 0) || (centerJ - i < 0)) {
return false;
}
// Continue up, left finding white space
while ((startI - i >= 0) && (centerJ - i >= 0) && !image.get(centerJ - i, startI - i) && stateCount[1] <= maxCount) {
stateCount[1]++;
i++;
}
// If already too many modules in this state or ran off the edge:
if ((startI - i < 0) || (centerJ - i < 0) || stateCount[1] > maxCount) {
return false;
}
// Continue up, left finding black border
while ((startI - i >= 0) && (centerJ - i >= 0) && image.get(centerJ - i, startI - i) && stateCount[0] <= maxCount) {
stateCount[0]++;
i++;
}
if (stateCount[0] > maxCount) {
return false;
}
// Now also count down, right from center
i = 1;
while ((startI + i < maxI) && (centerJ + i < maxJ) && image.get(centerJ + i, startI + i)) {
stateCount[2]++;
i++;
}
// Ran off the edge?
if ((startI + i >= maxI) || (centerJ + i >= maxJ)) {
return false;
}
while ((startI + i < maxI) && (centerJ + i < maxJ) && !image.get(centerJ + i, startI + i) && stateCount[3] < maxCount) {
stateCount[3]++;
i++;
}
if ((startI + i >= maxI) || (centerJ + i >= maxJ) || stateCount[3] >= maxCount) {
return false;
}
while ((startI + i < maxI) && (centerJ + i < maxJ) && image.get(centerJ + i, startI + i) && stateCount[4] < maxCount) {
stateCount[4]++;
i++;
}
if (stateCount[4] >= maxCount) {
return false;
}
// If we found a finder-pattern-like section, but its size is more than 100% different than
// the original, assume it's a false positive
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
return
Math.abs(stateCountTotal - originalStateCountTotal) < 2 * originalStateCountTotal &&
foundPatternCross(stateCount);
}
/**
* <p>After a horizontal scan finds a potential finder pattern, this method
* "cross-checks" by scanning down vertically through the center of the possible
@ -378,7 +465,8 @@ public class FinderPatternFinder {
* <p>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.</p>
* center of the pattern in cases of extreme skew.
* And then we cross-cross-cross check with another diagonal scan.</p>
*
* <p>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.
@ -390,7 +478,7 @@ public class FinderPatternFinder {
* @param j end of possible finder pattern in row
* @return true if a finder pattern candidate was found this time
*/
protected final boolean handlePossibleCenter(int[] stateCount, int i, int j) {
protected final boolean handlePossibleCenter(int[] stateCount, int i, int j, boolean pureBarcode) {
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
stateCount[4];
float centerJ = centerFromEnd(stateCount, j);
@ -398,7 +486,8 @@ public class FinderPatternFinder {
if (!Float.isNaN(centerI)) {
// Re-cross check
centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal);
if (!Float.isNaN(centerJ)) {
if (!Float.isNaN(centerJ) &&
(!pureBarcode || crossCheckDiagonal((int) centerI, (int) centerJ, stateCount[2], stateCountTotal))) {
float estimatedModuleSize = (float) stateCountTotal / 7.0f;
boolean found = false;
for (int index = 0; index < possibleCenters.size(); index++) {