Skip to content

Commit

Permalink
Replace FP math faked in integers with plain floating-point math
Browse files Browse the repository at this point in the history
  • Loading branch information
srowen committed Apr 25, 2014
1 parent 3b8b448 commit 408c384
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 86 deletions.
15 changes: 7 additions & 8 deletions core/src/main/java/com/google/zxing/oned/CodaBarReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public final class CodaBarReader extends OneDReader {
// These values are critical for determining how permissive the decoding
// will be. All stripe sizes must be within the window these define, as
// compared to the average stripe size.
private static final int MAX_ACCEPTABLE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 2.0f);
private static final int PADDING = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 1.5f);
private static final float MAX_ACCEPTABLE = 2.0f;
private static final float PADDING = 1.5f;

private static final String ALPHABET_STRING = "0123456789-$:/.+ABCD";
static final char[] ALPHABET = ALPHABET_STRING.toCharArray();
Expand Down Expand Up @@ -188,15 +188,14 @@ void validatePattern(int start) throws NotFoundException {
}

// Calculate our allowable size thresholds using fixed-point math.
int[] maxes = new int[4];
int[] mins = new int[4];
float[] maxes = new float[4];
float[] mins = new float[4];
// Define the threshold of acceptability to be the midpoint between the
// average small stripe and the average large stripe. No stripe lengths
// should be on the "wrong" side of that line.
for (int i = 0; i < 2; i++) {
mins[i] = 0; // Accept arbitrarily small "short" stripes.
mins[i + 2] = ((sizes[i] << INTEGER_MATH_SHIFT) / counts[i] +
(sizes[i + 2] << INTEGER_MATH_SHIFT) / counts[i + 2]) >> 1;
mins[i] = 0.0f; // Accept arbitrarily small "short" stripes.
mins[i + 2] = ((float) sizes[i] / counts[i] + (float) sizes[i + 2] / counts[i + 2]) / 2.0f;
maxes[i] = mins[i + 2];
maxes[i + 2] = (sizes[i + 2] * MAX_ACCEPTABLE + PADDING) / counts[i + 2];
}
Expand All @@ -209,7 +208,7 @@ void validatePattern(int start) throws NotFoundException {
// Even j = bars, while odd j = spaces. Categories 2 and 3 are for
// long stripes, while 0 and 1 are for short stripes.
int category = (j & 1) + (pattern & 1) * 2;
int size = counters[pos + j] << INTEGER_MATH_SHIFT;
int size = counters[pos + j];
if (size < mins[category] || size > maxes[category]) {
throw NotFoundException.getNotFoundInstance();
}
Expand Down
12 changes: 6 additions & 6 deletions core/src/main/java/com/google/zxing/oned/Code128Reader.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ public final class Code128Reader extends OneDReader {
{2, 3, 3, 1, 1, 1, 2}
};

private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
private static final float MAX_AVG_VARIANCE = 0.25f;
private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f;

private static final int CODE_SHIFT = 98;

Expand Down Expand Up @@ -181,10 +181,10 @@ private static int[] findStartPattern(BitArray row) throws NotFoundException {
counters[counterPosition]++;
} else {
if (counterPosition == patternLength - 1) {
int bestVariance = MAX_AVG_VARIANCE;
float bestVariance = MAX_AVG_VARIANCE;
int bestMatch = -1;
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode],
float variance = patternMatchVariance(counters, CODE_PATTERNS[startCode],
MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
Expand Down Expand Up @@ -214,11 +214,11 @@ private static int[] findStartPattern(BitArray row) throws NotFoundException {
private static int decodeCode(BitArray row, int[] counters, int rowOffset)
throws NotFoundException {
recordPattern(row, rowOffset, counters);
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
int bestMatch = -1;
for (int d = 0; d < CODE_PATTERNS.length; d++) {
int[] pattern = CODE_PATTERNS[d];
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = d;
Expand Down
12 changes: 4 additions & 8 deletions core/src/main/java/com/google/zxing/oned/Code93Reader.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,20 +170,16 @@ private static int toPattern(int[] counters) {
}
int pattern = 0;
for (int i = 0; i < max; i++) {
int scaledShifted = (counters[i] << INTEGER_MATH_SHIFT) * 9 / sum;
int scaledUnshifted = scaledShifted >> INTEGER_MATH_SHIFT;
if ((scaledShifted & 0xFF) > 0x7F) {
scaledUnshifted++;
}
if (scaledUnshifted < 1 || scaledUnshifted > 4) {
int scaled = Math.round(counters[i] * 9.0f / sum);
if (scaled < 1 || scaled > 4) {
return -1;
}
if ((i & 0x01) == 0) {
for (int j = 0; j < scaledUnshifted; j++) {
for (int j = 0; j < scaled; j++) {
pattern = (pattern << 1) | 0x01;
}
} else {
pattern <<= scaledUnshifted;
pattern <<= scaled;
}
}
return pattern;
Expand Down
9 changes: 4 additions & 5 deletions core/src/main/java/com/google/zxing/oned/ITFReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
*/
public final class ITFReader extends OneDReader {

private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.78f);
private static final float MAX_AVG_VARIANCE = 0.38f;
private static final float MAX_INDIVIDUAL_VARIANCE = 0.78f;

private static final int W = 3; // Pixel width of a wide line
private static final int N = 1; // Pixed width of a narrow line
Expand Down Expand Up @@ -336,13 +336,12 @@ private static int[] findGuardPattern(BitArray row,
* @throws NotFoundException if digit cannot be decoded
*/
private static int decodeDigit(int[] counters) throws NotFoundException {

int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
int bestMatch = -1;
int max = PATTERNS.length;
for (int i = 0; i < max; i++) {
int[] pattern = PATTERNS[i];
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = i;
Expand Down
34 changes: 13 additions & 21 deletions core/src/main/java/com/google/zxing/oned/OneDReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@
*/
public abstract class OneDReader implements Reader {

protected static final int INTEGER_MATH_SHIFT = 8;
protected static final int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;

@Override
public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {
return decode(image, null);
Expand Down Expand Up @@ -248,14 +245,11 @@ protected static void recordPatternInReverse(BitArray row, int start, int[] coun
* @param counters observed counters
* @param pattern expected pattern
* @param maxIndividualVariance The most any counter can differ before we give up
* @return ratio of total variance between counters and pattern compared to total pattern size,
* where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means
* the total variance between counters and patterns equals the pattern length, higher values mean
* even more variance
* @return ratio of total variance between counters and pattern compared to total pattern size
*/
protected static int patternMatchVariance(int[] counters,
int[] pattern,
int maxIndividualVariance) {
protected static float patternMatchVariance(int[] counters,
int[] pattern,
float maxIndividualVariance) {
int numCounters = counters.length;
int total = 0;
int patternLength = 0;
Expand All @@ -266,21 +260,19 @@ protected static int patternMatchVariance(int[] counters,
if (total < patternLength) {
// If we don't even have one pixel per unit of bar width, assume this is too small
// to reliably match, so fail:
return Integer.MAX_VALUE;
return Float.POSITIVE_INFINITY;
}
// We're going to fake floating-point math in integers. We just need to use more bits.
// Scale up patternLength so that intermediate values below like scaledCounter will have
// more "significant digits"
int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;

int totalVariance = 0;
float unitBarWidth = (float) total / patternLength;
maxIndividualVariance *= unitBarWidth;

float totalVariance = 0.0f;
for (int x = 0; x < numCounters; x++) {
int counter = counters[x] << INTEGER_MATH_SHIFT;
int scaledPattern = pattern[x] * unitBarWidth;
int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
int counter = counters[x];
float scaledPattern = pattern[x] * unitBarWidth;
float variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
if (variance > maxIndividualVariance) {
return Integer.MAX_VALUE;
return Float.POSITIVE_INFINITY;
}
totalVariance += variance;
}
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/java/com/google/zxing/oned/UPCEANReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public abstract class UPCEANReader extends OneDReader {
// These two values are critical for determining how permissive the decoding will be.
// We've arrived at these values through a lot of trial and error. Setting them any higher
// lets false positives creep in quickly.
private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.48f);
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
private static final float MAX_AVG_VARIANCE = 0.48f;
private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f;

/**
* Start/end guard pattern.
Expand Down Expand Up @@ -353,12 +353,12 @@ private static int[] findGuardPattern(BitArray row,
static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns)
throws NotFoundException {
recordPattern(row, rowOffset, counters);
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
int bestMatch = -1;
int max = patterns.length;
for (int i = 0; i < max; i++) {
int[] pattern = patterns[i];
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = i;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

public abstract class AbstractRSSReader extends OneDReader {

private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.2f);
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.45f);
private static final float MAX_AVG_VARIANCE = 0.2f;
private static final float MAX_INDIVIDUAL_VARIANCE = 0.45f;

private static final float MIN_FINDER_PATTERN_RATIO = 9.5f / 12.0f;
private static final float MAX_FINDER_PATTERN_RATIO = 12.5f / 14.0f;
Expand Down
30 changes: 12 additions & 18 deletions core/src/main/java/com/google/zxing/pdf417/detector/Detector.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ public final class Detector {

private static final int[] INDEXES_START_PATTERN = {0, 4, 1, 5};
private static final int[] INDEXES_STOP_PATTERN = {6, 2, 7, 3};
private static final int INTEGER_MATH_SHIFT = 8;
private static final int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;
private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f);
private static final float MAX_AVG_VARIANCE = 0.42f;
private static final float MAX_INDIVIDUAL_VARIANCE = 0.8f;

// B S B S B S B S Bar/Space pattern
// 11111111 0 1 0 1 0 1 000
Expand Down Expand Up @@ -310,13 +308,9 @@ private static int[] findGuardPattern(BitMatrix matrix,
* @param counters observed counters
* @param pattern expected pattern
* @param maxIndividualVariance The most any counter can differ before we give up
* @return ratio of total variance between counters and pattern compared to
* total pattern size, where the ratio has been multiplied by 256.
* So, 0 means no variance (perfect match); 256 means the total
* variance between counters and patterns equals the pattern length,
* higher values mean even more variance
* @return ratio of total variance between counters and pattern compared to total pattern size
*/
private static int patternMatchVariance(int[] counters, int[] pattern, int maxIndividualVariance) {
private static float patternMatchVariance(int[] counters, int[] pattern, float maxIndividualVariance) {
int numCounters = counters.length;
int total = 0;
int patternLength = 0;
Expand All @@ -327,21 +321,21 @@ private static int patternMatchVariance(int[] counters, int[] pattern, int maxIn
if (total < patternLength) {
// If we don't even have one pixel per unit of bar width, assume this
// is too small to reliably match, so fail:
return Integer.MAX_VALUE;
return Float.POSITIVE_INFINITY;
}
// We're going to fake floating-point math in integers. We just need to use more bits.
// Scale up patternLength so that intermediate values below like scaledCounter will have
// more "significant digits".
int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
float unitBarWidth = (float) total / patternLength;
maxIndividualVariance *= unitBarWidth;

int totalVariance = 0;
float totalVariance = 0.0f;
for (int x = 0; x < numCounters; x++) {
int counter = counters[x] << INTEGER_MATH_SHIFT;
int scaledPattern = pattern[x] * unitBarWidth;
int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
int counter = counters[x];
float scaledPattern = pattern[x] * unitBarWidth;
float variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
if (variance > maxIndividualVariance) {
return Integer.MAX_VALUE;
return Float.POSITIVE_INFINITY;
}
totalVariance += variance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public class FinderPatternFinder {
private static final int CENTER_QUORUM = 2;
protected static final int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
protected static final int MAX_MODULES = 57; // support up to version 10 for mobile clients
private static final int INTEGER_MATH_SHIFT = 8;

private final BitMatrix image;
private final List<FinderPattern> possibleCenters;
Expand Down Expand Up @@ -209,14 +208,15 @@ protected static boolean foundPatternCross(int[] stateCount) {
if (totalModuleSize < 7) {
return false;
}
int moduleSize = (totalModuleSize << INTEGER_MATH_SHIFT) / 7;
int maxVariance = moduleSize / 2;
float moduleSize = totalModuleSize / 7.0f;
float maxVariance = moduleSize / 2.0f;
// Allow less than 50% variance from 1-1-3-1-1 proportions
return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance &&
Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance &&
Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance &&
Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance &&
Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public final class PartialBlackBoxTestCase extends AbstractNegativeBlackBoxTestC

public PartialBlackBoxTestCase() {
super("src/test/resources/blackbox/partial");
addTest(2, 0.0f);
addTest(2, 90.0f);
addTest(2, 180.0f);
addTest(2, 270.0f);
addTest(1, 0.0f);
addTest(1, 90.0f);
addTest(1, 180.0f);
addTest(1, 270.0f);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public final class UPCABlackBox2TestCase extends AbstractBlackBoxTestCase {

public UPCABlackBox2TestCase() {
super("src/test/resources/blackbox/upca-2", new MultiFormatReader(), BarcodeFormat.UPC_A);
addTest(30, 36, 0, 2, 0.0f);
addTest(31, 36, 0, 2, 180.0f);
addTest(28, 36, 0, 2, 0.0f);
addTest(29, 36, 0, 2, 180.0f);
}

}

0 comments on commit 408c384

Please sign in to comment.