mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Replace FP math faked in integers with plain floating-point math
This commit is contained in:
parent
3b8b44856d
commit
408c3848b1
|
@ -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 @@ public final class CodaBarReader extends OneDReader {
|
|||
}
|
||||
|
||||
// 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 @@ public final class CodaBarReader extends OneDReader {
|
|||
// 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();
|
||||
}
|
||||
|
|
|
@ -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 @@ public final class Code128Reader extends OneDReader {
|
|||
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 @@ public final class Code128Reader extends OneDReader {
|
|||
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;
|
||||
|
|
|
@ -170,20 +170,16 @@ public final class Code93Reader extends OneDReader {
|
|||
}
|
||||
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;
|
||||
|
|
|
@ -44,8 +44,8 @@ import java.util.Map;
|
|||
*/
|
||||
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 @@ public final class ITFReader extends OneDReader {
|
|||
* @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;
|
||||
|
|
|
@ -41,9 +41,6 @@ import java.util.Map;
|
|||
*/
|
||||
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 @@ public abstract class OneDReader implements Reader {
|
|||
* @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 @@ public abstract class OneDReader implements Reader {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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 @@ public abstract class UPCEANReader extends OneDReader {
|
|||
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;
|
||||
|
|
|
@ -21,8 +21,8 @@ import com.google.zxing.oned.OneDReader;
|
|||
|
||||
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;
|
||||
|
|
|
@ -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 @@ public final class Detector {
|
|||
* @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 @@ public final class Detector {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -209,14 +208,15 @@ public class FinderPatternFinder {
|
|||
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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue