From: srowen Date: Wed, 14 Nov 2007 19:23:23 +0000 (+0000) Subject: Code tweaks and so forth with Daniel X-Git-Url: http://git.rot13.org/?p=zxing.git;a=commitdiff_plain;h=8e9b56650e2d9bdfe3a69abe2b79d3fa54ac0555 Code tweaks and so forth with Daniel git-svn-id: http://zxing.googlecode.com/svn/trunk@34 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- diff --git a/core/src/com/google/zxing/MultiFormatReader.java b/core/src/com/google/zxing/MultiFormatReader.java index d1619b4c..a4d21edb 100644 --- a/core/src/com/google/zxing/MultiFormatReader.java +++ b/core/src/com/google/zxing/MultiFormatReader.java @@ -38,8 +38,7 @@ public final class MultiFormatReader implements Reader { public Result decode(MonochromeBitmapSource image, Hashtable hints) throws ReaderException { - Hashtable possibleFormats = - hints == null ? null : (Hashtable) hints.get(DecodeHintType.POSSIBLE_FORMATS); + Hashtable possibleFormats = hints == null ? null : (Hashtable) hints.get(DecodeHintType.POSSIBLE_FORMATS); boolean tryUPC = false; boolean tryQR = false; @@ -51,7 +50,7 @@ public final class MultiFormatReader implements Reader { } else if (possibleFormats.contains(BarcodeFormat.QR_CODE)) { tryQR = true; } else { - throw new ReaderException(); + throw new ReaderException("POSSIBLE_FORMATS specifies no supported types"); } // UPC is much faster to decode, so try it first. @@ -70,7 +69,7 @@ public final class MultiFormatReader implements Reader { } } - throw new ReaderException(); + throw new ReaderException("Could not locate and decode a barcode in the image"); } } diff --git a/core/src/com/google/zxing/common/BitArray.java b/core/src/com/google/zxing/common/BitArray.java index de44db3c..67cdbfd2 100644 --- a/core/src/com/google/zxing/common/BitArray.java +++ b/core/src/com/google/zxing/common/BitArray.java @@ -86,21 +86,21 @@ public final class BitArray { for (int i = 0; i < max; i++) { newBits[i] = 0; } + int size = this.size; for (int i = 0; i < size; i++) { - if (this.get(size - i - 1)) { + if (get(size - i - 1)) { newBits[i >> 5] |= 1 << (i & 0x1F); } } bits = newBits; } - private int[] makeArray(int size) { + private static int[] makeArray(int size) { int arraySize = size >> 5; if ((size & 0x1F) != 0) { arraySize++; } - int[] result = new int[arraySize]; - return result; + return new int[arraySize]; } } \ No newline at end of file diff --git a/core/src/com/google/zxing/upc/UPCDecoder.java b/core/src/com/google/zxing/upc/UPCDecoder.java index 3e530e0a..99928f32 100755 --- a/core/src/com/google/zxing/upc/UPCDecoder.java +++ b/core/src/com/google/zxing/upc/UPCDecoder.java @@ -19,8 +19,6 @@ package com.google.zxing.upc; import com.google.zxing.common.BitArray; import com.google.zxing.MonochromeBitmapSource; -import java.util.Arrays; - /** * This class takes a bitmap, and attempts to return a String which is the contents of the UPC * barcode in the image. It should be scale-invariant, but does not make any corrections for @@ -28,36 +26,55 @@ import java.util.Arrays; * * @author dswitkin@google.com (Daniel Switkin) */ -public class UPCDecoder { +final class UPCDecoder { + + private static final byte[] BITMAP_SEARCH_PATTERN = { 50, 49, 51, 48, 52, 46, 54, 43, 57, 40, 60 }; + private static final byte[] START_END_PATTERN = { 1, 1, 1 }; + private static final byte[] MIDDLE_PATTERN = { 1, 1, 1, 1, 1 }; + private static final byte[][] DIGIT_PATTERNS = { + { 30, 20, 10, 10 }, // 0 + { 20, 20, 20, 10 }, // 1 + { 20, 10, 20, 20 }, // 2 + { 10, 40, 10, 10 }, // 3 + { 10, 10, 30, 20 }, // 4 + { 10, 20, 30, 10 }, // 5 + { 10, 10, 10, 40 }, // 6 + { 10, 30, 10, 20 }, // 7 + { 10, 20, 10, 30 }, // 8 + { 30, 10, 10, 20 } // 9 + }; + private static final int TOLERANCE = 5; + + private MonochromeBitmapSource bitmap; + private int width; + private int height; + private StringBuffer result; + UPCDecoder(MonochromeBitmapSource bitmap) { - mBitmap = bitmap; - if (bitmap != null) { - mWidth = bitmap.getWidth(); - mHeight = bitmap.getHeight(); - } + this.bitmap = bitmap; + width = bitmap.getWidth(); + height = bitmap.getHeight(); } // To decode the image, we follow a search pattern defined in kBitmapSearchPattern. It is a // list of percentages which translate to row numbers to scan across. For each row, we scan // left to right, and if that fails, we reverse the row in place and try again to see if the // bar code was upside down. - public String decode() { - if (mBitmap == null) return ""; - - BitArray rowData = new BitArray(mWidth); + String decode() { + BitArray rowData = new BitArray(width); String longestResult = ""; int found = -1; - for (int x = 0; x < kBitmapSearchPattern.length; x++) { - int row = mHeight * kBitmapSearchPattern[x] / 100; - mBitmap.getBlackRow(row, rowData, 0, mWidth); + for (int x = 0; x < BITMAP_SEARCH_PATTERN.length; x++) { + int row = height * BITMAP_SEARCH_PATTERN[x] / 100; + bitmap.getBlackRow(row, rowData, 0, width); if (decodeRow(rowData)) { found = x; break; } //Log("decode: row " + row + " normal result: " + mResult); - if (mResult.length() > longestResult.length()) { - longestResult = mResult; + if (result.length() > longestResult.length()) { + longestResult = result.toString(); } rowData.reverse(); @@ -66,48 +83,60 @@ public class UPCDecoder { break; } //Log("decode: row " + row + " inverted result: " + mResult); - if (mResult.length() > longestResult.length()) { - longestResult = mResult; + if (result.length() > longestResult.length()) { + longestResult = result.toString(); } } - if (found >= 0) return mResult; - else return ""; + if (found >= 0) { + return result.toString(); + } else { + return ""; + } } - // UPC-A bar codes are made up of a left marker, six digits, a middle marker, six more digits, - // and an end marker, reading from left to right. For more information, see: - // - // http://en.wikipedia.org/wiki/Universal_Product_Code - // - // TODO: Add support for UPC-E Zero Compressed bar codes. - // TODO: Add support for EAN-13 (European Article Number) bar codes. - // FIXME: Don't trust the first result from findPattern() for the start sequence - resume from - // that spot and try to start again if finding digits fails. + /** + * UPC-A bar codes are made up of a left marker, six digits, a middle marker, six more digits, + * and an end marker, reading from left to right. For more information, see: + * + * http://en.wikipedia.org/wiki/Universal_Product_Code + */ private boolean decodeRow(BitArray rowData) { - mResult = ""; - int rowOffset = findPattern(rowData, 0, kStartEndPattern, false); - if (rowOffset < 0) return false; + // TODO: Add support for UPC-E Zero Compressed bar codes. + // TODO: Add support for EAN-13 (European Article Number) bar codes. + // FIXME: Don't trust the first result from findPattern() for the start sequence - resume from + // that spot and try to start again if finding digits fails. + result = new StringBuffer(); + int rowOffset = findPattern(rowData, 0, START_END_PATTERN, false); + if (rowOffset < 0) { + return false; + } //Log("Start pattern ends at column " + rowOffset); rowOffset = decodeOneSide(rowData, rowOffset); - if (rowOffset < 0) return false; + if (rowOffset < 0) { + return false; + } - rowOffset = findPattern(rowData, rowOffset, kMiddlePattern, true); - if (rowOffset < 0) return false; + rowOffset = findPattern(rowData, rowOffset, MIDDLE_PATTERN, true); + if (rowOffset < 0) { + return false; + } //Log("Middle pattern ends at column " + rowOffset); rowOffset = decodeOneSide(rowData, rowOffset); - if (rowOffset < 0) return false; + if (rowOffset < 0) { + return false; + } // We could attempt to read the end pattern for sanity, but there's not much point. // UPC-A codes have 12 digits, so any other result is garbage. - return (mResult.length() == 12); + return result.length() == 12; } private int decodeOneSide(BitArray rowData, int rowOffset) { int[] counters = new int[4]; - for (int x = 0; x < 6 && rowOffset < mWidth; x++) { + for (int x = 0; x < 6 && rowOffset < width; x++) { recordPattern(rowData, rowOffset, counters, 4); for (int y = 0; y < 4; y++) { rowOffset += counters[y]; @@ -116,7 +145,7 @@ public class UPCDecoder { if (c == '-') { return -1; } else { - mResult += c; + result.append(c); } } return rowOffset; @@ -127,13 +156,14 @@ public class UPCDecoder { // begin on white or black based on the flag. private int findPattern(BitArray rowData, int rowOffset, byte[] pattern, boolean whiteFirst) { int[] counters = new int[pattern.length]; - int width = mWidth; + int width = this.width; boolean isWhite = false; - for (; rowOffset < width; rowOffset++) { + while (rowOffset < width) { isWhite = !rowData.get(rowOffset); if (whiteFirst == isWhite) { break; } + rowOffset++; } int counterPosition = 0; @@ -160,15 +190,19 @@ public class UPCDecoder { return -1; } - // Records a pattern of alternating white and black pixels, returning an array of how many - // pixels of each color were seen. The pattern begins immediately based on the color of the - // first pixel encountered, so a patternSize of 3 could result in WBW or BWB. + /** + * Records a pattern of alternating white and black pixels, returning an array of how many + * pixels of each color were seen. The pattern begins immediately based on the color of the + * first pixel encountered, so a patternSize of 3 could result in WBW or BWB. + */ private void recordPattern(BitArray rowData, int rowOffset, int[] counters, int patternSize) { - Arrays.fill(counters, 0); + for (int i = 0; i < counters.length; i++) { + counters[i] = 0; + } boolean isWhite = !rowData.get(rowOffset); int counterPosition = 0; - int width = mWidth; + int width = this.width; for (int x = rowOffset; x < width; x++) { boolean pixel = rowData.get(x); if ((!pixel && isWhite) || (pixel && !isWhite)) { @@ -185,13 +219,15 @@ public class UPCDecoder { } } - // This is an optimized version of doesPatternMatch() which is specific to recognizing digits. - // The average is divided by 7 because there are 7 bits per digit, even though the color only - // alternates four times. kDigitPatterns has been premultiplied by 10 for efficiency. Notice - // that the contents of the counters array are modified to save an extra allocation, so don't - // use these values after returning from this call. - // TODO: add EAN even parity support - private char findDigit(int[] counters) { + /** + * This is an optimized version of doesPatternMatch() which is specific to recognizing digits. + * The average is divided by 7 because there are 7 bits per digit, even though the color only + * alternates four times. kDigitPatterns has been premultiplied by 10 for efficiency. Notice + * that the contents of the counters array are modified to save an extra allocation, so don't + * use these values after returning from this call. + */ + private static char findDigit(int[] counters) { + // TODO: add EAN even parity support int total = counters[0] + counters[1] + counters[2] + counters[3]; int average = total * 10 / 7; for (int x = 0; x < 4; x++) { @@ -201,60 +237,41 @@ public class UPCDecoder { for (int x = 0; x < 10; x++) { boolean match = true; for (int y = 0; y < 4; y++) { - int diff = counters[y] - kDigitPatterns[x][y]; - if (diff > kTolerance || diff < -kTolerance) { + int diff = counters[y] - DIGIT_PATTERNS[x][y]; + if (diff > TOLERANCE || diff < -TOLERANCE) { match = false; break; } } - if (match) return kDigits[x]; + if (match) { + return (char) ((int) '0' + x); + } } return '-'; } - // Finds whether the given set of pixel counters matches the requested pattern. Taking an - // average based on the number of counters offers some robustness when antialiased edges get - // interpreted as the wrong color. - // TODO: Remove the divide for performance. - private boolean doesPatternMatch(int[] counters, byte[] pattern) { + /** + * Finds whether the given set of pixel counters matches the requested pattern. Taking an + * average based on the number of counters offers some robustness when antialiased edges get + * interpreted as the wrong color. + */ + private static boolean doesPatternMatch(int[] counters, byte[] pattern) { + // TODO: Remove the divide for performance. int total = 0; - for (int x = 0; x < counters.length; x++) { + int numCounters = counters.length; + for (int x = 0; x < numCounters; x++) { total += counters[x]; } int average = total * 10 / counters.length; - for (int x = 0; x < counters.length; x++) { + for (int x = 0; x < numCounters; x++) { int scaledCounter = counters[x] * 100 / average; int scaledPattern = pattern[x] * 10; - if (scaledCounter < scaledPattern - kTolerance || scaledCounter > scaledPattern + kTolerance) { + if (scaledCounter < scaledPattern - TOLERANCE || scaledCounter > scaledPattern + TOLERANCE) { return false; } } return true; } - private static final byte[] kBitmapSearchPattern = { 50, 49, 51, 48, 52, 46, 54, 43, 57, 40, 60 }; - private static final byte[] kStartEndPattern = { 1, 1, 1 }; - private static final byte[] kMiddlePattern = { 1, 1, 1, 1, 1 }; - - private static final byte[][] kDigitPatterns = { - { 30, 20, 10, 10 }, // 0 - { 20, 20, 20, 10 }, // 1 - { 20, 10, 20, 20 }, // 2 - { 10, 40, 10, 10 }, // 3 - { 10, 10, 30, 20 }, // 4 - { 10, 20, 30, 10 }, // 5 - { 10, 10, 10, 40 }, // 6 - { 10, 30, 10, 20 }, // 7 - { 10, 20, 10, 30 }, // 8 - { 30, 10, 10, 20 } // 9 - }; - - private static final char[] kDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - private static final int kTolerance = 5; - - private MonochromeBitmapSource mBitmap; - private int mWidth; - private int mHeight; - private String mResult; } diff --git a/core/src/com/google/zxing/upc/UPCReader.java b/core/src/com/google/zxing/upc/UPCReader.java index d92e8c44..ce165ac1 100755 --- a/core/src/com/google/zxing/upc/UPCReader.java +++ b/core/src/com/google/zxing/upc/UPCReader.java @@ -52,4 +52,5 @@ public final class UPCReader implements Reader { } return new Result(result, NO_POINTS); } + } diff --git a/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java b/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java index 62058340..2baf6f01 100644 --- a/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java +++ b/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java @@ -51,15 +51,14 @@ public final class CommandLineRunner { } } - private static void decode(URI uri) throws IOException, ReaderException { + private static void decode(URI uri) throws IOException { BufferedImage image = ImageIO.read(uri.toURL()); if (image == null) { - System.out.println(uri.toString() + ": Could not load image"); + System.err.println(uri.toString() + ": Could not load image"); return; } try { - String result = - new MultiFormatReader().decode(new BufferedImageMonochromeBitmapSource(image)).getText(); + String result = new MultiFormatReader().decode(new BufferedImageMonochromeBitmapSource(image)).getText(); System.out.println(uri.toString() + ": " + result); } catch (ReaderException e) { System.out.println(uri.toString() + ": No barcode found");