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) Eric Kobrin (Velocitude)
evansepdx evansepdx
Erik Barbara Erik Barbara
fics.danmark
Francois B. (Google) Francois B. (Google)
Frank Yellin Frank Yellin
Fred Lin (Anobiit) Fred Lin (Anobiit)

View file

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

View file

@ -76,6 +76,7 @@ public class FinderPatternFinder {
final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException { final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
boolean pureBarcode = hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE);
int maxI = image.getHeight(); int maxI = image.getHeight();
int maxJ = image.getWidth(); int maxJ = image.getWidth();
// We are looking for black/white/black/white/black modules in // 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 & 1) == 0) { // Counting black pixels
if (currentState == 4) { // A winner? if (currentState == 4) { // A winner?
if (foundPatternCross(stateCount)) { // Yes if (foundPatternCross(stateCount)) { // Yes
boolean confirmed = handlePossibleCenter(stateCount, i, j); boolean confirmed = handlePossibleCenter(stateCount, i, j, pureBarcode);
if (confirmed) { if (confirmed) {
// Start examining every other line. Checking each line turned out to be too // Start examining every other line. Checking each line turned out to be too
// expensive and didn't improve performance. // expensive and didn't improve performance.
@ -166,7 +167,7 @@ public class FinderPatternFinder {
} }
} }
if (foundPatternCross(stateCount)) { if (foundPatternCross(stateCount)) {
boolean confirmed = handlePossibleCenter(stateCount, i, maxJ); boolean confirmed = handlePossibleCenter(stateCount, i, maxJ, pureBarcode);
if (confirmed) { if (confirmed) {
iSkip = stateCount[0]; iSkip = stateCount[0];
if (hasSkipped) { if (hasSkipped) {
@ -227,6 +228,92 @@ public class FinderPatternFinder {
return crossCheckStateCount; 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 * <p>After a horizontal scan finds a potential finder pattern, this method
* "cross-checks" by scanning down vertically through the center of the possible * "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 * <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 * 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 * 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 * <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. * 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 * @param j end of possible finder pattern in row
* @return true if a finder pattern candidate was found this time * @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] + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
stateCount[4]; stateCount[4];
float centerJ = centerFromEnd(stateCount, j); float centerJ = centerFromEnd(stateCount, j);
@ -398,7 +486,8 @@ public class FinderPatternFinder {
if (!Float.isNaN(centerI)) { if (!Float.isNaN(centerI)) {
// Re-cross check // Re-cross check
centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal); 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; float estimatedModuleSize = (float) stateCountTotal / 7.0f;
boolean found = false; boolean found = false;
for (int index = 0; index < possibleCenters.size(); index++) { for (int index = 0; index < possibleCenters.size(); index++) {