public BitArray getBlackColumn(int x, BitArray column, int startY, int getHeight) { if (column == null || column.getSize() < getHeight) { column = new BitArray(getHeight); } else { column.clear(); } // Reuse the same int array each time initLuminances(); luminances = getLuminanceColumn(x, luminances); // We don't handle "row sampling" specially here for (int y = 0; y < getHeight; y++) { if (luminances[startY + y] < blackPoint) { column.set(y); } } return(column); }
public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) { if (row == null || row.getSize() < getWidth) { row = new BitArray(getWidth); } else { row.clear(); } // Reuse the same int array each time initLuminances(); luminances = getLuminanceRow(y, luminances); // If the current decoder calculated the blackPoint based on one row, assume we're trying to // decode a 1D barcode, and apply some sharpening. if (lastMethod.Equals(BlackPointEstimationMethod.ROW_SAMPLING)) { int left = luminances[startX]; int center = luminances[startX + 1]; for (int x = 1; x < getWidth - 1; x++) { int right = luminances[startX + x + 1]; // Simple -1 4 -1 box filter with a weight of 2 int luminance = ((center << 2) - left - right) >> 1; if (luminance < blackPoint) { row.set(x); } left = center; center = right; } } else { for (int x = 0; x < getWidth; x++) { if (luminances[startX + x] < blackPoint) { row.set(x); } } } return row; }
protected override int decodeMiddle(BitArray row, int[] startRange, StringBuilder result) { int[] counters = decodeMiddleCounters; counters[0] = 0; counters[1] = 0; counters[2] = 0; counters[3] = 0; int end = row.getSize(); int rowOffset = startRange[1]; for (int x = 0; x < 4 && rowOffset < end; x++) { int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS); result.Append((char) ('0' + bestMatch)); for (int i = 0; i < counters.Length; i++) { rowOffset += counters[i]; } } int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN); rowOffset = middleRange[1]; for (int x = 0; x < 4 && rowOffset < end; x++) { int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS); result.Append((char) ('0' + bestMatch)); for (int i = 0; i < counters.Length; i++) { rowOffset += counters[i]; } } return rowOffset; }
public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) { if (row == null || row.getSize() < getWidth) { row = new BitArray(getWidth); } else { row.clear(); } // Reuse the same int array each time initLuminances(); luminances = getLuminanceRow(y, luminances); // If the current decoder calculated the blackPoint based on one row, assume we're trying to // decode a 1D barcode, and apply some sharpening. if (lastMethod.Equals(BlackPointEstimationMethod.ROW_SAMPLING)) { int left = luminances[startX]; int center = luminances[startX + 1]; for (int x = 1; x < getWidth - 1; x++) { int right = luminances[startX + x + 1]; // Simple -1 4 -1 box filter with a weight of 2 int luminance = ((center << 2) - left - right) >> 1; if (luminance < blackPoint) { row.set(x); } left = center; center = right; } } else { for (int x = 0; x < getWidth; x++) { if (luminances[startX + x] < blackPoint) { row.set(x); } } } return(row); }
protected override int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString) { int[] counters = decodeMiddleCounters; counters[0] = 0; counters[1] = 0; counters[2] = 0; counters[3] = 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, rowOffset, L_AND_G_PATTERNS); resultString.Append((char) ('0' + bestMatch % 10)); for (int i = 0; i < counters.Length; i++) { rowOffset += counters[i]; } if (bestMatch >= 10) { lgPatternFound |= 1 << (5 - x); } } determineFirstDigit(resultString, lgPatternFound); int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN); rowOffset = middleRange[1]; for (int x = 0; x < 6 && rowOffset < end; x++) { int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS); resultString.Append((char) ('0' + bestMatch)); for (int i = 0; i < counters.Length; i++) { rowOffset += counters[i]; } } return rowOffset; }
/** * 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 */ private int skipWhiteSpace(BitArray row) { int width = row.getSize(); int endStart = 0; while (endStart < width) { if (row.get(endStart)) { break; } endStart++; } if (endStart == width) { throw new 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[] findGuardPattern(BitArray row, int rowOffset, int[] pattern) { // TODO: This is very similar to implementation in AbstractUPCEANReader. Consider if they can be merged to // a single method. int patternLength = pattern.Length; int[] counters = new int[patternLength]; int width = row.getSize(); bool isWhite = false; int counterPosition = 0; int patternStart = rowOffset; for (int x = rowOffset; x < width; x++) { bool pixel = row.get(x); if ((!pixel && isWhite) || (pixel && !isWhite)) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { 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++; } counters[counterPosition] = 1; isWhite = !isWhite; } } throw new ReaderException(); }
/** * 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[] decodeEnd(BitArray row) { // For convenience, reverse the row and then // search from 'the start' for the end block row.reverse(); int endStart = skipWhiteSpace(row); int[] endPattern; try { endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED); } catch (ReaderException e) { // Put our row of data back the right way before throwing row.reverse(); throw e; } // 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 recalc the indicies of where the 'endblock' starts & stops to // accomodate // the reversed nature of the search int temp = endPattern[0]; endPattern[0] = row.getSize() - endPattern[1]; endPattern[1] = row.getSize() - temp; // Put the row back the righ way. row.reverse(); return endPattern; }
public Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange) { StringBuilder result = decodeRowStringBuffer; result.Length = 0; int endStart = decodeMiddle(row, startGuardRange, result); 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. int end = endRange[1]; int quietEnd = end + (end - endRange[0]); if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false)) { throw new ReaderException(); } String resultString = result.ToString(); if (!checkChecksum(resultString)) { throw new ReaderException(); } 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()); }
/** * @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/... * @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 */ public static int[] findGuardPattern(BitArray row, int rowOffset, bool whiteFirst, int[] pattern) { int patternLength = pattern.Length; int[] counters = new int[patternLength]; 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) || (pixel && !isWhite)) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { 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++; } counters[counterPosition] = 1; isWhite = !isWhite; } } throw new ReaderException(); }
/** * Records the size of successive runs of white and black pixels in a row, starting at a given point. * The values are recorded in the given array, and the number of runs recorded is equal to the size * of the array. If the row starts on a white pixel at the given start point, then the first count * recorded is the run of white pixels starting from that point; likewise it is the count of a run * of black pixels if the row begin on a black pixels at that point. * * @param row row to count from * @param start offset into row to start at * @param counters array into which to record counts * @throws ReaderException if counters cannot be filled entirely from row before running out of pixels */ public static void recordPattern(BitArray row, int start, int[] counters) { int numCounters = counters.Length; for (int i = 0; i < numCounters; i++) { counters[i] = 0; } int end = row.getSize(); if (start >= end) { throw new ReaderException(); } bool isWhite = !row.get(start); int counterPosition = 0; int k = start; while (k < end) { bool pixel = row.get(k); if ((!pixel && isWhite) || (pixel && !isWhite)) { counters[counterPosition]++; } else { counterPosition++; if (counterPosition == numCounters) { break; } else { counters[counterPosition] = 1; isWhite = !isWhite; } } k++; } // If we read fully the last section of pixels and filled up our counters -- or filled // the last counter but ran off the side of the image, OK. Otherwise, a problem. if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && k == end))) { throw new ReaderException(); } }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) { int[] startPatternInfo = findStartPattern(row); int startCode = startPatternInfo[2]; int codeSet; switch (startCode) { case CODE_START_A: codeSet = CODE_CODE_A; break; case CODE_START_B: codeSet = CODE_CODE_B; break; case CODE_START_C: codeSet = CODE_CODE_C; break; default: throw new ReaderException(); } bool done = false; bool isNextShifted = false; StringBuilder result = new StringBuilder(); int lastStart = startPatternInfo[0]; int nextStart = startPatternInfo[1]; int[] counters = new int[6]; int lastCode = 0; int code = 0; int checksumTotal = startCode; int multiplier = 0; bool lastCharacterWasPrintable = true; while (!done) { bool unshift = isNextShifted; isNextShifted = false; // Save off last code lastCode = code; // Decode another code from image code = decodeCode(row, counters, nextStart); // Remember whether the last code was printable or not (excluding CODE_STOP) if (code != CODE_STOP) { lastCharacterWasPrintable = true; } // Add to checksum computation (if not CODE_STOP of course) if (code != CODE_STOP) { multiplier++; checksumTotal += multiplier * code; } // Advance to where the next code will to start lastStart = nextStart; for (int i = 0; i < counters.Length; i++) { nextStart += counters[i]; } // Take care of illegal start codes switch (code) { case CODE_START_A: case CODE_START_B: case CODE_START_C: throw new ReaderException(); } switch (codeSet) { case CODE_CODE_A: if (code < 64) { result.Append((char) (' ' + code)); } else if (code < 96) { result.Append((char) (code - 64)); } else { // Don't let CODE_STOP, which always appears, affect whether whether we think the last code // was printable or not if (code != CODE_STOP) { lastCharacterWasPrintable = false; } switch (code) { case CODE_FNC_1: case CODE_FNC_2: case CODE_FNC_3: case CODE_FNC_4_A: // do nothing? break; case CODE_SHIFT: isNextShifted = true; codeSet = CODE_CODE_B; break; case CODE_CODE_B: codeSet = CODE_CODE_B; break; case CODE_CODE_C: codeSet = CODE_CODE_C; break; case CODE_STOP: done = true; break; } } break; case CODE_CODE_B: if (code < 96) { result.Append((char) (' ' + code)); } else { if (code != CODE_STOP) { lastCharacterWasPrintable = false; } switch (code) { case CODE_FNC_1: case CODE_FNC_2: case CODE_FNC_3: case CODE_FNC_4_B: // do nothing? break; case CODE_SHIFT: isNextShifted = true; codeSet = CODE_CODE_C; break; case CODE_CODE_A: codeSet = CODE_CODE_A; break; case CODE_CODE_C: codeSet = CODE_CODE_C; break; case CODE_STOP: done = true; break; } } break; case CODE_CODE_C: if (code < 100) { if (code < 10) { result.Append('0'); } result.Append(code); } else { if (code != CODE_STOP) { lastCharacterWasPrintable = false; } switch (code) { case CODE_FNC_1: // do nothing? break; case CODE_CODE_A: codeSet = CODE_CODE_A; break; case CODE_CODE_B: codeSet = CODE_CODE_B; break; case CODE_STOP: done = true; break; } } break; } // Unshift back to another code set if we were shifted if (unshift) { switch (codeSet) { case CODE_CODE_A: codeSet = CODE_CODE_C; break; case CODE_CODE_B: codeSet = CODE_CODE_A; break; case CODE_CODE_C: codeSet = CODE_CODE_B; break; } } } // Check for ample whitespice following pattern, but, to do this we first need to remember that we // fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left to read off. // Would be slightly better to properly read. Here we just skip it: while (row.get(nextStart)) { nextStart++; } if (!row.isRange(nextStart, Math.Min(row.getSize(), nextStart + (nextStart - lastStart) / 2), false)) { throw new ReaderException(); } // Pull out from sum the value of the penultimate check code checksumTotal -= multiplier * lastCode; // lastCode is the checksum then: if (checksumTotal % 103 != lastCode) { throw new ReaderException(); } // Need to pull out the check digits from string int resultLength = result.Length; // Only bother if, well, the result had at least one character, and if the checksum digit happened // to be a printable character. If it was just interpreted as a control code, nothing to remove if (resultLength > 0 && lastCharacterWasPrintable) { if (codeSet == CODE_CODE_C) { result.Remove(resultLength - 2, 2); } else { result.Remove(resultLength - 1, 1); } } String resultString = result.ToString(); if (resultString.Length == 0) { // Almost surely a false positive throw new ReaderException(); } float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; float right = (float) (nextStart + lastStart) / 2.0f; return new Result( resultString, null, new ResultPoint[]{ new GenericResultPoint(left, (float) rowNumber), new GenericResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_128); }
private static int[] findStartPattern(BitArray row) { int width = row.getSize(); int rowOffset = 0; while (rowOffset < width) { if (row.get(rowOffset)) { break; } rowOffset++; } int counterPosition = 0; int[] counters = new int[6]; int patternStart = rowOffset; bool isWhite = false; int patternLength = counters.Length; for (int i = rowOffset; i < width; i++) { bool pixel = row.get(i); if ((!pixel && isWhite) || (pixel && !isWhite)) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { int bestVariance = MAX_AVG_VARIANCE; int bestMatch = -1; for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) { int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE); if (variance < bestVariance) { bestVariance = variance; bestMatch = startCode; } } if (bestMatch >= 0) { // Look for whitespace before start pattern, >= 50% of width of start pattern if (row.isRange(Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) { return new int[]{patternStart, i, bestMatch}; } } 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 new ReaderException(); }
public BitArray getBlackColumn(int x, BitArray column, int startY, int getHeight) { if (column == null || column.getSize() < getHeight) { column = new BitArray(getHeight); } else { column.clear(); } // Reuse the same int array each time initLuminances(); luminances = getLuminanceColumn(x, luminances); // We don't handle "row sampling" specially here for (int y = 0; y < getHeight; y++) { if (luminances[startY + y] < blackPoint) { column.set(y); } } return column; }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) { int[] start = findAsteriskPattern(row); int nextStart = start[1]; int end = row.getSize(); // Read off white space while (nextStart < end && !row.get(nextStart)) { nextStart++; } StringBuilder result = new StringBuilder(); int[] counters = new int[9]; char decodedChar; int lastStart; do { recordPattern(row, nextStart, counters); int pattern = toNarrowWidePattern(counters); decodedChar = patternToChar(pattern); result.Append(decodedChar); lastStart = nextStart; for (int i = 0; i < counters.Length; i++) { nextStart += counters[i]; } // Read off white space while (nextStart < end && !row.get(nextStart)) { nextStart++; } } while (decodedChar != '*'); result.Remove(result.Length - 1, 1); // remove asterisk // Look for whitespace after pattern: int lastPatternSize = 0; for (int i = 0; i < counters.Length; 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 new ReaderException(); } if (usingCheckDigit) { int max = result.Length - 1; int total = 0; for (int i = 0; i < max; i++) { total += ALPHABET_STRING.IndexOf(result[i]); } if (total % 43 != ALPHABET_STRING.IndexOf(result[max])) { throw new ReaderException(); } result.Remove(max,1); } String resultString = result.ToString(); if (extendedMode) { resultString = decodeExtended(resultString); } if (resultString.Length == 0) { // Almost surely a false positive throw new ReaderException(); } float left = (float) (start[1] + start[0]) / 2.0f; float right = (float) (nextStart + lastStart) / 2.0f; return new Result( resultString, null, new ResultPoint[]{ new GenericResultPoint(left, (float) rowNumber), new GenericResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_39); }
private static int[] findAsteriskPattern(BitArray row) { int width = row.getSize(); int rowOffset = 0; while (rowOffset < width) { if (row.get(rowOffset)) { break; } rowOffset++; } int counterPosition = 0; int[] counters = new int[9]; int patternStart = rowOffset; bool isWhite = false; int patternLength = counters.Length; for (int i = rowOffset; i < width; i++) { bool pixel = row.get(i); if ((!pixel && isWhite) || (pixel && !isWhite)) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { try { if (toNarrowWidePattern(counters) == ASTERISK_ENCODING) { // Look for whitespace before start pattern, >= 50% of width of start pattern if (row.isRange(Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) { return new int[]{patternStart, i}; } } } catch (ReaderException re) { // no match, continue } 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 new ReaderException(); }