/** * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of * a finder pattern by looking for a black-white-black run from the center in the direction * of another point (another finder pattern center), and in the opposite direction too.</p> */ private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) { float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); // Now count other way -- don't run off image though of course int otherToX = fromX - (toX - fromX); if (otherToX < 0) { // "to" should the be the first value not included, so, the first value off // the edge is -1 otherToX = -1; } else if (otherToX >= image.getWidth()) { otherToX = image.getWidth(); } int otherToY = fromY - (toY - fromY); if (otherToY < 0) { otherToY = -1; } else if (otherToY >= image.getHeight()) { otherToY = image.getHeight(); } result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); return(result - 1.0f); // -1 because we counted the middle pixel twice }
/** * 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". * * @param image The image to decode * @param hints Any hints that were requested * @return The contents of the decoded barcode * @throws ReaderException Any spontaneous errors which occur */ private Result doDecode(MonochromeBitmapSource image, System.Collections.Hashtable hints) { int width = image.getWidth(); int height = image.getHeight(); BitArray row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = 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 { image.estimateBlackPoint(BlackPointEstimationMethod.ROW_SAMPLING, rowNumber); } catch (ReaderException re) { continue; } image.getBlackRow(rowNumber, row, 0, width); // 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 } 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, 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.getResultPoints(); points[0] = (ResultPoint) new GenericResultPoint(width - points[0].getX() - 1, points[0].getY()); points[1] = (ResultPoint) new GenericResultPoint(width - points[1].getX() - 1, points[1].getY()); } return(result); } catch (ReaderException re) { // continue -- just couldn't decode this row } } } throw new ReaderException(); }
/** * This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image * which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border * around it. This is a specialized method that works exceptionally fast in this special * case. */ private static BitMatrix extractPureBits(MonochromeBitmapSource image) { // Now need to determine module size in pixels int height = image.getHeight(); int width = image.getWidth(); int minDimension = Math.Min(height, width); // First, skip white border by tracking diagonally from the top left down and to the right: int borderWidth = 0; while (borderWidth < minDimension && !image.isBlack(borderWidth, borderWidth)) { borderWidth++; } if (borderWidth == minDimension) { throw new ReaderException(); } // And then keep tracking across the top-left black module to determine module size int moduleEnd = borderWidth + 1; while (moduleEnd < width && image.isBlack(moduleEnd, borderWidth)) { moduleEnd++; } if (moduleEnd == width) { throw new ReaderException(); } int moduleSize = moduleEnd - borderWidth; // And now find where the bottommost black module on the first column ends int columnEndOfSymbol = height - 1; while (columnEndOfSymbol >= 0 && !image.isBlack(borderWidth, columnEndOfSymbol)) { columnEndOfSymbol--; } if (columnEndOfSymbol < 0) { throw new ReaderException(); } columnEndOfSymbol++; // Make sure width of barcode is a multiple of module size if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) { throw new ReaderException(); } int dimension = (columnEndOfSymbol - borderWidth) / moduleSize; // Push in the "border" by half the module width so that we start // sampling in the middle of the module. Just in case the image is a // little off, this will help recover. borderWidth += moduleSize >> 1; int sampleDimension = borderWidth + (dimension - 1) * moduleSize; if (sampleDimension >= width || sampleDimension >= height) { throw new ReaderException(); } // Now just read off the bits BitMatrix bits = new BitMatrix(dimension); for (int i = 0; i < dimension; i++) { int iOffset = borderWidth + i * moduleSize; for (int j = 0; j < dimension; j++) { if (image.isBlack(borderWidth + j * moduleSize, iOffset)) { bits.set(i, j); } } } return(bits); }
/** * This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image * which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border * around it. This is a specialized method that works exceptionally fast in this special * case. */ private static BitMatrix extractPureBits(MonochromeBitmapSource image) { // Now need to determine module size in pixels int height = image.getHeight(); int width = image.getWidth(); int minDimension = Math.Min(height, width); // First, skip white border by tracking diagonally from the top left down and to the right: int borderWidth = 0; while (borderWidth < minDimension && !image.isBlack(borderWidth, borderWidth)) { borderWidth++; } if (borderWidth == minDimension) { throw new ReaderException(); } // And then keep tracking across the top-left black module to determine module size int moduleEnd = borderWidth + 1; while (moduleEnd < width && image.isBlack(moduleEnd, borderWidth)) { moduleEnd++; } if (moduleEnd == width) { throw new ReaderException(); } int moduleSize = moduleEnd - borderWidth; // And now find where the bottommost black module on the first column ends int columnEndOfSymbol = height - 1; while (columnEndOfSymbol >= 0 && !image.isBlack(borderWidth, columnEndOfSymbol)) { columnEndOfSymbol--; } if (columnEndOfSymbol < 0) { throw new ReaderException(); } columnEndOfSymbol++; // Make sure width of barcode is a multiple of module size if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) { throw new ReaderException(); } int dimension = (columnEndOfSymbol - borderWidth) / moduleSize; // Push in the "border" by half the module width so that we start // sampling in the middle of the module. Just in case the image is a // little off, this will help recover. borderWidth += moduleSize >> 1; int sampleDimension = borderWidth + (dimension - 1) * moduleSize; if (sampleDimension >= width || sampleDimension >= height) { throw new ReaderException(); } // Now just read off the bits BitMatrix bits = new BitMatrix(dimension); for (int i = 0; i < dimension; i++) { int iOffset = borderWidth + i * moduleSize; for (int j = 0; j < dimension; j++) { if (image.isBlack(borderWidth + j * moduleSize, iOffset)) { bits.set(i, j); } } } return bits; }
/** * <p>After a horizontal scan finds a potential alignment pattern, this method * "cross-checks" by scanning down vertically through the center of the possible * alignment pattern to see if the same proportion is detected.</p> * * @param startI row where an alignment pattern was detected * @param centerJ center of the section that appears to cross an alignment pattern * @param maxCount maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * @return vertical center of alignment pattern, or {@link Float#NaN} if not found */ private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) { MonochromeBitmapSource image = this.image; int maxI = image.getHeight(); int[] stateCount = crossCheckStateCount; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; // Start counting up from center int i = startI; while (i >= 0 && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) { stateCount[1]++; i--; } // If already too many modules in this state or ran off the edge: if (i < 0 || stateCount[1] > maxCount) { return(float.NaN); } while (i >= 0 && !image.isBlack(centerJ, i) && stateCount[0] <= maxCount) { stateCount[0]++; i--; } if (stateCount[0] > maxCount) { return(float.NaN); } // Now also count down from center i = startI + 1; while (i < maxI && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) { stateCount[1]++; i++; } if (i == maxI || stateCount[1] > maxCount) { return(float.NaN); } while (i < maxI && !image.isBlack(centerJ, i) && stateCount[2] <= maxCount) { stateCount[2]++; i++; } if (stateCount[2] > maxCount) { return(float.NaN); } int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { return(float.NaN); } return(foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : float.NaN); }
/** * 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". * * @param image The image to decode * @param hints Any hints that were requested * @return The contents of the decoded barcode * @throws ReaderException Any spontaneous errors which occur */ private Result doDecode(MonochromeBitmapSource image, System.Collections.Hashtable hints) { int width = image.getWidth(); int height = image.getHeight(); BitArray row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = 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 { image.estimateBlackPoint(BlackPointEstimationMethod.ROW_SAMPLING, rowNumber); } catch (ReaderException re) { continue; } image.getBlackRow(rowNumber, row,0, width); // 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 } 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, 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.getResultPoints(); points[0] = (ResultPoint) new GenericResultPoint(width - points[0].getX() - 1, points[0].getY()); points[1] = (ResultPoint)new GenericResultPoint(width - points[1].getX() - 1, points[1].getY()); } return result; } catch (ReaderException re) { // continue -- just couldn't decode this row } } } throw new ReaderException(); }
/** * <p>After a horizontal scan finds a potential finder pattern, this method * "cross-checks" by scanning down vertically through the center of the possible * finder pattern to see if the same proportion is detected.</p> * * @param startI row where a finder pattern was detected * @param centerJ center of the section that appears to cross a finder pattern * @param maxCount maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * @return vertical center of finder pattern, or {@link Float#NaN} if not found */ private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) { MonochromeBitmapSource image = this.image; int maxI = image.getHeight(); int[] stateCount = getCrossCheckStateCount(); // Start counting up from center int i = startI; while (i >= 0 && image.isBlack(centerJ, i)) { stateCount[2]++; i--; } if (i < 0) { return(float.NaN); } while (i >= 0 && !image.isBlack(centerJ, i) && stateCount[1] <= maxCount) { stateCount[1]++; i--; } // If already too many modules in this state or ran off the edge: if (i < 0 || stateCount[1] > maxCount) { return(float.NaN); } while (i >= 0 && image.isBlack(centerJ, i) && stateCount[0] <= maxCount) { stateCount[0]++; i--; } if (stateCount[0] > maxCount) { return(float.NaN); } // Now also count down from center i = startI + 1; while (i < maxI && image.isBlack(centerJ, i)) { stateCount[2]++; i++; } if (i == maxI) { return(float.NaN); } while (i < maxI && !image.isBlack(centerJ, i) && stateCount[3] < maxCount) { stateCount[3]++; i++; } if (i == maxI || stateCount[3] >= maxCount) { return(float.NaN); } while (i < maxI && image.isBlack(centerJ, i) && stateCount[4] < maxCount) { stateCount[4]++; i++; } if (stateCount[4] >= maxCount) { return(float.NaN); } // If we found a finder-pattern-like section, but its size is more than 20% different than // the original, assume it's a false positive int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { return(float.NaN); } return(foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : float.NaN); }