mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Improved approach to 1D decoding -- better use of integer math by scaling pattern ratios up to expected number of pixels, rather than the other way. Modified constants accordingly. Also introduced notion of maxium variance that any one bar in a pattern can have and stiill be accepted. Finally, adjusted false-positives test failure limit downward due to recent improvements.
git-svn-id: https://zxing.googlecode.com/svn/trunk@441 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
2f9e5ca32b
commit
180e833b3e
|
@ -36,6 +36,7 @@ import java.util.Hashtable;
|
||||||
public abstract class AbstractOneDReader implements OneDReader {
|
public abstract class AbstractOneDReader implements OneDReader {
|
||||||
|
|
||||||
private static final int INTEGER_MATH_SHIFT = 8;
|
private static final int INTEGER_MATH_SHIFT = 8;
|
||||||
|
public static final int PATTERN_MATCH_RESULT_SCALE_FACTOR = 256;
|
||||||
|
|
||||||
public final Result decode(MonochromeBitmapSource image) throws ReaderException {
|
public final Result decode(MonochromeBitmapSource image) throws ReaderException {
|
||||||
return decode(image, null);
|
return decode(image, null);
|
||||||
|
@ -183,12 +184,13 @@ public abstract class AbstractOneDReader implements OneDReader {
|
||||||
*
|
*
|
||||||
* @param counters observed counters
|
* @param counters observed counters
|
||||||
* @param pattern expected pattern
|
* @param pattern expected pattern
|
||||||
|
* @param maxIndividualVariance
|
||||||
* @return ratio of total variance between counters and pattern compared to total pattern size,
|
* @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
|
* 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
|
* the total variance between counters and patterns equals the pattern length, higher values mean
|
||||||
* even more variance
|
* even more variance
|
||||||
*/
|
*/
|
||||||
static int patternMatchVariance(int[] counters, int[] pattern) {
|
static int patternMatchVariance(int[] counters, int[] pattern, int maxIndividualVariance) {
|
||||||
int numCounters = counters.length;
|
int numCounters = counters.length;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
int patternLength = 0;
|
int patternLength = 0;
|
||||||
|
@ -204,16 +206,20 @@ public abstract class AbstractOneDReader implements OneDReader {
|
||||||
// We're going to fake floating-point math in integers. We just need to use more bits.
|
// 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
|
// Scale up patternLength so that intermediate values below like scaledCounter will have
|
||||||
// more "significant digits"
|
// more "significant digits"
|
||||||
patternLength <<= INTEGER_MATH_SHIFT;
|
int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
|
||||||
int patternRatio = patternLength / total;
|
maxIndividualVariance *= unitBarWidth;
|
||||||
|
|
||||||
int totalVariance = 0;
|
int totalVariance = 0;
|
||||||
for (int x = 0; x < numCounters; x++) {
|
for (int x = 0; x < numCounters; x++) {
|
||||||
int scaledCounter = counters[x] * patternRatio;
|
int counter = counters[x] << INTEGER_MATH_SHIFT;
|
||||||
int width = pattern[x] << INTEGER_MATH_SHIFT;
|
int scaledPattern = pattern[x] * unitBarWidth;
|
||||||
totalVariance += scaledCounter > width ? scaledCounter - width : width - scaledCounter;
|
int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
|
||||||
|
if (variance > maxIndividualVariance) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
totalVariance += variance;
|
||||||
}
|
}
|
||||||
return (totalVariance << 8) / patternLength;
|
return totalVariance / total;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This declaration should not be necessary, since this class is
|
// This declaration should not be necessary, since this class is
|
||||||
|
|
|
@ -35,7 +35,8 @@ import java.util.Hashtable;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractUPCEANReader extends AbstractOneDReader implements UPCEANReader {
|
public abstract class AbstractUPCEANReader extends AbstractOneDReader implements UPCEANReader {
|
||||||
|
|
||||||
private static final int MAX_VARIANCE = 104;
|
private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.40625f);
|
||||||
|
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.5f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start/end guard pattern.
|
* Start/end guard pattern.
|
||||||
|
@ -207,7 +208,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
|
||||||
counters[counterPosition]++;
|
counters[counterPosition]++;
|
||||||
} else {
|
} else {
|
||||||
if (counterPosition == patternLength - 1) {
|
if (counterPosition == patternLength - 1) {
|
||||||
if (patternMatchVariance(counters, pattern) < MAX_VARIANCE) {
|
if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
||||||
return new int[]{patternStart, x};
|
return new int[]{patternStart, x};
|
||||||
}
|
}
|
||||||
patternStart += counters[0] + counters[1];
|
patternStart += counters[0] + counters[1];
|
||||||
|
@ -242,12 +243,12 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
|
||||||
static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns)
|
static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns)
|
||||||
throws ReaderException {
|
throws ReaderException {
|
||||||
recordPattern(row, rowOffset, counters);
|
recordPattern(row, rowOffset, counters);
|
||||||
int bestVariance = MAX_VARIANCE; // worst variance we'll accept
|
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||||
int bestMatch = -1;
|
int bestMatch = -1;
|
||||||
int max = patterns.length;
|
int max = patterns.length;
|
||||||
for (int i = 0; i < max; i++) {
|
for (int i = 0; i < max; i++) {
|
||||||
int[] pattern = patterns[i];
|
int[] pattern = patterns[i];
|
||||||
int variance = patternMatchVariance(counters, pattern);
|
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||||
if (variance < bestVariance) {
|
if (variance < bestVariance) {
|
||||||
bestVariance = variance;
|
bestVariance = variance;
|
||||||
bestMatch = i;
|
bestMatch = i;
|
||||||
|
|
|
@ -142,7 +142,8 @@ public final class Code128Reader extends AbstractOneDReader {
|
||||||
{2, 3, 3, 1, 1, 1, 2}
|
{2, 3, 3, 1, 1, 1, 2}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int MAX_VARIANCE = 56;
|
private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.1875f);
|
||||||
|
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);
|
||||||
|
|
||||||
private static final int CODE_SHIFT = 98;
|
private static final int CODE_SHIFT = 98;
|
||||||
|
|
||||||
|
@ -183,10 +184,10 @@ public final class Code128Reader extends AbstractOneDReader {
|
||||||
counters[counterPosition]++;
|
counters[counterPosition]++;
|
||||||
} else {
|
} else {
|
||||||
if (counterPosition == patternLength - 1) {
|
if (counterPosition == patternLength - 1) {
|
||||||
int bestVariance = MAX_VARIANCE;
|
int bestVariance = MAX_AVG_VARIANCE;
|
||||||
int bestMatch = -1;
|
int bestMatch = -1;
|
||||||
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
|
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
|
||||||
int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode]);
|
int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);
|
||||||
if (variance < bestVariance) {
|
if (variance < bestVariance) {
|
||||||
bestVariance = variance;
|
bestVariance = variance;
|
||||||
bestMatch = startCode;
|
bestMatch = startCode;
|
||||||
|
@ -214,11 +215,11 @@ public final class Code128Reader extends AbstractOneDReader {
|
||||||
|
|
||||||
private static int decodeCode(BitArray row, int[] counters, int rowOffset) throws ReaderException {
|
private static int decodeCode(BitArray row, int[] counters, int rowOffset) throws ReaderException {
|
||||||
recordPattern(row, rowOffset, counters);
|
recordPattern(row, rowOffset, counters);
|
||||||
int bestVariance = MAX_VARIANCE; // worst variance we'll accept
|
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||||
int bestMatch = -1;
|
int bestMatch = -1;
|
||||||
for (int d = 0; d < CODE_PATTERNS.length; d++) {
|
for (int d = 0; d < CODE_PATTERNS.length; d++) {
|
||||||
int[] pattern = CODE_PATTERNS[d];
|
int[] pattern = CODE_PATTERNS[d];
|
||||||
int variance = patternMatchVariance(counters, pattern);
|
int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||||
if (variance < bestVariance) {
|
if (variance < bestVariance) {
|
||||||
bestVariance = variance;
|
bestVariance = variance;
|
||||||
bestMatch = d;
|
bestMatch = d;
|
||||||
|
|
|
@ -35,7 +35,7 @@ import java.io.IOException;
|
||||||
public final class FalsePositivesBlackBoxTestCase extends AbstractBlackBoxTestCase {
|
public final class FalsePositivesBlackBoxTestCase extends AbstractBlackBoxTestCase {
|
||||||
|
|
||||||
// This number should be reduced as we get better at rejecting false positives.
|
// This number should be reduced as we get better at rejecting false positives.
|
||||||
private static final int FALSE_POSITIVES_ALLOWED = 44;
|
private static final int FALSE_POSITIVES_ALLOWED = 23;
|
||||||
|
|
||||||
// Use the multiformat reader to evaluate all decoders in the system.
|
// Use the multiformat reader to evaluate all decoders in the system.
|
||||||
public FalsePositivesBlackBoxTestCase() {
|
public FalsePositivesBlackBoxTestCase() {
|
||||||
|
|
Loading…
Reference in a new issue