protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder result) { int[] counters = decodeMiddleCounters; counters[0] = 0; counters[1] = 0; counters[2] = 0; counters[3] = 0; int end = row.Size; 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 override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> hints) { int[] start = findAsteriskPattern(row); int nextStart = start[1]; int end = row.Size; // Read off white space while (nextStart < end && !row.get_Renamed(nextStart)) { nextStart++; } System.Text.StringBuilder result = new System.Text.StringBuilder(20); int[] counters = new int[9]; char decodedChar; int lastStart; do { recordPattern(row, nextStart, counters); int pattern = toNarrowWidePattern(counters); if (pattern < 0) { throw ReaderException.Instance; } 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_Renamed(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 ReaderException.Instance; } if (usingCheckDigit) { int max = result.Length - 1; int total = 0; for (int i = 0; i < max; i++) { total += ALPHABET_STRING.IndexOf((System.Char) result[i]); } if (total % 43 != ALPHABET_STRING.IndexOf((System.Char) result[max])) { throw ReaderException.Instance; } result.Remove(max, 1); } System.String resultString = result.ToString(); if (extendedMode) { resultString = decodeExtended(resultString); } if (resultString.Length == 0) { // Almost surely a false positive throw ReaderException.Instance; } //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float left = (float) (start[1] + start[0]) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float right = (float) (nextStart + lastStart) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_39); }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> hints) { // Compute this location once and reuse it on multiple implementations int[] startGuardPattern = UPCEANReader.findStartGuardPattern(row); int size = readers.Count; for (int i = 0; i < size; i++) { UPCEANReader reader = (UPCEANReader) readers[i]; Result result; try { result = reader.decodeRow(rowNumber, row, startGuardPattern, hints); } catch (ReaderException re) { continue; } // Special case: a 12-digit code encoded in UPC-A is identical to a "0" // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". // Individually these are correct and their readers will both read such a code // and correctly call it EAN-13, or UPC-A, respectively. // // In this case, if we've been looking for both types, we'd like to call it // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A // result if appropriate. if (result.BarcodeFormat.Equals(BarcodeFormat.EAN_13) && result.Text[0] == '0') { return new Result(result.Text.Substring(1), null, result.ResultPoints, BarcodeFormat.UPC_A); } return result; } throw ReaderException.Instance; }
protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder resultString) { return ean13Reader.decodeMiddle(row, startRange, resultString); }
/// <summary> <p>Like {@link #decodeRow(int, BitArray, java.util.Hashtable)}, but /// allows caller to inform method about where the UPC/EAN start pattern is /// found. This allows this to be computed once and reused across many implementations.</p> /// </summary> public virtual Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, System.Collections.Generic.Dictionary <Object,Object> hints) { ResultPointCallback resultPointCallback = (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK))?(ResultPointCallback) hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]:null; //Upgrade: Fixed issue with dictionary if (resultPointCallback != null) { resultPointCallback.foundPossibleResultPoint(new ResultPoint((startGuardRange[0] + startGuardRange[1]) / 2.0f, rowNumber)); } System.Text.StringBuilder result = decodeRowStringBuffer; result.Length = 0; int endStart = decodeMiddle(row, startGuardRange, result); if (resultPointCallback != null) { resultPointCallback.foundPossibleResultPoint(new ResultPoint(endStart, rowNumber)); } int[] endRange = decodeEnd(row, endStart); if (resultPointCallback != null) { resultPointCallback.foundPossibleResultPoint(new ResultPoint((endRange[0] + endRange[1]) / 2.0f, rowNumber)); } // 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.Size || !row.isRange(end, quietEnd, false)) { throw ReaderException.Instance; } System.String resultString = result.ToString(); if (!checkChecksum(resultString)) { throw ReaderException.Instance; } //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float right = (float) (endRange[1] + endRange[0]) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat); }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> 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 ReaderException.Instance; } bool done = false; bool isNextShifted = false; System.Text.StringBuilder result = new System.Text.StringBuilder(20); 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 ReaderException.Instance; } 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 whitespace 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: int width = row.Size; while (nextStart < width && row.get_Renamed(nextStart)) { nextStart++; } if (!row.isRange(nextStart, System.Math.Min(width, nextStart + (nextStart - lastStart) / 2), false)) { throw ReaderException.Instance; } // Pull out from sum the value of the penultimate check code checksumTotal -= multiplier * lastCode; // lastCode is the checksum then: if (checksumTotal % 103 != lastCode) { throw ReaderException.Instance; } // Need to pull out the check digits from string int resultLength = result.Length; // Only bother if 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, resultLength - (resultLength - 2)); } else { result.Remove(resultLength - 1, resultLength - (resultLength - 1)); } } System.String resultString = result.ToString(); if (resultString.Length == 0) { // Almost surely a false positive throw ReaderException.Instance; } //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" float right = (float) (nextStart + lastStart) / 2.0f; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_128); }
private static int decodeCode(BitArray row, int[] counters, int rowOffset) { recordPattern(row, rowOffset, counters); int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept int bestMatch = - 1; for (int d = 0; d < CODE_PATTERNS.Length; d++) { int[] pattern = CODE_PATTERNS[d]; int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); if (variance < bestVariance) { bestVariance = variance; bestMatch = d; } } // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6. if (bestMatch >= 0) { return bestMatch; } else { throw ReaderException.Instance; } }
protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder resultString) { int[] counters = decodeMiddleCounters; counters[0] = 0; counters[1] = 0; counters[2] = 0; counters[3] = 0; int end = row.Size; 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; }
/// <summary> Converts one row of luminance data to 1 bit data. May actually do the conversion, or return /// cached data. Callers should assume this method is expensive and call it as seldom as possible. /// This method is intended for decoding 1D barcodes and may choose to apply sharpening. /// /// </summary> /// <param name="y">The row to fetch, 0 <= y < bitmap height. /// </param> /// <param name="row">An optional preallocated array. If null or too small, it will be ignored. /// If used, the Binarizer will call BitArray.clear(). Always use the returned object. /// </param> /// <returns> The array of bits for this row (true means black). /// </returns> public BitArray getBlackRow(int y, BitArray row) { return binarizer.getBlackRow(y, row); }
//UPGRADE_NOTE: Access modifiers of method 'decodeEnd' were changed to 'protected'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1204'" protected internal virtual int[] decodeEnd(BitArray row, int endStart) { return findGuardPattern(row, endStart, false, START_END_PATTERN); }
/// <summary> Subclasses override this to decode the portion of a barcode between the start /// and end guard patterns. /// /// </summary> /// <param name="row">row of black/white values to search /// </param> /// <param name="startRange">start/end offset of start guard pattern /// </param> /// <param name="resultString">{@link StringBuffer} to append decoded chars to /// </param> /// <returns> horizontal offset of first pixel after the "middle" that was decoded /// </returns> /// <throws> ReaderException if decoding could not complete successfully </throws> protected internal abstract int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder resultString);
internal static int[] findStartGuardPattern(BitArray row) { bool foundStart = false; int[] startRange = null; int nextStart = 0; while (!foundStart) { startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN); 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; }
/// <param name="row">row of black/white values to search /// </param> /// <param name="rowOffset">position to start search /// </param> /// <param name="whiteFirst">if true, indicates that the pattern specifies white/black/white/... /// pixel counts, otherwise, it is interpreted as black/white/black/... /// </param> /// <param name="pattern">pattern of counts of number of black and white pixels that are being /// searched for as a pattern /// </param> /// <returns> start/end horizontal offset of guard pattern, as an array of two ints /// </returns> /// <throws> ReaderException if pattern is not found </throws> internal static int[] findGuardPattern(BitArray row, int rowOffset, bool whiteFirst, int[] pattern) { int patternLength = pattern.Length; int[] counters = new int[patternLength]; int width = row.Size; bool isWhite = false; while (rowOffset < width) { isWhite = !row.get_Renamed(rowOffset); if (whiteFirst == isWhite) { break; } rowOffset++; } int counterPosition = 0; int patternStart = rowOffset; for (int x = rowOffset; x < width; x++) { bool pixel = row.get_Renamed(x); if (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 ReaderException.Instance; }
/// <summary> Attempts to decode a single UPC/EAN-encoded digit. /// /// </summary> /// <param name="row">row of black/white values to decode /// </param> /// <param name="counters">the counts of runs of observed black/white/black/... values /// </param> /// <param name="rowOffset">horizontal offset to start decoding from /// </param> /// <param name="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 /// </param> /// <returns> horizontal offset of first pixel beyond the decoded digit /// </returns> /// <throws> ReaderException if digit cannot be decoded </throws> internal static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns) { recordPattern(row, rowOffset, counters); int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept int bestMatch = - 1; 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 = i; } } if (bestMatch >= 0) { return bestMatch; } else { throw ReaderException.Instance; } }
private static int[] findAsteriskPattern(BitArray row) { int width = row.Size; int rowOffset = 0; while (rowOffset < width) { if (row.get_Renamed(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_Renamed(i); if (pixel ^ isWhite) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { if (toNarrowWidePattern(counters) == ASTERISK_ENCODING) { // Look for whitespace before start pattern, >= 50% of width of start pattern if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) { return new int[]{patternStart, i}; } } 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.Instance; }
/// <summary> <p>Attempts to decode a one-dimensional barcode format given a single row of /// an image.</p> /// /// </summary> /// <param name="rowNumber">row number from top of the row /// </param> /// <param name="row">the black/white pixel data of the row /// </param> /// <param name="hints">decode hints /// </param> /// <returns> {@link Result} containing encoded string and start/end of barcode /// </returns> /// <throws> ReaderException if an error occurs or barcode cannot be found </throws> public abstract Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> hints);
/// <summary> Converts one row of luminance data to 1 bit data. May actually do the conversion, or return /// cached data. Callers should assume this method is expensive and call it as seldom as possible. /// This method is intended for decoding 1D barcodes and may choose to apply sharpening. /// For callers which only examine one row of pixels at a time, the same BitArray should be reused /// and passed in with each call for performance. However it is legal to keep more than one row /// at a time if needed. /// /// </summary> /// <param name="y">The row to fetch, 0 <= y < bitmap height. /// </param> /// <param name="row">An optional preallocated array. If null or too small, it will be ignored. /// If used, the Binarizer will call BitArray.clear(). Always use the returned object. /// </param> /// <returns> The array of bits for this row (true means black). /// </returns> public abstract BitArray getBlackRow(int y, BitArray row);
/// <summary> 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. /// /// </summary> /// <param name="row">row to count from /// </param> /// <param name="start">offset into row to start at /// </param> /// <param name="counters">array into which to record counts /// </param> /// <throws> ReaderException if counters cannot be filled entirely from row before running out </throws> /// <summary> of pixels /// </summary> internal 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.Size; if (start >= end) { throw ReaderException.Instance; } bool isWhite = !row.get_Renamed(start); int counterPosition = 0; int i2 = start; while (i2 < end) { bool pixel = row.get_Renamed(i2); if (pixel ^ isWhite) { // that is, exactly one is true counters[counterPosition]++; } else { counterPosition++; if (counterPosition == numCounters) { break; } else { counters[counterPosition] = 1; isWhite ^= true; // isWhite = !isWhite; } } i2++; } // 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 && i2 == end))) { throw ReaderException.Instance; } }
/// <summary> A fast method to retrieve one row of data from the matrix as a BitArray. /// /// </summary> /// <param name="y">The row to retrieve /// </param> /// <param name="row">An optional caller-allocated BitArray, will be allocated if null or too small /// </param> /// <returns> The resulting BitArray - this reference should always be used even when passing /// your own row /// </returns> public BitArray getRow(int y, BitArray row) { if (row == null || row.Size < width) { row = new BitArray(width); } int offset = y * rowSize; for (int x = 0; x < rowSize; x++) { row.setBulk(x << 5, bits[offset + x]); } return row; }
/// <summary> We're going to examine rows from the middle outward, searching alternately above and below the /// middle, and farther out each time. rowStep is the number of rows between each successive /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then /// middle + rowStep, then middle - (2 * rowStep), etc. /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the /// image if "trying harder". /// /// </summary> /// <param name="image">The image to decode /// </param> /// <param name="hints">Any hints that were requested /// </param> /// <returns> The contents of the decoded barcode /// </returns> /// <throws> ReaderException Any spontaneous errors which occur </throws> private Result doDecode(BinaryBitmap image, System.Collections.Generic.Dictionary <Object,Object> hints) { int width = image.Width; int height = image.Height; BitArray row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = System.Math.Max(1, height >> (tryHarder?7:4)); int maxLines; if (tryHarder) { maxLines = height; // Look at the whole image, not just the center } else { maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image } for (int x = 0; x < maxLines; x++) { // Scanning from the middle out. Determine which row we're looking at next: int rowStepsAboveOrBelow = (x + 1) >> 1; bool isAbove = (x & 0x01) == 0; // i.e. is x even? int rowNumber = middle + rowStep * (isAbove?rowStepsAboveOrBelow:- rowStepsAboveOrBelow); if (rowNumber < 0 || rowNumber >= height) { // Oops, if we run off the top or bottom, stop break; } // Estimate black point for this row and load it: try { row = image.getBlackRow(rowNumber, row); } catch (ReaderException re) { continue; } // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to // handle decoding upside down barcodes. for (int attempt = 0; attempt < 2; attempt++) { if (attempt == 1) { // trying again? row.reverse(); // reverse the row and continue // This means we will only ever draw result points *once* in the life of this method // since we want to avoid drawing the wrong points after flipping the row, and, // don't want to clutter with noise from every single row scan -- just the scans // that start on the center line. if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { System.Collections.Generic.Dictionary <Object,Object> newHints = new System.Collections.Generic.Dictionary <Object,Object>(); // Can't use clone() in J2ME System.Collections.IEnumerator hintEnum = hints.Keys.GetEnumerator(); //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'" while (hintEnum.MoveNext()) { //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'" System.Object key = hintEnum.Current; if (!key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { newHints[key] = hints[key]; } } hints = newHints; } } try { // Look for a barcode Result result = decodeRow(rowNumber, row, hints); // We found our barcode if (attempt == 1) { // But it was upside down, so note that result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.ResultPoints; points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y); points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y); } return result; } catch (ReaderException re) { // continue -- just couldn't decode this row } } } throw ReaderException.Instance; }
private static int[] findStartPattern(BitArray row) { int width = row.Size; int rowOffset = 0; while (rowOffset < width) { if (row.get_Renamed(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_Renamed(i); if (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(System.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 ReaderException.Instance; }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> hints) { return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, hints)); }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> hints) { int size = readers.Count; for (int i = 0; i < size; i++) { OneDReader reader = (OneDReader) readers[i]; try { return reader.decodeRow(rowNumber, row, hints); } catch (ReaderException re) { // continue } } throw ReaderException.Instance; }
public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary <Object,Object> hints) { return decodeRow(rowNumber, row, findStartGuardPattern(row), hints); }