Add QRCode lenient diagonal check (#906)

* Add QRCode lenient diagonal check

Adding diagonal check to solve #897 to prevent false positives for centers

* update tests to expect the increased number of successes
This commit is contained in:
Aldracor 2017-11-08 12:11:25 +02:00 committed by Sean Owen
parent ce98de83bd
commit 72a21d158b
4 changed files with 104 additions and 4 deletions

View file

@ -218,6 +218,34 @@ public class FinderPatternFinder {
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
*/
protected static boolean foundPatternDiagonal(int[] stateCount) {
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;
}
private int[] getCrossCheckStateCount() {
crossCheckStateCount[0] = 0;
crossCheckStateCount[1] = 0;
@ -227,6 +255,78 @@ 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 centerI 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 centerI, int centerJ) {
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);
}
/**
* <p>After a horizontal scan finds a potential finder pattern, this method
* "cross-checks" by scanning down vertically through the center of the possible
@ -408,7 +508,7 @@ 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) && crossCheckDiagonal((int) centerI, (int) centerJ)) {
float estimatedModuleSize = stateCountTotal / 7.0f;
boolean found = false;
for (int index = 0; index < possibleCenters.size(); index++) {

View file

@ -30,7 +30,7 @@ public final class QRCodeBlackBox2TestCase extends AbstractBlackBoxTestCase {
addTest(31, 31, 0.0f);
addTest(29, 29, 90.0f);
addTest(30, 30, 180.0f);
addTest(29, 29, 270.0f);
addTest(30, 30, 270.0f);
}
}

View file

@ -28,7 +28,7 @@ public final class QRCodeBlackBox3TestCase extends AbstractBlackBoxTestCase {
public QRCodeBlackBox3TestCase() {
super("src/test/resources/blackbox/qrcode-3", new MultiFormatReader(), BarcodeFormat.QR_CODE);
addTest(38, 38, 0.0f);
addTest(38, 38, 90.0f);
addTest(39, 39, 90.0f);
addTest(36, 36, 180.0f);
addTest(39, 39, 270.0f);
}

View file

@ -34,7 +34,7 @@ public final class QRCodeBlackBox5TestCase extends AbstractBlackBoxTestCase {
addTest(19, 19, 0.0f);
addTest(19, 19, 90.0f);
addTest(19, 19, 180.0f);
addTest(18, 18, 270.0f);
addTest(19, 19, 270.0f);
}
}