From 408c3848b10fab8e823dc71d306fb44f62896419 Mon Sep 17 00:00:00 2001 From: Sean Owen Date: Fri, 25 Apr 2014 13:02:44 +0100 Subject: [PATCH] Replace FP math faked in integers with plain floating-point math --- .../com/google/zxing/oned/CodaBarReader.java | 15 ++++---- .../com/google/zxing/oned/Code128Reader.java | 12 +++---- .../com/google/zxing/oned/Code93Reader.java | 12 +++---- .../java/com/google/zxing/oned/ITFReader.java | 9 +++-- .../com/google/zxing/oned/OneDReader.java | 34 +++++++------------ .../com/google/zxing/oned/UPCEANReader.java | 8 ++--- .../zxing/oned/rss/AbstractRSSReader.java | 4 +-- .../zxing/pdf417/detector/Detector.java | 30 +++++++--------- .../qrcode/detector/FinderPatternFinder.java | 16 ++++----- .../negative/PartialBlackBoxTestCase.java | 8 ++--- .../zxing/oned/UPCABlackBox2TestCase.java | 4 +-- 11 files changed, 66 insertions(+), 86 deletions(-) diff --git a/core/src/main/java/com/google/zxing/oned/CodaBarReader.java b/core/src/main/java/com/google/zxing/oned/CodaBarReader.java index 7c9cbd8ce9..d5fd956cb1 100644 --- a/core/src/main/java/com/google/zxing/oned/CodaBarReader.java +++ b/core/src/main/java/com/google/zxing/oned/CodaBarReader.java @@ -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(); @@ -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]; } @@ -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(); } diff --git a/core/src/main/java/com/google/zxing/oned/Code128Reader.java b/core/src/main/java/com/google/zxing/oned/Code128Reader.java index 82540c7295..5fe6c986c5 100644 --- a/core/src/main/java/com/google/zxing/oned/Code128Reader.java +++ b/core/src/main/java/com/google/zxing/oned/Code128Reader.java @@ -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; @@ -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; @@ -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; diff --git a/core/src/main/java/com/google/zxing/oned/Code93Reader.java b/core/src/main/java/com/google/zxing/oned/Code93Reader.java index e79925867a..56b2d78de3 100644 --- a/core/src/main/java/com/google/zxing/oned/Code93Reader.java +++ b/core/src/main/java/com/google/zxing/oned/Code93Reader.java @@ -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; diff --git a/core/src/main/java/com/google/zxing/oned/ITFReader.java b/core/src/main/java/com/google/zxing/oned/ITFReader.java index 004cd27771..b250b32786 100644 --- a/core/src/main/java/com/google/zxing/oned/ITFReader.java +++ b/core/src/main/java/com/google/zxing/oned/ITFReader.java @@ -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 @@ -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; diff --git a/core/src/main/java/com/google/zxing/oned/OneDReader.java b/core/src/main/java/com/google/zxing/oned/OneDReader.java index 0ef3e14e1f..29d93f0c0f 100644 --- a/core/src/main/java/com/google/zxing/oned/OneDReader.java +++ b/core/src/main/java/com/google/zxing/oned/OneDReader.java @@ -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); @@ -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; @@ -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; } diff --git a/core/src/main/java/com/google/zxing/oned/UPCEANReader.java b/core/src/main/java/com/google/zxing/oned/UPCEANReader.java index c9ddc32c72..677ec91720 100644 --- a/core/src/main/java/com/google/zxing/oned/UPCEANReader.java +++ b/core/src/main/java/com/google/zxing/oned/UPCEANReader.java @@ -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. @@ -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; diff --git a/core/src/main/java/com/google/zxing/oned/rss/AbstractRSSReader.java b/core/src/main/java/com/google/zxing/oned/rss/AbstractRSSReader.java index d230961311..e86b394deb 100644 --- a/core/src/main/java/com/google/zxing/oned/rss/AbstractRSSReader.java +++ b/core/src/main/java/com/google/zxing/oned/rss/AbstractRSSReader.java @@ -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; diff --git a/core/src/main/java/com/google/zxing/pdf417/detector/Detector.java b/core/src/main/java/com/google/zxing/pdf417/detector/Detector.java index 72a2eec349..1538e4a9c2 100644 --- a/core/src/main/java/com/google/zxing/pdf417/detector/Detector.java +++ b/core/src/main/java/com/google/zxing/pdf417/detector/Detector.java @@ -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 @@ -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; @@ -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; } diff --git a/core/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/core/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java index f35b0af4b9..9c0c59f893 100755 --- a/core/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java +++ b/core/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java @@ -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 possibleCenters; @@ -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() { diff --git a/core/src/test/java/com/google/zxing/negative/PartialBlackBoxTestCase.java b/core/src/test/java/com/google/zxing/negative/PartialBlackBoxTestCase.java index 8357cc5dfb..f38d29a8b5 100644 --- a/core/src/test/java/com/google/zxing/negative/PartialBlackBoxTestCase.java +++ b/core/src/test/java/com/google/zxing/negative/PartialBlackBoxTestCase.java @@ -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); } } diff --git a/core/src/test/java/com/google/zxing/oned/UPCABlackBox2TestCase.java b/core/src/test/java/com/google/zxing/oned/UPCABlackBox2TestCase.java index c379fd9745..b6414df664 100644 --- a/core/src/test/java/com/google/zxing/oned/UPCABlackBox2TestCase.java +++ b/core/src/test/java/com/google/zxing/oned/UPCABlackBox2TestCase.java @@ -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); } }