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
for (int index = 0, downIndex = size, end = size * size; index < end; ) {
Arrays.fill(runHistory, 0);
int runColor = 0;
int runX = 0;
Arrays.fill(runHistory, 0);
int padRun = size; // Add white border to initial run
int curRow = 0;
int nextRow = 0;
for (int x = 0; x < size; x++, index++, downIndex++) {
@ -495,9 +496,10 @@ public final class QrCode {
else if (runX > 5)
result++;
} else {
addRunToHistory(runX, runHistory);
if (runColor == 0 && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
finderPenaltyAddHistory(runX + padRun, runHistory);
padRun = 0;
if (runColor == 0)
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
runColor = c;
runX = 1;
}
@ -510,18 +512,15 @@ public final class QrCode {
result += PENALTY_N2;
}
}
addRunToHistory(runX, runHistory);
if (runColor == 1)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3;
}
// Iterate over single columns
for (int x = 0; x < size; x++) {
Arrays.fill(runHistory, 0);
int runColor = 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) {
int c = getBit(modules[index >>> 5], index);
if (c == runColor) {
@ -531,18 +530,15 @@ public final class QrCode {
else if (runY > 5)
result++;
} else {
addRunToHistory(runY, runHistory);
if (runColor == 0 && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
finderPenaltyAddHistory(runY + padRun, runHistory);
padRun = 0;
if (runColor == 0)
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
runColor = c;
runY = 1;
}
}
addRunToHistory(runY, runHistory);
if (runColor == 1)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3;
}
// 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
// existing values and deletes the last value. A helper function for getPenaltyScore().
private static void addRunToHistory(int run, int[] history) {
System.arraycopy(history, 0, history, 1, history.length - 1);
history[0] = run;
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
private int finderPenaltyCountPatterns(int[] runHistory) {
int n = runHistory[1];
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
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
// Must only be called immediately after a run of white modules has ended.
private static boolean hasFinderLikePattern(int[] runHistory) {
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;
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
private static void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) {
System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1);
runHistory[0] = currentRunLength;
}

Loading…
Cancel
Save