diff --git a/cpp/core/src/zxing/MultiFormatReader.cpp b/cpp/core/src/zxing/MultiFormatReader.cpp index 66b4b1329..2132fcb6b 100644 --- a/cpp/core/src/zxing/MultiFormatReader.cpp +++ b/cpp/core/src/zxing/MultiFormatReader.cpp @@ -27,16 +27,16 @@ #include namespace zxing { - MultiFormatReader::MultiFormatReader() { + MultiFormatReader::MultiFormatReader() { - } - - Ref MultiFormatReader::decode(Ref image) { - setHints(DecodeHints::DEFAULT_HINT); - return decodeInternal(image); - } + } + + Ref MultiFormatReader::decode(Ref image) { + setHints(DecodeHints::DEFAULT_HINT); + return decodeInternal(image); + } - Ref MultiFormatReader::decode(Ref image, DecodeHints hints) { + Ref MultiFormatReader::decode(Ref image, DecodeHints hints) { setHints(hints); return decodeInternal(image); } @@ -62,43 +62,41 @@ namespace zxing { hints.containsFormat(BarcodeFormat_CODE_39) || hints.containsFormat(BarcodeFormat_ITF); if (addOneDReader && !tryHarder) { - readers_.push_back(new zxing::oned::MultiFormatOneDReader(hints)); + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); } if (hints.containsFormat(BarcodeFormat_QR_CODE)) { - readers_.push_back(new zxing::qrcode::QRCodeReader()); + readers_.push_back(Ref(new zxing::qrcode::QRCodeReader())); } if (hints.containsFormat(BarcodeFormat_DATA_MATRIX)) { - readers_.push_back(new zxing::datamatrix::DataMatrixReader()); + readers_.push_back(Ref(new zxing::datamatrix::DataMatrixReader())); } //TODO: add PDF417 here once PDF417 reader is implemented if (addOneDReader && tryHarder) { - readers_.push_back(new zxing::oned::MultiFormatOneDReader(hints)); + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); } if (readers_.size() == 0) { if (!tryHarder) { - readers_.push_back(new zxing::oned::MultiFormatOneDReader(hints)); + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); } - readers_.push_back(new zxing::qrcode::QRCodeReader()); + readers_.push_back(Ref(new zxing::qrcode::QRCodeReader())); if (tryHarder) { - readers_.push_back(new zxing::oned::MultiFormatOneDReader(hints)); + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); } } } - Ref MultiFormatReader::decodeInternal(Ref image) { - for (unsigned int i = 0; i < readers_.size(); i++) { - try { - return readers_[i]->decode(image, hints_); - } catch (ReaderException re) { - // continue - } - } - throw ReaderException("No code detected"); - } - - MultiFormatReader::~MultiFormatReader(){ - for (unsigned int i = 0; i < readers_.size(); i++) { - delete readers_[i]; - } - } + Ref MultiFormatReader::decodeInternal(Ref image) { + for (unsigned int i = 0; i < readers_.size(); i++) { + try { + return readers_[i]->decode(image, hints_); + } catch (ReaderException re) { + // continue + } + } + throw ReaderException("No code detected"); + } + + MultiFormatReader::~MultiFormatReader() { + + } } diff --git a/cpp/core/src/zxing/MultiFormatReader.h b/cpp/core/src/zxing/MultiFormatReader.h index 53d6b57de..9411cfbf1 100644 --- a/cpp/core/src/zxing/MultiFormatReader.h +++ b/cpp/core/src/zxing/MultiFormatReader.h @@ -27,23 +27,23 @@ #include namespace zxing { - class MultiFormatReader : public Reader { - - private: - Ref decodeInternal(Ref image); - - std::vector readers_; - DecodeHints hints_; + class MultiFormatReader : public Reader { + + private: + Ref decodeInternal(Ref image); + + std::vector > readers_; + DecodeHints hints_; - public: - MultiFormatReader(); - - Ref decode(Ref image); - Ref decode(Ref image, DecodeHints hints); + public: + MultiFormatReader(); + + Ref decode(Ref image); + Ref decode(Ref image, DecodeHints hints); Ref decodeWithState(Ref image); void setHints(DecodeHints hints); - ~MultiFormatReader(); - }; + ~MultiFormatReader(); + }; } #endif diff --git a/cpp/core/src/zxing/common/GlobalHistogramBinarizer.cpp b/cpp/core/src/zxing/common/GlobalHistogramBinarizer.cpp index d30f1a11b..428a47a15 100644 --- a/cpp/core/src/zxing/common/GlobalHistogramBinarizer.cpp +++ b/cpp/core/src/zxing/common/GlobalHistogramBinarizer.cpp @@ -55,35 +55,41 @@ Ref GlobalHistogramBinarizer::getBlackRow(int y, Ref row) { } //TODO(flyashi): cache this instead of allocating and deleting per row - unsigned char* row_pixels = new unsigned char[width]; - getLuminanceSource()->getRow(y,row_pixels); - for (int x = 0; x < width; x++) { - histogram[row_pixels[x] >> LUMINANCE_SHIFT]++; + unsigned char* row_pixels = NULL; + try { + row_pixels = new unsigned char[width]; + getLuminanceSource()->getRow(y,row_pixels); + for (int x = 0; x < width; x++) { + histogram[row_pixels[x] >> LUMINANCE_SHIFT]++; + } + int blackPoint = estimate(histogram) << LUMINANCE_SHIFT; + + + Ref array_ref(new BitArray(width)); + BitArray& array = *array_ref; + + int left = row_pixels[0]; + int center = row_pixels[1]; + for (int x = 1; x < width - 1; x++) { + int right = row_pixels[x + 1]; + // A simple -1 4 -1 box filter with a weight of 2. + int luminance = ((center << 2) - left - right) >> 1; + if (luminance < blackPoint) { + array.set(x); + } + left = center; + center = right; + } + + cached_row_ = array_ref; + cached_row_num_ = y; + + delete [] row_pixels; + return array_ref; + } catch (IllegalArgumentException const& iae) { + delete [] row_pixels; + throw iae; } - int blackPoint = estimate(histogram) << LUMINANCE_SHIFT; - - - Ref array_ref(new BitArray(width)); - BitArray& array = *array_ref; - - int left = row_pixels[0]; - int center = row_pixels[1]; - for (int x = 1; x < width - 1; x++) { - int right = row_pixels[x + 1]; - // A simple -1 4 -1 box filter with a weight of 2. - int luminance = ((center << 2) - left - right) >> 1; - if (luminance < blackPoint) { - array.set(x); - } - left = center; - center = right; - } - - cached_row_ = array_ref; - cached_row_num_ = y; - - delete [] row_pixels; - return array_ref; } Ref GlobalHistogramBinarizer::getBlackMatrix() { diff --git a/cpp/core/src/zxing/oned/Code39Reader.cpp b/cpp/core/src/zxing/oned/Code39Reader.cpp index 337f23728..9a32f5175 100644 --- a/cpp/core/src/zxing/oned/Code39Reader.cpp +++ b/cpp/core/src/zxing/oned/Code39Reader.cpp @@ -75,112 +75,108 @@ namespace oned { } + Code39Reader::Code39Reader(bool usingCheckDigit_, bool extendedMode_) : + alphabet_string(ALPHABET_STRING), + usingCheckDigit(usingCheckDigit_), + extendedMode(extendedMode_) { + } Ref Code39Reader::decodeRow(int rowNumber, Ref row){ - int* start = findAsteriskPattern(row); - int nextStart = start[1]; - int end = row->getSize(); + int* start = NULL; + try { + start = findAsteriskPattern(row); + int nextStart = start[1]; + int end = row->getSize(); - // Read off white space - while (nextStart < end && !row->get(nextStart)) { - nextStart++; - } - - std::string tmpResultString; - - int countersLen = 9; - int* counters = new int[countersLen]; - for (int i=0; iget(nextStart)) { nextStart++; } - } while (decodedChar != '*'); - tmpResultString.erase(tmpResultString.length()-1, 1);// remove asterisk - // Look for whitespace after pattern: - int lastPatternSize = 0; - for (int i = 0; i < countersLen; i++) { - lastPatternSize += counters[i]; - } - // IS begin - delete [] counters; - // IS end - int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; - // If 50% of last pattern size, following last pattern, is not whitespace, - // fail (but if it's whitespace to the very end of the image, that's OK) - if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) { - delete [] start; - throw ReaderException("too short end white space"); - } + std::string tmpResultString; - if (usingCheckDigit) { - int max = tmpResultString.length() - 1; - unsigned int total = 0; - for (int i = 0; i < max; i++) { - total += alphabet_string.find_first_of(tmpResultString[i], 0); + const int countersLen = 9; + int counters[countersLen]; + for (int i = 0; i < countersLen; i++) { + counters[i] = 0; } - if (total % 43 != alphabet_string.find_first_of(tmpResultString[max], 0)) { + char decodedChar; + int lastStart; + do { + recordPattern(row, nextStart, counters, countersLen); + int pattern = toNarrowWidePattern(counters, countersLen); + if (pattern < 0) { + throw ReaderException("pattern < 0"); + } + decodedChar = patternToChar(pattern); + tmpResultString.append(1, decodedChar); + lastStart = nextStart; + for (int i = 0; i < countersLen; i++) { + nextStart += counters[i]; + } + // Read off white space + while (nextStart < end && !row->get(nextStart)) { + nextStart++; + } + } while (decodedChar != '*'); + tmpResultString.erase(tmpResultString.length()-1, 1);// remove asterisk + + // Look for whitespace after pattern: + int lastPatternSize = 0; + for (int i = 0; i < countersLen; i++) { + lastPatternSize += counters[i]; + } + int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; + // If 50% of last pattern size, following last pattern, is not whitespace, + // fail (but if it's whitespace to the very end of the image, that's OK) + if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) { + throw ReaderException("too short end white space"); + } + + if (usingCheckDigit) { + int max = tmpResultString.length() - 1; + unsigned int total = 0; + for (int i = 0; i < max; i++) { + total += alphabet_string.find_first_of(tmpResultString[i], 0); + } + if (total % 43 != alphabet_string.find_first_of(tmpResultString[max], 0)) { + throw ReaderException(""); + } + tmpResultString.erase(max, 1); + } + + Ref resultString(new String(tmpResultString)); + if (extendedMode) { + resultString = decodeExtended(tmpResultString); + } + + if (tmpResultString.length() == 0) { + // Almost surely a false positive throw ReaderException(""); } - tmpResultString.erase(max, 1); - } + float left = (float) (start[1] + start[0]) / 2.0f; + float right = (float) (nextStart + lastStart) / 2.0f; + std::vector< Ref > resultPoints(2); + Ref resultPoint1( + new OneDResultPoint(left, (float) rowNumber)); + Ref resultPoint2( + new OneDResultPoint(right, (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + ArrayRef resultBytes(1); - Ref resultString(new String(tmpResultString)); - if (extendedMode) { - delete resultString; - resultString = decodeExtended(tmpResultString); - } + Ref res(new Result( + resultString, resultBytes, resultPoints, BarcodeFormat_CODE_39)); - if (tmpResultString.length() == 0) { delete [] start; - // Almost surely a false positive - throw ReaderException(""); + return res; + } catch (ReaderException const& re) { + delete [] start; + throw re; } - - float left = (float) (start[1] + start[0]) / 2.0f; - float right = (float) (nextStart + lastStart) / 2.0f; - - std::vector< Ref > resultPoints(2); - Ref resultPoint1( - new OneDResultPoint(left, (float) rowNumber)); - Ref resultPoint2( - new OneDResultPoint(right, (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - ArrayRef resultBytes(1); - - delete [] start; - - Ref res(new Result( - resultString, resultBytes, resultPoints, BarcodeFormat_CODE_39)); - return res; } int* Code39Reader::findAsteriskPattern(Ref row){ @@ -194,9 +190,9 @@ namespace oned { } int counterPosition = 0; - int countersLen = 9; - int* counters = new int[countersLen]; - for (int i=0; i namespace zxing { - namespace oned { - - static const int FIRST_DIGIT_ENCODINGS[10] = {0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}; - - - EAN13Reader::EAN13Reader() { } - - int EAN13Reader::decodeMiddle(Ref row, int startRange[], int startRangeLen, std::string& resultString){ - const int countersLen = 4; - int counters[countersLen] = { 0, 0, 0, 0 }; - - - int end = row->getSize(); - int rowOffset = startRange[1]; - - int lgPatternFound = 0; - - for (int x = 0; x < 6 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_AND_G_PATTERNS); - resultString.append(1, (char) ('0' + bestMatch % 10)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - if (bestMatch >= 10) { - lgPatternFound |= 1 << (5 - x); - } - } - - determineFirstDigit(resultString, lgPatternFound); - - int* middleRange = findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), getMIDDLE_PATTERN_LEN()); - rowOffset = middleRange[1]; - - for (int x = 0; x < 6 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS); - resultString.append(1, (char) ('0' + bestMatch)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - } - - return rowOffset; - } - - void EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound){ - for (int d = 0; d < 10; d++) { - if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) { - resultString.insert(0, 1, (char) ('0' + d)); - return; - } - } - throw ReaderException("determineFirstDigit"); - } - - BarcodeFormat EAN13Reader::getBarcodeFormat(){ - return BarcodeFormat_EAN_13; - } - } + namespace oned { + + static const int FIRST_DIGIT_ENCODINGS[10] = {0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}; + + + EAN13Reader::EAN13Reader() { } + + int EAN13Reader::decodeMiddle(Ref row, int startRange[], int startRangeLen, std::string& resultString){ + const int countersLen = 4; + int counters[countersLen] = { 0, 0, 0, 0 }; + + + int end = row->getSize(); + int rowOffset = startRange[1]; + + int lgPatternFound = 0; + + for (int x = 0; x < 6 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_AND_G_PATTERNS); + resultString.append(1, (char) ('0' + bestMatch % 10)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + if (bestMatch >= 10) { + lgPatternFound |= 1 << (5 - x); + } + } + + determineFirstDigit(resultString, lgPatternFound); + + int* middleRange = 0; + try { + middleRange = findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), getMIDDLE_PATTERN_LEN()); + rowOffset = middleRange[1]; + + for (int x = 0; x < 6 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS); + resultString.append(1, (char) ('0' + bestMatch)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + } + + delete [] middleRange; + return rowOffset; + } catch (ReaderException const& re) { + delete [] middleRange; + throw re; + } + } + + void EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound){ + for (int d = 0; d < 10; d++) { + if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) { + resultString.insert(0, 1, (char) ('0' + d)); + return; + } + } + throw ReaderException("determineFirstDigit"); + } + + BarcodeFormat EAN13Reader::getBarcodeFormat(){ + return BarcodeFormat_EAN_13; + } + } } diff --git a/cpp/core/src/zxing/oned/EAN8Reader.cpp b/cpp/core/src/zxing/oned/EAN8Reader.cpp index e3c0e2a7e..f54d0cfba 100644 --- a/cpp/core/src/zxing/oned/EAN8Reader.cpp +++ b/cpp/core/src/zxing/oned/EAN8Reader.cpp @@ -22,41 +22,48 @@ #include namespace zxing { - namespace oned { - - EAN8Reader::EAN8Reader(){ } - - int EAN8Reader::decodeMiddle(Ref row, int startRange[], int startRangeLen, std::string& resultString){ - const int countersLen = 4; - int counters[countersLen] = { 0, 0, 0, 0 }; - - int end = row->getSize(); - int rowOffset = startRange[1]; - - for (int x = 0; x < 4 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS); - resultString.append(1, (char) ('0' + bestMatch)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - } - - int* middleRange = findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), getMIDDLE_PATTERN_LEN()); - rowOffset = middleRange[1]; - - for (int x = 0; x < 4 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS); - resultString.append(1, (char) ('0' + bestMatch)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - } - - return rowOffset; - } - - BarcodeFormat EAN8Reader::getBarcodeFormat(){ - return BarcodeFormat_EAN_8; - } - } + namespace oned { + + EAN8Reader::EAN8Reader(){ } + + int EAN8Reader::decodeMiddle(Ref row, int startRange[], int startRangeLen, std::string& resultString){ + const int countersLen = 4; + int counters[countersLen] = { 0, 0, 0, 0 }; + + int end = row->getSize(); + int rowOffset = startRange[1]; + + for (int x = 0; x < 4 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS); + resultString.append(1, (char) ('0' + bestMatch)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + } + + int* middleRange = 0; + try { + middleRange = findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), getMIDDLE_PATTERN_LEN()); + rowOffset = middleRange[1]; + + for (int x = 0; x < 4 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS); + resultString.append(1, (char) ('0' + bestMatch)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + } + + delete [] middleRange; + return rowOffset; + } catch (ReaderException const& re) { + delete [] middleRange; + throw re; + } + } + + BarcodeFormat EAN8Reader::getBarcodeFormat(){ + return BarcodeFormat_EAN_8; + } + } } diff --git a/cpp/core/src/zxing/oned/ITFReader.cpp b/cpp/core/src/zxing/oned/ITFReader.cpp index 3848d6e16..0a45bd55c 100644 --- a/cpp/core/src/zxing/oned/ITFReader.cpp +++ b/cpp/core/src/zxing/oned/ITFReader.cpp @@ -25,340 +25,341 @@ #include namespace zxing { - namespace oned { - - static const int W = 3; // Pixel width of a wide line - static const int N = 1; // Pixed width of a narrow line - - const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 }; - - /** - * Start/end guard pattern. - * - * Note: The end pattern is reversed because the row is reversed before - * searching for the END_PATTERN - */ - static const int START_PATTERN_LEN = 4; - static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N}; - - static const int END_PATTERN_REVERSED_LEN = 3; - static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W}; - - /** - * Patterns of Wide / Narrow lines to indicate each digit - */ - static const int PATTERNS_LEN = 10; - static const int PATTERNS[PATTERNS_LEN][5] = { - {N, N, W, W, N}, // 0 - {W, N, N, N, W}, // 1 - {N, W, N, N, W}, // 2 - {W, W, N, N, N}, // 3 - {N, N, W, N, W}, // 4 - {W, N, W, N, N}, // 5 - {N, W, W, N, N}, // 6 - {N, N, N, W, W}, // 7 - {W, N, N, W, N}, // 8 - {N, W, N, W, N} // 9 - }; - - - ITFReader::ITFReader() : narrowLineWidth(-1) { - } - - - Ref ITFReader::decodeRow(int rowNumber, Ref row){ - // Find out where the Middle section (payload) starts & ends - int* startRange = decodeStart(row); - int* endRange; - try { - endRange = decodeEnd(row); - } catch (ReaderException re) { - delete [] startRange; - throw re; - } - - std::string tmpResult; - try { - decodeMiddle(row, startRange[1], endRange[0], tmpResult); - } catch (zxing::ReaderException re) { - delete [] startRange; - delete [] endRange; - throw re; - } - - // To avoid false positives with 2D barcodes (and other patterns), make - // an assumption that the decoded string must be 6, 10 or 14 digits. - int length = tmpResult.length(); - bool lengthOK = false; - if (length == 6 || length == 10 || length == 14) { - lengthOK = true; - } - if (!lengthOK) { - delete [] startRange; - delete [] endRange; - throw ReaderException("not enough characters count"); - } - - Ref resultString(new String(tmpResult)); - - std::vector< Ref > resultPoints(2); - Ref resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber)); - Ref resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - ArrayRef resultBytes(1); - - delete [] startRange; - delete [] endRange; - - Ref res(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF)); - return res; - } - - /** - * @param row row of black/white values to search - * @param payloadStart offset of start pattern - * @param resultString {@link StringBuffer} to append decoded chars to - * @throws ReaderException if decoding could not complete successfully - */ - void ITFReader::decodeMiddle(Ref row, int payloadStart, int payloadEnd, std::string& resultString){ - // Digits are interleaved in pairs - 5 black lines for one digit, and the - // 5 - // interleaved white lines for the second digit. - // Therefore, need to scan 10 lines and then - // split these into two arrays - int counterDigitPairLen = 10; - int counterDigitPair[counterDigitPairLen]; - for (int i=0; i row){ - int endStart = skipWhiteSpace(row); -/// static int* findGuardPattern(Ref row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen); - int* startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN); - - // Determine the width of a narrow line in pixels. We can do this by - // getting the width of the start pattern and dividing by 4 because its - // made up of 4 narrow lines. - narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2; - - validateQuietZone(row, startPattern[0]); - - return startPattern; - } - - /** - * Identify where the end of the middle / payload section ends. - * - * @param row row of black/white values to search - * @return Array, containing index of start of 'end block' and end of 'end - * block' - * @throws ReaderException - */ - - int* ITFReader::decodeEnd(Ref row){ - // For convenience, reverse the row and then - // search from 'the start' for the end block - row->reverse(); - try { - int endStart = skipWhiteSpace(row); - int* endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN); - - // The start & end patterns must be pre/post fixed by a quiet zone. This - // zone must be at least 10 times the width of a narrow line. - // ref: http://www.barcode-1.net/i25code.html - validateQuietZone(row, endPattern[0]); - - // Now recalculate the indices of where the 'endblock' starts & stops to - // accommodate - // the reversed nature of the search - int temp = endPattern[0]; - endPattern[0] = row->getSize() - endPattern[1]; - endPattern[1] = row->getSize() - temp; - - row->reverse(); - return endPattern; - } catch (ReaderException re) { - row->reverse(); - throw re; - } - } - - /** - * The start & end patterns must be pre/post fixed by a quiet zone. This - * zone must be at least 10 times the width of a narrow line. Scan back until - * we either get to the start of the barcode or match the necessary number of - * quiet zone pixels. - * - * Note: Its assumed the row is reversed when using this method to find - * quiet zone after the end pattern. - * - * ref: http://www.barcode-1.net/i25code.html - * - * @param row bit array representing the scanned barcode. - * @param startPattern index into row of the start or end pattern. - * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown. - */ - void ITFReader::validateQuietZone(Ref row, int startPattern){ + namespace oned { + + static const int W = 3; // Pixel width of a wide line + static const int N = 1; // Pixed width of a narrow line + + const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 }; + + /** + * Start/end guard pattern. + * + * Note: The end pattern is reversed because the row is reversed before + * searching for the END_PATTERN + */ + static const int START_PATTERN_LEN = 4; + static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N}; + + static const int END_PATTERN_REVERSED_LEN = 3; + static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W}; + + /** + * Patterns of Wide / Narrow lines to indicate each digit + */ + static const int PATTERNS_LEN = 10; + static const int PATTERNS[PATTERNS_LEN][5] = { + {N, N, W, W, N}, // 0 + {W, N, N, N, W}, // 1 + {N, W, N, N, W}, // 2 + {W, W, N, N, N}, // 3 + {N, N, W, N, W}, // 4 + {W, N, W, N, N}, // 5 + {N, W, W, N, N}, // 6 + {N, N, N, W, W}, // 7 + {W, N, N, W, N}, // 8 + {N, W, N, W, N} // 9 + }; + + + ITFReader::ITFReader() : narrowLineWidth(-1) { + } + + + Ref ITFReader::decodeRow(int rowNumber, Ref row){ + int* startRange = 0; + int* endRange = 0; + try { + // Find out where the Middle section (payload) starts & ends + startRange = decodeStart(row); + endRange = decodeEnd(row); + + std::string tmpResult; + decodeMiddle(row, startRange[1], endRange[0], tmpResult); + + // To avoid false positives with 2D barcodes (and other patterns), make + // an assumption that the decoded string must be 6, 10 or 14 digits. + int length = tmpResult.length(); + bool lengthOK = false; + if (length == 6 || length == 10 || length == 14) { + lengthOK = true; + } + if (!lengthOK) { + throw ReaderException("not enough characters count"); + } + + Ref resultString(new String(tmpResult)); + + std::vector< Ref > resultPoints(2); + Ref resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber)); + Ref resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + + ArrayRef resultBytes(1); + + Ref res(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF)); + delete [] startRange; + delete [] endRange; + return res; + } catch (ReaderException re) { + delete [] startRange; + delete [] endRange; + throw re; + } + } + + /** + * @param row row of black/white values to search + * @param payloadStart offset of start pattern + * @param resultString {@link StringBuffer} to append decoded chars to + * @throws ReaderException if decoding could not complete successfully + */ + void ITFReader::decodeMiddle(Ref row, int payloadStart, int payloadEnd, std::string& resultString){ + // Digits are interleaved in pairs - 5 black lines for one digit, and the + // 5 + // interleaved white lines for the second digit. + // Therefore, need to scan 10 lines and then + // split these into two arrays + int counterDigitPairLen = 10; + int counterDigitPair[counterDigitPairLen]; + for (int i=0; i row){ + int endStart = skipWhiteSpace(row); +/// static int* findGuardPattern(Ref row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen); + int* startPattern = 0; + try { + startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN); + + // Determine the width of a narrow line in pixels. We can do this by + // getting the width of the start pattern and dividing by 4 because its + // made up of 4 narrow lines. + narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2; + + validateQuietZone(row, startPattern[0]); + + return startPattern; + } catch (ReaderException re) { + delete [] startPattern; + throw re; + } + } + + /** + * Identify where the end of the middle / payload section ends. + * + * @param row row of black/white values to search + * @return Array, containing index of start of 'end block' and end of 'end + * block' + * @throws ReaderException + */ + + int* ITFReader::decodeEnd(Ref row){ + // For convenience, reverse the row and then + // search from 'the start' for the end block + row->reverse(); + int* endPattern = 0; + try { + int endStart = skipWhiteSpace(row); + endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN); + + // The start & end patterns must be pre/post fixed by a quiet zone. This + // zone must be at least 10 times the width of a narrow line. + // ref: http://www.barcode-1.net/i25code.html + validateQuietZone(row, endPattern[0]); + + // Now recalculate the indices of where the 'endblock' starts & stops to + // accommodate + // the reversed nature of the search + int temp = endPattern[0]; + endPattern[0] = row->getSize() - endPattern[1]; + endPattern[1] = row->getSize() - temp; + + row->reverse(); + return endPattern; + } catch (ReaderException re) { + delete [] endPattern; + row->reverse(); + throw re; + } + } + + /** + * The start & end patterns must be pre/post fixed by a quiet zone. This + * zone must be at least 10 times the width of a narrow line. Scan back until + * we either get to the start of the barcode or match the necessary number of + * quiet zone pixels. + * + * Note: Its assumed the row is reversed when using this method to find + * quiet zone after the end pattern. + * + * ref: http://www.barcode-1.net/i25code.html + * + * @param row bit array representing the scanned barcode. + * @param startPattern index into row of the start or end pattern. + * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown. + */ + void ITFReader::validateQuietZone(Ref row, int startPattern){ //#pragma mark needs some corrections -// int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone -// -// for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { -// if (row->get(i)) { -// break; -// } -// quietCount--; -// } -// if (quietCount != 0) { -// // Unable to find the necessary number of quiet zone pixels. -// throw ReaderException("Unable to find the necessary number of quiet zone pixels"); -// } - } - - /** - * Skip all whitespace until we get to the first black line. - * - * @param row row of black/white values to search - * @return index of the first black line. - * @throws ReaderException Throws exception if no black lines are found in the row - */ - int ITFReader::skipWhiteSpace(Ref row){ - int width = row->getSize(); - int endStart = 0; - while (endStart < width) { - if (row->get(endStart)) { - break; - } - endStart++; - } - if (endStart == width) { - throw ReaderException(""); - } - return endStart; - } - - /** - * @param row row of black/white values to search - * @param rowOffset position to start search - * @param pattern pattern of counts of number of black and white pixels that are - * being 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 - */ - - int* ITFReader::findGuardPattern(Ref row, int rowOffset, const int pattern[], int patternLen){ - // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be - // merged to a single method. - int patternLength = patternLen; - int counters[patternLength]; - for (int i=0; igetSize(); - bool isWhite = false; - - int counterPosition = 0; - int patternStart = rowOffset; - for (int x = rowOffset; x < width; x++) { - bool pixel = row->get(x); - if (pixel ^ isWhite) { - counters[counterPosition]++; - } else { - if (counterPosition == patternLength - 1) { - if (patternMatchVariance(counters, patternLength, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { - int* resultValue = new int[2]; - resultValue[0] = patternStart; - resultValue[1] = x; - return resultValue; - } - 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++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw ReaderException(""); - } - - /** - * Attempts to decode a sequence of ITF black/white lines into single - * digit. - * - * @param counters the counts of runs of observed black/white/black/... values - * @return The decoded digit - * @throws ReaderException if digit cannot be decoded - */ - int ITFReader::decodeDigit(int counters[], int countersLen){ - unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept - int bestMatch = -1; - int max = PATTERNS_LEN; - for (int i = 0; i < max; i++) { - int pattern[countersLen]; - for(int ind = 0; ind= 0) { - return bestMatch; - } else { - throw ReaderException("digit didint found"); - } - } - - - ITFReader::~ITFReader(){ - } - } +// int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone +// +// for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { +// if (row->get(i)) { +// break; +// } +// quietCount--; +// } +// if (quietCount != 0) { +// // Unable to find the necessary number of quiet zone pixels. +// throw ReaderException("Unable to find the necessary number of quiet zone pixels"); +// } + } + + /** + * Skip all whitespace until we get to the first black line. + * + * @param row row of black/white values to search + * @return index of the first black line. + * @throws ReaderException Throws exception if no black lines are found in the row + */ + int ITFReader::skipWhiteSpace(Ref row){ + int width = row->getSize(); + int endStart = 0; + while (endStart < width) { + if (row->get(endStart)) { + break; + } + endStart++; + } + if (endStart == width) { + throw ReaderException(""); + } + return endStart; + } + + /** + * @param row row of black/white values to search + * @param rowOffset position to start search + * @param pattern pattern of counts of number of black and white pixels that are + * being 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 + */ + + int* ITFReader::findGuardPattern(Ref row, int rowOffset, const int pattern[], int patternLen){ + // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be + // merged to a single method. + int patternLength = patternLen; + int counters[patternLength]; + for (int i=0; igetSize(); + bool isWhite = false; + + int counterPosition = 0; + int patternStart = rowOffset; + for (int x = rowOffset; x < width; x++) { + bool pixel = row->get(x); + if (pixel ^ isWhite) { + counters[counterPosition]++; + } else { + if (counterPosition == patternLength - 1) { + if (patternMatchVariance(counters, patternLength, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { + int* resultValue = new int[2]; + resultValue[0] = patternStart; + resultValue[1] = x; + return resultValue; + } + 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++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException(""); + } + + /** + * Attempts to decode a sequence of ITF black/white lines into single + * digit. + * + * @param counters the counts of runs of observed black/white/black/... values + * @return The decoded digit + * @throws ReaderException if digit cannot be decoded + */ + int ITFReader::decodeDigit(int counters[], int countersLen){ + unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = -1; + int max = PATTERNS_LEN; + for (int i = 0; i < max; i++) { + int pattern[countersLen]; + for(int ind = 0; ind= 0) { + return bestMatch; + } else { + throw ReaderException("digit didint found"); + } + } + + + ITFReader::~ITFReader(){ + } + } } diff --git a/cpp/core/src/zxing/oned/UPCEANReader.cpp b/cpp/core/src/zxing/oned/UPCEANReader.cpp index ef143b85a..133bd4763 100644 --- a/cpp/core/src/zxing/oned/UPCEANReader.cpp +++ b/cpp/core/src/zxing/oned/UPCEANReader.cpp @@ -22,308 +22,304 @@ #include #include namespace zxing { - namespace oned { - - /** - * Start/end guard pattern. - */ - static const int START_END_PATTERN[3] = {1, 1, 1}; - - /** - * Pattern marking the middle of a UPC/EAN pattern, separating the two halves. - */ - static const int MIDDLE_PATTERN_LEN = 5; - static const int MIDDLE_PATTERN[MIDDLE_PATTERN_LEN] = {1, 1, 1, 1, 1}; - - /** - * "Odd", or "L" patterns used to encode UPC/EAN digits. - */ - const int L_PATTERNS_LEN = 10; - const int L_PATTERNS_SUB_LEN = 4; - const int L_PATTERNS[10][4] = { - {3, 2, 1, 1}, // 0 - {2, 2, 2, 1}, // 1 - {2, 1, 2, 2}, // 2 - {1, 4, 1, 1}, // 3 - {1, 1, 3, 2}, // 4 - {1, 2, 3, 1}, // 5 - {1, 1, 1, 4}, // 6 - {1, 3, 1, 2}, // 7 - {1, 2, 1, 3}, // 8 - {3, 1, 1, 2} // 9 - }; - - /** - * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. - */ - const int L_AND_G_PATTERNS_LEN = 20; - const int L_AND_G_PATTERNS_SUB_LEN = 4; - const int L_AND_G_PATTERNS[L_AND_G_PATTERNS_LEN][L_AND_G_PATTERNS_SUB_LEN] = { - {3, 2, 1, 1}, // 0 - {2, 2, 2, 1}, // 1 - {2, 1, 2, 2}, // 2 - {1, 4, 1, 1}, // 3 - {1, 1, 3, 2}, // 4 - {1, 2, 3, 1}, // 5 - {1, 1, 1, 4}, // 6 - {1, 3, 1, 2}, // 7 - {1, 2, 1, 3}, // 8 - {3, 1, 1, 2}, // 9 - {1, 1, 2, 3}, // 10 reversed 0 - {1, 2, 2, 2}, // 11 reversed 1 - {2, 2, 1, 2}, // 12 reversed 2 - {1, 1, 4, 1}, // 13 reversed 3 - {2, 3, 1, 1}, // 14 reversed 4 - {1, 3, 2, 1}, // 15 reversed 5 - {4, 1, 1, 1}, // 16 reversed 6 - {2, 1, 3, 1}, // 17 reversed 7 - {3, 1, 2, 1}, // 18 reversed 8 - {2, 1, 1, 3} // 19 reversed 9 - }; - + namespace oned { + + /** + * Start/end guard pattern. + */ + static const int START_END_PATTERN[3] = {1, 1, 1}; + + /** + * Pattern marking the middle of a UPC/EAN pattern, separating the two halves. + */ + static const int MIDDLE_PATTERN_LEN = 5; + static const int MIDDLE_PATTERN[MIDDLE_PATTERN_LEN] = {1, 1, 1, 1, 1}; + + /** + * "Odd", or "L" patterns used to encode UPC/EAN digits. + */ + const int L_PATTERNS_LEN = 10; + const int L_PATTERNS_SUB_LEN = 4; + const int L_PATTERNS[10][4] = { + {3, 2, 1, 1}, // 0 + {2, 2, 2, 1}, // 1 + {2, 1, 2, 2}, // 2 + {1, 4, 1, 1}, // 3 + {1, 1, 3, 2}, // 4 + {1, 2, 3, 1}, // 5 + {1, 1, 1, 4}, // 6 + {1, 3, 1, 2}, // 7 + {1, 2, 1, 3}, // 8 + {3, 1, 1, 2} // 9 + }; + + /** + * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. + */ + const int L_AND_G_PATTERNS_LEN = 20; + const int L_AND_G_PATTERNS_SUB_LEN = 4; + const int L_AND_G_PATTERNS[L_AND_G_PATTERNS_LEN][L_AND_G_PATTERNS_SUB_LEN] = { + {3, 2, 1, 1}, // 0 + {2, 2, 2, 1}, // 1 + {2, 1, 2, 2}, // 2 + {1, 4, 1, 1}, // 3 + {1, 1, 3, 2}, // 4 + {1, 2, 3, 1}, // 5 + {1, 1, 1, 4}, // 6 + {1, 3, 1, 2}, // 7 + {1, 2, 1, 3}, // 8 + {3, 1, 1, 2}, // 9 + {1, 1, 2, 3}, // 10 reversed 0 + {1, 2, 2, 2}, // 11 reversed 1 + {2, 2, 1, 2}, // 12 reversed 2 + {1, 1, 4, 1}, // 13 reversed 3 + {2, 3, 1, 1}, // 14 reversed 4 + {1, 3, 2, 1}, // 15 reversed 5 + {4, 1, 1, 1}, // 16 reversed 6 + {2, 1, 3, 1}, // 17 reversed 7 + {3, 1, 2, 1}, // 18 reversed 8 + {2, 1, 1, 3} // 19 reversed 9 + }; + - const int UPCEANReader::getMIDDLE_PATTERN_LEN(){ - return MIDDLE_PATTERN_LEN; - } - const int* UPCEANReader::getMIDDLE_PATTERN(){ - return MIDDLE_PATTERN; - } - - UPCEANReader::UPCEANReader(){ - } - - - Ref UPCEANReader::decodeRow(int rowNumber, Ref row){ - return decodeRow(rowNumber, row, findStartGuardPattern(row)); - } - Ref UPCEANReader::decodeRow(int rowNumber, Ref row, int startGuardRange[]){ - - std::string tmpResultString; - std::string& tmpResultStringRef = tmpResultString; - int endStart; + const int UPCEANReader::getMIDDLE_PATTERN_LEN(){ + return MIDDLE_PATTERN_LEN; + } + const int* UPCEANReader::getMIDDLE_PATTERN(){ + return MIDDLE_PATTERN; + } + + UPCEANReader::UPCEANReader(){ + } + + + Ref UPCEANReader::decodeRow(int rowNumber, Ref row) { + int* start = NULL; try { - endStart = decodeMiddle(row, startGuardRange, 2 /*reference findGuardPattern*/ , tmpResultStringRef); - } catch (ReaderException re) { - if (startGuardRange!=NULL) { - delete [] startGuardRange; - startGuardRange = NULL; - } + start = findStartGuardPattern(row); + Ref result = decodeRow(rowNumber, row, start); + delete [] start; + return result; + } catch (ReaderException const& re) { + delete [] start; throw re; } - - int* endRange = decodeEnd(row, endStart); - - // 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. - size_t end = endRange[1]; - size_t quietEnd = end + (end - endRange[0]); - if (quietEnd >= row->getSize() || !row->isRange(end, quietEnd, false)) { - throw ReaderException("Quiet zone asserrt fail."); + } + + Ref UPCEANReader::decodeRow(int rowNumber, Ref row, int startGuardRange[]){ + int* endRange = NULL; + try { + std::string tmpResultString; + std::string& tmpResultStringRef = tmpResultString; + int endStart; + endStart = decodeMiddle(row, startGuardRange, 2 /*reference findGuardPattern*/ , tmpResultStringRef); + + endRange = decodeEnd(row, endStart); + + // 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. + size_t end = endRange[1]; + size_t quietEnd = end + (end - endRange[0]); + if (quietEnd >= row->getSize() || !row->isRange(end, quietEnd, false)) { + throw ReaderException("Quiet zone asserrt fail."); + } + + if (!checkChecksum(tmpResultString)) { + throw ReaderException("Checksum fail."); + } + + Ref resultString(new String(tmpResultString)); + + float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f; + float right = (float) (endRange[1] + endRange[0]) / 2.0f; + + std::vector< Ref > resultPoints(2); + Ref resultPoint1(new OneDResultPoint(left, (float) rowNumber)); + Ref resultPoint2(new OneDResultPoint(right, (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + + ArrayRef resultBytes(1); + + Ref res(new Result(resultString, resultBytes, resultPoints, getBarcodeFormat())); + delete [] endRange; + return res; + } catch (ReaderException const& re) { + delete [] endRange; + throw re; } - - if (!checkChecksum(tmpResultString)) { - if (startGuardRange!=NULL) { - delete [] startGuardRange; - startGuardRange = NULL; - } - if (endRange!=NULL) { - delete [] endRange; - endRange = NULL; - } - throw ReaderException("Checksum fail."); - } - - Ref resultString(new String(tmpResultString)); - - float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f; - float right = (float) (endRange[1] + endRange[0]) / 2.0f; - - std::vector< Ref > resultPoints(2); - Ref resultPoint1(new OneDResultPoint(left, (float) rowNumber)); - Ref resultPoint2(new OneDResultPoint(right, (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - ArrayRef resultBytes(1); - - if (startGuardRange!=NULL) { - delete [] startGuardRange; - startGuardRange = NULL; - } - if (endRange!=NULL) { - delete [] endRange; - endRange = NULL; - } - - Ref res(new Result(resultString, resultBytes, resultPoints, getBarcodeFormat())); - return res; - } - - int* UPCEANReader::findStartGuardPattern(Ref row){ - bool foundStart = false; - - int* startRange = NULL; - int nextStart = 0; - while (!foundStart) { - startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN, sizeof(START_END_PATTERN)/sizeof(int)); - int start = startRange[0]; - nextStart = startRange[1]; - // 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); - } - if (!foundStart) { - delete [] startRange; - } - } - return startRange; - } - - int* UPCEANReader::findGuardPattern(Ref row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen){ - int patternLength = patternLen; - - int counters[patternLength]; - int countersCount = sizeof(counters)/sizeof(int); - for (int i=0; igetSize(); - bool isWhite = false; - while (rowOffset < width) { - isWhite = !row->get(rowOffset); - if (whiteFirst == isWhite) { - break; - } - rowOffset++; - } - - int counterPosition = 0; - int patternStart = rowOffset; - for (int x = rowOffset; x < width; x++) { - bool pixel = row->get(x); - if (pixel ^ isWhite) { - counters[counterPosition]++; - } else { - if (counterPosition == patternLength - 1) { - if (patternMatchVariance(counters, countersCount, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { - int* resultValue = new int[2]; - resultValue[0] = patternStart; - resultValue[1] = x; - return resultValue; - } - 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++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw ReaderException("findGuardPattern"); - } - - int* UPCEANReader::decodeEnd(Ref row, int endStart){ - return findGuardPattern(row, endStart, false, START_END_PATTERN, sizeof(START_END_PATTERN)/sizeof(int)); - } - -// int UPCEANReader::decodeDigit(Ref row, int counters[], int countersLen, int rowOffset, int** patterns/*[][]*/, int paterns1Len, int paterns2Len) - int UPCEANReader::decodeDigit(Ref row, int counters[], int countersLen, int rowOffset, UPC_EAN_PATTERNS patternType){ - recordPattern(row, rowOffset, counters, countersLen); - unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept - int bestMatch = -1; - - int max = 0; - switch (patternType) { - case UPC_EAN_PATTERNS_L_PATTERNS: - max = L_PATTERNS_LEN; - for (int i = 0; i < max; i++) { - int pattern[countersLen]; - for(int j = 0; j< countersLen; j++){ - pattern[j] = L_PATTERNS[i][j]; - } - - unsigned int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } - } - break; - case UPC_EAN_PATTERNS_L_AND_G_PATTERNS: - max = L_AND_G_PATTERNS_LEN; - for (int i = 0; i < max; i++) { - int pattern[countersLen]; - for(int j = 0; j< countersLen; j++){ - pattern[j] = L_AND_G_PATTERNS[i][j]; - } - - unsigned int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } - } - break; - default: - break; - } - if (bestMatch >= 0) { - return bestMatch; - } else { - throw ReaderException("UPCEANReader::decodeDigit: No best mach"); - } - } - - - /** - * @return {@link #checkStandardUPCEANChecksum(String)} - */ - bool UPCEANReader::checkChecksum(std::string s){ - return checkStandardUPCEANChecksum(s); - } - - /** - * Computes the UPC/EAN checksum on a string of digits, and reports - * whether the checksum is correct or not. - * - * @param s string of digits to check - * @return true iff string of digits passes the UPC/EAN checksum algorithm - * @throws ReaderException if the string does not contain only digits - */ - bool UPCEANReader::checkStandardUPCEANChecksum(std::string s){ - 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[i] - (int) '0'; - if (digit < 0 || digit > 9) { - throw ReaderException("checkStandardUPCEANChecksum"); - } - sum += digit; - } - sum *= 3; - for (int i = length - 1; i >= 0; i -= 2) { - int digit = (int) s[i] - (int) '0'; - if (digit < 0 || digit > 9) { - throw ReaderException("checkStandardUPCEANChecksum"); - } - sum += digit; - } - return sum % 10 == 0; - } - UPCEANReader::~UPCEANReader(){ - } - } + + } + + int* UPCEANReader::findStartGuardPattern(Ref row){ + bool foundStart = false; + + int* startRange = NULL; + int nextStart = 0; + try { + while (!foundStart) { + delete [] startRange; + startRange = NULL; + startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN, sizeof(START_END_PATTERN)/sizeof(int)); + int start = startRange[0]; + nextStart = startRange[1]; + // 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; + } catch (ReaderException const& re) { + delete [] startRange; + throw re; + } + } + + // TODO(flyashi): Return a pair for return value to avoid using the heap. + int* UPCEANReader::findGuardPattern(Ref row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen){ + int patternLength = patternLen; + int counters[patternLength]; + int countersCount = sizeof(counters) / sizeof(int); + for (int i = 0; i < countersCount; i++) { + counters[i] = 0; + } + int width = row->getSize(); + bool isWhite = false; + while (rowOffset < width) { + isWhite = !row->get(rowOffset); + if (whiteFirst == isWhite) { + break; + } + rowOffset++; + } + + int counterPosition = 0; + int patternStart = rowOffset; + for (int x = rowOffset; x < width; x++) { + bool pixel = row->get(x); + if (pixel ^ isWhite) { + counters[counterPosition]++; + } else { + if (counterPosition == patternLength - 1) { + if (patternMatchVariance(counters, countersCount, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { + int* resultValue = new int[2]; + resultValue[0] = patternStart; + resultValue[1] = x; + return resultValue; + } + 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++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException("findGuardPattern"); + } + + int* UPCEANReader::decodeEnd(Ref row, int endStart){ + return findGuardPattern(row, endStart, false, START_END_PATTERN, sizeof(START_END_PATTERN)/sizeof(int)); + } + +// int UPCEANReader::decodeDigit(Ref row, int counters[], int countersLen, int rowOffset, int** patterns/*[][]*/, int paterns1Len, int paterns2Len) + int UPCEANReader::decodeDigit(Ref row, int counters[], int countersLen, int rowOffset, UPC_EAN_PATTERNS patternType){ + recordPattern(row, rowOffset, counters, countersLen); + unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = -1; + + int max = 0; + switch (patternType) { + case UPC_EAN_PATTERNS_L_PATTERNS: + max = L_PATTERNS_LEN; + for (int i = 0; i < max; i++) { + int pattern[countersLen]; + for(int j = 0; j< countersLen; j++){ + pattern[j] = L_PATTERNS[i][j]; + } + + unsigned int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) { + bestVariance = variance; + bestMatch = i; + } + } + break; + case UPC_EAN_PATTERNS_L_AND_G_PATTERNS: + max = L_AND_G_PATTERNS_LEN; + for (int i = 0; i < max; i++) { + int pattern[countersLen]; + for(int j = 0; j< countersLen; j++){ + pattern[j] = L_AND_G_PATTERNS[i][j]; + } + + unsigned int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) { + bestVariance = variance; + bestMatch = i; + } + } + break; + default: + break; + } + if (bestMatch >= 0) { + return bestMatch; + } else { + throw ReaderException("UPCEANReader::decodeDigit: No best mach"); + } + } + + + /** + * @return {@link #checkStandardUPCEANChecksum(String)} + */ + bool UPCEANReader::checkChecksum(std::string s){ + return checkStandardUPCEANChecksum(s); + } + + /** + * Computes the UPC/EAN checksum on a string of digits, and reports + * whether the checksum is correct or not. + * + * @param s string of digits to check + * @return true iff string of digits passes the UPC/EAN checksum algorithm + * @throws ReaderException if the string does not contain only digits + */ + bool UPCEANReader::checkStandardUPCEANChecksum(std::string s){ + 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[i] - (int) '0'; + if (digit < 0 || digit > 9) { + throw ReaderException("checkStandardUPCEANChecksum"); + } + sum += digit; + } + sum *= 3; + for (int i = length - 1; i >= 0; i -= 2) { + int digit = (int) s[i] - (int) '0'; + if (digit < 0 || digit > 9) { + throw ReaderException("checkStandardUPCEANChecksum"); + } + sum += digit; + } + return sum % 10 == 0; + } + UPCEANReader::~UPCEANReader(){ + } + } }