Replaced the finder-like pattern detection algorithm with a more sophisticated and accurate one, synchronizing with the parent project.

pull/134/head
Project Nayuki 5 years ago
parent 42c357ae1c
commit f05a8f9098

@ -481,9 +481,10 @@ public final class QrCode {
// Iterate over adjacent pairs of rows // Iterate over adjacent pairs of rows
for (int index = 0, downIndex = size, end = size * size; index < end; ) { for (int index = 0, downIndex = size, end = size * size; index < end; ) {
Arrays.fill(runHistory, 0);
int runColor = 0; int runColor = 0;
int runX = 0; int runX = 0;
Arrays.fill(runHistory, 0);
int padRun = size; // Add white border to initial run
int curRow = 0; int curRow = 0;
int nextRow = 0; int nextRow = 0;
for (int x = 0; x < size; x++, index++, downIndex++) { for (int x = 0; x < size; x++, index++, downIndex++) {
@ -495,9 +496,10 @@ public final class QrCode {
else if (runX > 5) else if (runX > 5)
result++; result++;
} else { } else {
addRunToHistory(runX, runHistory); finderPenaltyAddHistory(runX + padRun, runHistory);
if (runColor == 0 && hasFinderLikePattern(runHistory)) padRun = 0;
result += PENALTY_N3; if (runColor == 0)
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
runColor = c; runColor = c;
runX = 1; runX = 1;
} }
@ -510,18 +512,15 @@ public final class QrCode {
result += PENALTY_N2; result += PENALTY_N2;
} }
} }
addRunToHistory(runX, runHistory); result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3;
if (runColor == 1)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
} }
// Iterate over single columns // Iterate over single columns
for (int x = 0; x < size; x++) { for (int x = 0; x < size; x++) {
Arrays.fill(runHistory, 0);
int runColor = 0; int runColor = 0;
int runY = 0; int runY = 0;
Arrays.fill(runHistory, 0);
int padRun = size; // Add white border to initial run
for (int y = 0, index = x; y < size; y++, index += size) { for (int y = 0, index = x; y < size; y++, index += size) {
int c = getBit(modules[index >>> 5], index); int c = getBit(modules[index >>> 5], index);
if (c == runColor) { if (c == runColor) {
@ -531,18 +530,15 @@ public final class QrCode {
else if (runY > 5) else if (runY > 5)
result++; result++;
} else { } else {
addRunToHistory(runY, runHistory); finderPenaltyAddHistory(runY + padRun, runHistory);
if (runColor == 0 && hasFinderLikePattern(runHistory)) padRun = 0;
result += PENALTY_N3; if (runColor == 0)
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
runColor = c; runColor = c;
runY = 1; runY = 1;
} }
} }
addRunToHistory(runY, runHistory); result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3;
if (runColor == 1)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
} }
// Balance of black and white modules // Balance of black and white modules
@ -567,21 +563,33 @@ public final class QrCode {
} }
// Inserts the given value to the front of the given array, which shifts over the // Can only be called immediately after a white run is added, and
// existing values and deletes the last value. A helper function for getPenaltyScore(). // returns either 0, 1, or 2. A helper function for getPenaltyScore().
private static void addRunToHistory(int run, int[] history) { private int finderPenaltyCountPatterns(int[] runHistory) {
System.arraycopy(history, 0, history, 1, history.length - 1); int n = runHistory[1];
history[0] = run; assert n <= size * 3;
boolean core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;
return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
+ (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);
}
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
private int finderPenaltyTerminateAndCount(int currentRunColor, int currentRunLength, int[] runHistory) {
if (currentRunColor == 1) { // Terminate black run
finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += size; // Add white border to final run
finderPenaltyAddHistory(currentRunLength, runHistory);
return finderPenaltyCountPatterns(runHistory);
} }
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). private static void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) {
// Must only be called immediately after a run of white modules has ended. System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1);
private static boolean hasFinderLikePattern(int[] runHistory) { runHistory[0] = currentRunLength;
int n = runHistory[1];
return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n
&& runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4;
} }

Loading…
Cancel
Save