X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=cpp%2Fcore%2Fsrc%2Fzxing%2Foned%2FUPCEANReader.cpp;fp=cpp%2Fcore%2Fsrc%2Fzxing%2Foned%2FUPCEANReader.cpp;h=133bd47636e614e08f7601bddf57c5b987ce4be7;hb=1a2f46fa71c381cd112134a115448625bc3fe3f9;hp=ef143b85ac71b682acfc76d8af4fae5b56754f78;hpb=24278b097c9c874c4b2758f1eac2e41a975c8d98;p=zxing.git diff --git a/cpp/core/src/zxing/oned/UPCEANReader.cpp b/cpp/core/src/zxing/oned/UPCEANReader.cpp index ef143b85..133bd476 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."); - } - - 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; + } + + 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; } - 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(){ + } + } }