X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=core%2Fsrc%2Fcom%2Fgoogle%2Fzxing%2Foned%2FAbstractUPCEANReader.java;h=5336e0dc306afcec333cea5fab00967367f5f565;hb=a96a22b731c6402277a70ca54ccec32088adbfbb;hp=ea5f11e7820f4bd2ed6e4edf06092018460e9028;hpb=513e49c662f3caa0934fbed84b296c9c8e00f819;p=zxing.git diff --git a/core/src/com/google/zxing/oned/AbstractUPCEANReader.java b/core/src/com/google/zxing/oned/AbstractUPCEANReader.java index ea5f11e7..5336e0dc 100644 --- a/core/src/com/google/zxing/oned/AbstractUPCEANReader.java +++ b/core/src/com/google/zxing/oned/AbstractUPCEANReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2008 Google Inc. + * Copyright 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,32 +16,42 @@ package com.google.zxing.oned; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.ResultPoint; -import com.google.zxing.ReaderException; import com.google.zxing.common.BitArray; import com.google.zxing.common.GenericResultPoint; +import java.util.Hashtable; + /** *

Encapsulates functionality and implementation that is common to UPC and EAN families * of one-dimensional barcodes.

* * @author dswitkin@google.com (Daniel Switkin) - * @author srowen@google.com (Sean Owen) + * @author Sean Owen * @author alasdair@google.com (Alasdair Mackintosh) */ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements UPCEANReader { - private static final float MAX_VARIANCE = 0.4f; + 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.7f); - /** Start/end guard pattern. */ - protected static final int[] START_END_PATTERN = {1, 1, 1,}; + /** + * Start/end guard pattern. + */ + private static final int[] START_END_PATTERN = {1, 1, 1,}; - /** Pattern marking the middle of a UPC/EAN pattern, separating the two halves. */ - protected static final int[] MIDDLE_PATTERN = {1, 1, 1, 1, 1}; + /** + * Pattern marking the middle of a UPC/EAN pattern, separating the two halves. + */ + static final int[] MIDDLE_PATTERN = {1, 1, 1, 1, 1}; - /** "Odd", or "L" patterns used to encode UPC/EAN digits. */ - protected static final int[][] L_PATTERNS = { + /** + * "Odd", or "L" patterns used to encode UPC/EAN digits. + */ + static final int[][] L_PATTERNS = { {3, 2, 1, 1}, // 0 {2, 2, 2, 1}, // 1 {2, 1, 2, 2}, // 2 @@ -54,8 +64,10 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements {3, 1, 1, 2} // 9 }; - /** As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. */ - protected static final int[][] L_AND_G_PATTERNS; + /** + * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. + */ + static final int[][] L_AND_G_PATTERNS; static { L_AND_G_PATTERNS = new int[20][]; @@ -72,7 +84,13 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements } } - static int[] findStartGuardPattern(final BitArray row) throws ReaderException { + private final StringBuffer decodeRowStringBuffer; + + protected AbstractUPCEANReader() { + decodeRowStringBuffer = new StringBuffer(20); + } + + static int[] findStartGuardPattern(BitArray row) throws ReaderException { boolean foundStart = false; int[] startRange = null; int nextStart = 0; @@ -80,39 +98,57 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN); int start = startRange[0]; nextStart = startRange[1]; - // As a check, we want to see some white in front of this "start pattern", - // maybe as wide as the start pattern itself? - foundStart = row.isRange(Math.max(0, start - 2 * (startRange[1] - start)), start, false); + // Make sure there is a quiet zone at least as big as the start pattern before the barcode. If + // this check would run off the left edge of the image, do not accept this barcode, as it is + // very likely to be a false positive. + int quietStart = start - (nextStart - start); + if (quietStart >= 0) { + foundStart = row.isRange(quietStart, start, false); + } } return startRange; } - public final Result decodeRow(int rowNumber, BitArray row) throws ReaderException { + public final Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException { return decodeRow(rowNumber, row, findStartGuardPattern(row)); } public final Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange) throws ReaderException { - - StringBuffer result = new StringBuffer(); - + StringBuffer result = decodeRowStringBuffer; + result.setLength(0); int endStart = decodeMiddle(row, startGuardRange, result); - int[] endRange = decodeEnd(row, endStart); - // Check for whitespace after the pattern + // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The + // spec might want more whitespace, but in practice this is the maximum we can count on. int end = endRange[1]; - if (!row.isRange(end, Math.min(row.getSize(), end + 2 * (end - endRange[0])), false)) { - throw new ReaderException("Pattern not followed by whitespace"); + int quietEnd = end + (end - endRange[0]); + if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false)) { + throw ReaderException.getInstance(); } String resultString = result.toString(); if (!checkChecksum(resultString)) { - throw new ReaderException("Checksum failed"); + throw ReaderException.getInstance(); } - return new Result(resultString, new ResultPoint[]{ - new GenericResultPoint((float) (startGuardRange[1] - startGuardRange[0]) / 2.0f, (float) rowNumber), - new GenericResultPoint((float) (endRange[1] - endRange[0]) / 2.0f, (float) rowNumber)}); + float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f; + float right = (float) (endRange[1] + endRange[0]) / 2.0f; + return new Result(resultString, + null, // no natural byte representation for these barcodes + new ResultPoint[]{ + new GenericResultPoint(left, (float) rowNumber), + new GenericResultPoint(right, (float) rowNumber)}, + getBarcodeFormat()); + } + + abstract BarcodeFormat getBarcodeFormat(); + + /** + * @return {@link #checkStandardUPCEANChecksum(String)} + */ + boolean checkChecksum(String s) throws ReaderException { + return checkStandardUPCEANChecksum(s); } /** @@ -123,13 +159,17 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements * @return true iff string of digits passes the UPC/EAN checksum algorithm * @throws ReaderException if the string does not contain only digits */ - protected boolean checkChecksum(String s) throws ReaderException { - int sum = 0; + public static boolean checkStandardUPCEANChecksum(String s) throws ReaderException { int length = s.length(); + if (length == 0) { + return false; + } + + int sum = 0; for (int i = length - 2; i >= 0; i -= 2) { int digit = (int) s.charAt(i) - (int) '0'; if (digit < 0 || digit > 9) { - throw new ReaderException("Illegal character during checksum"); + throw ReaderException.getInstance(); } sum += digit; } @@ -137,7 +177,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements for (int i = length - 1; i >= 0; i -= 2) { int digit = (int) s.charAt(i) - (int) '0'; if (digit < 0 || digit > 9) { - throw new ReaderException("Illegal character during checksum"); + throw ReaderException.getInstance(); } sum += digit; } @@ -156,7 +196,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements protected abstract int decodeMiddle(BitArray row, int[] startRange, StringBuffer resultString) throws ReaderException; - protected int[] decodeEnd(BitArray row, int endStart) throws ReaderException { + int[] decodeEnd(BitArray row, int endStart) throws ReaderException { return findGuardPattern(row, endStart, false, START_END_PATTERN); } @@ -164,13 +204,13 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements * @param row row of black/white values to search * @param rowOffset position to start search * @param whiteFirst if true, indicates that the pattern specifies white/black/white/... - * pixel counts, otherwise, it is interpreted as black/white/black/... + * pixel counts, otherwise, it is interpreted as black/white/black/... * @param pattern pattern of counts of number of black and white pixels that are being - * searched for as a pattern + * searched for as a pattern * @return start/end horizontal offset of guard pattern, as an array of two ints * @throws ReaderException if pattern is not found */ - protected static int[] findGuardPattern(BitArray row, int rowOffset, boolean whiteFirst, int[] pattern) + static int[] findGuardPattern(BitArray row, int rowOffset, boolean whiteFirst, int[] pattern) throws ReaderException { int patternLength = pattern.length; int[] counters = new int[patternLength]; @@ -192,13 +232,15 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { - if (patternMatchVariance(counters, pattern) < MAX_VARIANCE) { - return new int[] {patternStart, x}; + if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { + return new int[]{patternStart, x}; } patternStart += counters[0] + counters[1]; for (int y = 2; y < patternLength; y++) { counters[y - 2] = counters[y]; } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; @@ -207,7 +249,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements isWhite = !isWhite; } } - throw new ReaderException("Can't find pattern"); + throw ReaderException.getInstance(); } /** @@ -217,30 +259,29 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements * @param counters the counts of runs of observed black/white/black/... values * @param rowOffset horizontal offset to start decoding from * @param patterns the set of patterns to use to decode -- sometimes different encodings - * for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should - * be used + * for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should + * be used * @return horizontal offset of first pixel beyond the decoded digit * @throws ReaderException if digit cannot be decoded */ - protected static int decodeDigit(BitArray row, - int[] counters, - int rowOffset, - int[][] patterns) throws ReaderException { + static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns) + throws ReaderException { recordPattern(row, rowOffset, counters); - float bestVariance = MAX_VARIANCE; // worst variance we'll accept + int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept int bestMatch = -1; - for (int d = 0; d < patterns.length; d++) { - int[] pattern = patterns[d]; - float variance = patternMatchVariance(counters, pattern); + int max = patterns.length; + for (int i = 0; i < max; i++) { + int[] pattern = patterns[i]; + int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); if (variance < bestVariance) { bestVariance = variance; - bestMatch = d; + bestMatch = i; } } if (bestMatch >= 0) { return bestMatch; } else { - throw new ReaderException("Could not match any digit in pattern"); + throw ReaderException.getInstance(); } }