From 676716880120e19798be909ab6ddef390f8a1c44 Mon Sep 17 00:00:00 2001 From: srowen Date: Tue, 13 Nov 2012 13:31:50 +0000 Subject: [PATCH] More optimizations plus some improved RSS expanded misdetection detection git-svn-id: https://zxing.googlecode.com/svn/trunk@2517 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../com/google/zxing/oned/Code39Reader.java | 32 +++---- .../com/google/zxing/oned/Code93Reader.java | 45 ++++++---- .../google/zxing/oned/rss/RSS14Reader.java | 8 +- .../oned/rss/expanded/RSSExpandedReader.java | 84 ++++++++++++++++--- 4 files changed, 126 insertions(+), 43 deletions(-) diff --git a/core/src/com/google/zxing/oned/Code39Reader.java b/core/src/com/google/zxing/oned/Code39Reader.java index 53a8ff3fc..598f43e82 100644 --- a/core/src/com/google/zxing/oned/Code39Reader.java +++ b/core/src/com/google/zxing/oned/Code39Reader.java @@ -99,10 +99,12 @@ public final class Code39Reader extends OneDReader { public Result decodeRow(int rowNumber, BitArray row, Map hints) throws NotFoundException, ChecksumException, FormatException { - Arrays.fill(counters, 0); - decodeRowResult.setLength(0); + int[] theCounters = counters; + Arrays.fill(theCounters, 0); + StringBuilder result = decodeRowResult; + result.setLength(0); - int[] start = findAsteriskPattern(row, counters); + int[] start = findAsteriskPattern(row, theCounters); // Read off white space int nextStart = row.getNextSet(start[1]); int end = row.getSize(); @@ -110,25 +112,25 @@ public final class Code39Reader extends OneDReader { char decodedChar; int lastStart; do { - recordPattern(row, nextStart, counters); - int pattern = toNarrowWidePattern(counters); + recordPattern(row, nextStart, theCounters); + int pattern = toNarrowWidePattern(theCounters); if (pattern < 0) { throw NotFoundException.getNotFoundInstance(); } decodedChar = patternToChar(pattern); - decodeRowResult.append(decodedChar); + result.append(decodedChar); lastStart = nextStart; - for (int counter : counters) { + for (int counter : theCounters) { nextStart += counter; } // Read off white space nextStart = row.getNextSet(nextStart); } while (decodedChar != '*'); - decodeRowResult.setLength(decodeRowResult.length() - 1); // remove asterisk + result.setLength(result.length() - 1); // remove asterisk // Look for whitespace after pattern: int lastPatternSize = 0; - for (int counter : counters) { + for (int counter : theCounters) { lastPatternSize += counter; } int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; @@ -139,27 +141,27 @@ public final class Code39Reader extends OneDReader { } if (usingCheckDigit) { - int max = decodeRowResult.length() - 1; + int max = result.length() - 1; int total = 0; for (int i = 0; i < max; i++) { total += ALPHABET_STRING.indexOf(decodeRowResult.charAt(i)); } - if (decodeRowResult.charAt(max) != ALPHABET[total % 43]) { + if (result.charAt(max) != ALPHABET[total % 43]) { throw ChecksumException.getChecksumInstance(); } - decodeRowResult.setLength(max); + result.setLength(max); } - if (decodeRowResult.length() == 0) { + if (result.length() == 0) { // false positive throw NotFoundException.getNotFoundInstance(); } String resultString; if (extendedMode) { - resultString = decodeExtended(decodeRowResult); + resultString = decodeExtended(result); } else { - resultString = decodeRowResult.toString(); + resultString = result.toString(); } float left = (float) (start[1] + start[0]) / 2.0f; diff --git a/core/src/com/google/zxing/oned/Code93Reader.java b/core/src/com/google/zxing/oned/Code93Reader.java index 8f49d3352..7cb2ee0fb 100644 --- a/core/src/com/google/zxing/oned/Code93Reader.java +++ b/core/src/com/google/zxing/oned/Code93Reader.java @@ -25,6 +25,7 @@ import com.google.zxing.Result; import com.google.zxing.ResultPoint; import com.google.zxing.common.BitArray; +import java.util.Arrays; import java.util.Map; /** @@ -53,6 +54,14 @@ public final class Code93Reader extends OneDReader { }; private static final int ASTERISK_ENCODING = CHARACTER_ENCODINGS[47]; + private final StringBuilder decodeRowResult; + private final int[] counters; + + public Code93Reader() { + decodeRowResult = new StringBuilder(20); + counters = new int[6]; + } + @Override public Result decodeRow(int rowNumber, BitArray row, Map hints) throws NotFoundException, ChecksumException, FormatException { @@ -62,20 +71,23 @@ public final class Code93Reader extends OneDReader { int nextStart = row.getNextSet(start[1]); int end = row.getSize(); - StringBuilder result = new StringBuilder(20); - int[] counters = new int[6]; + int[] theCounters = counters; + Arrays.fill(theCounters, 0); + StringBuilder result = decodeRowResult; + result.setLength(0); + char decodedChar; int lastStart; do { - recordPattern(row, nextStart, counters); - int pattern = toPattern(counters); + recordPattern(row, nextStart, theCounters); + int pattern = toPattern(theCounters); if (pattern < 0) { throw NotFoundException.getNotFoundInstance(); } decodedChar = patternToChar(pattern); result.append(decodedChar); lastStart = nextStart; - for (int counter : counters) { + for (int counter : theCounters) { nextStart += counter; } // Read off white space @@ -111,33 +123,34 @@ public final class Code93Reader extends OneDReader { } - private static int[] findAsteriskPattern(BitArray row) throws NotFoundException { + private int[] findAsteriskPattern(BitArray row) throws NotFoundException { int width = row.getSize(); int rowOffset = row.getNextSet(0); - int counterPosition = 0; - int[] counters = new int[6]; + Arrays.fill(counters, 0); + int[] theCounters = counters; int patternStart = rowOffset; boolean isWhite = false; - int patternLength = counters.length; + int patternLength = theCounters.length; + int counterPosition = 0; for (int i = rowOffset; i < width; i++) { if (row.get(i) ^ isWhite) { - counters[counterPosition]++; + theCounters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { - if (toPattern(counters) == ASTERISK_ENCODING) { + if (toPattern(theCounters) == ASTERISK_ENCODING) { return new int[]{patternStart, i}; } - patternStart += counters[0] + counters[1]; - System.arraycopy(counters, 2, counters, 0, patternLength - 2); - counters[patternLength - 2] = 0; - counters[patternLength - 1] = 0; + patternStart += theCounters[0] + theCounters[1]; + System.arraycopy(theCounters, 2, theCounters, 0, patternLength - 2); + theCounters[patternLength - 2] = 0; + theCounters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; } - counters[counterPosition] = 1; + theCounters[counterPosition] = 1; isWhite = !isWhite; } } diff --git a/core/src/com/google/zxing/oned/rss/RSS14Reader.java b/core/src/com/google/zxing/oned/rss/RSS14Reader.java index 99d25ef3a..98316b32e 100644 --- a/core/src/com/google/zxing/oned/rss/RSS14Reader.java +++ b/core/src/com/google/zxing/oned/rss/RSS14Reader.java @@ -71,9 +71,13 @@ public final class RSS14Reader extends AbstractRSSReader { Pair rightPair = decodePair(row, true, rowNumber, hints); addOrTally(possibleRightPairs, rightPair); row.reverse(); - for (Pair left : possibleLeftPairs) { + int lefSize = possibleLeftPairs.size(); + for (int i = 0; i < lefSize; i++) { + Pair left = possibleLeftPairs.get(i); if (left.getCount() > 1) { - for (Pair right : possibleRightPairs) { + int rightSize = possibleRightPairs.size(); + for (int j = 0; j < rightSize; j++) { + Pair right = possibleRightPairs.get(j); if (right.getCount() > 1) { if (checkChecksum(left, right)) { return constructResult(left, right); diff --git a/core/src/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java b/core/src/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java index 05e57559f..6f84550fc 100644 --- a/core/src/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java +++ b/core/src/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java @@ -89,7 +89,6 @@ public final class RSSExpandedReader extends AbstractRSSReader { { 45, 135, 194, 160, 58, 174, 100, 89} }; - /* private static final int FINDER_PAT_A = 0; private static final int FINDER_PAT_B = 1; private static final int FINDER_PAT_C = 2; @@ -110,8 +109,7 @@ public final class RSSExpandedReader extends AbstractRSSReader { { FINDER_PAT_A, FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B, FINDER_PAT_C, FINDER_PAT_D, FINDER_PAT_D, FINDER_PAT_E, FINDER_PAT_E, FINDER_PAT_F, FINDER_PAT_F }, }; - private static final int LONGEST_SEQUENCE_SIZE = FINDER_PATTERN_SEQUENCES[FINDER_PATTERN_SEQUENCES.length - 1].length; - */ + //private static final int LONGEST_SEQUENCE_SIZE = FINDER_PATTERN_SEQUENCES[FINDER_PATTERN_SEQUENCES.length - 1].length; private static final int MAX_PAIRS = 11; @@ -188,24 +186,90 @@ public final class RSSExpandedReader extends AbstractRSSReader { } private List checkRows(boolean reverse) { + // Limit number of rows we are checking + // We use recursive algorithm with pure complexity and don't want it to take forever + // Stacked barcode can have up to 11 rows, so 25 seems resonable enough + if (this.rows.size() > 25) { + this.rows.clear(); // We will never have a chance to get result, so clear it + return null; + } + this.pairs.clear(); if (reverse) { Collections.reverse(this.rows); } - for(ExpandedRow erow : this.rows) { - this.pairs.addAll(erow.getPairs()); - } - //System.out.println(this.pairs.size()+" pairs on MULTIPLE ROWS: "+this.rows); - if (checkChecksum()) { - return this.pairs; + List ps = null; + try { + ps = checkRows(new ArrayList(), 0); + } catch (NotFoundException e) { + // OK } if (reverse) { Collections.reverse(this.rows); } - return null; + return ps; + } + + // Try to construct a valid rows sequence + // Recursion is used to implement backtracking + private List checkRows(List collectedRows, int currentRow) throws NotFoundException { + for (int i = currentRow; i < rows.size(); i++) { + ExpandedRow row = rows.get(i); + this.pairs.clear(); + int size = collectedRows.size(); + for (int j = 0; j < size; j++) { + this.pairs.addAll(collectedRows.get(j).getPairs()); + } + this.pairs.addAll(row.getPairs()); + + if (!isValidSequence(this.pairs)) { + continue; + } + + if (checkChecksum()) { + return this.pairs; + } + + List rs = new ArrayList(); + rs.addAll(collectedRows); + rs.add(row); + try { + // Recursion: try to add more rows + return checkRows(rs, i + 1); + } catch (NotFoundException e) { + // We failed, try the next candidate + continue; + } + } + + throw NotFoundException.getNotFoundInstance(); + } + + // Whether the pairs form a valid find pattern seqience, + // either complete or a prefix + private static boolean isValidSequence(List pairs) { + for (int[] sequence : FINDER_PATTERN_SEQUENCES) { + if (pairs.size() > sequence.length) { + continue; + } + + boolean stop = true; + for (int j = 0; j < pairs.size(); j++) { + if (pairs.get(j).getFinderPattern().getValue() != sequence[j]) { + stop = false; + break; + } + } + + if (stop) { + return true; + } + } + + return false; } private void storeRow(int rowNumber, boolean wasReversed) {