/** * <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since * it's pretty performance-critical and so is written to be fast foremost.</p> * * @return {@link AlignmentPattern} if found * @throws ReaderException if not found */ public AlignmentPattern find() { int startX = this.startX; int height = this.height; int maxJ = startX + width; int middleI = startY + (height >> 1); BitArray luminanceRow = new BitArray(width); // We are looking for black/white/black modules in 1:1:1 ratio; // this tracks the number of black/white/black modules seen so far int[] stateCount = new int[3]; for (int iGen = 0; iGen < height; iGen++) { // Search from middle outwards int i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1)); image.getBlackRow(i, luminanceRow, startX, width); stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; int j = startX; // Burn off leading white pixels before anything else; if we start in the middle of // a white run, it doesn't make sense to count its length, since we don't know if the // white run continued to the left of the start point while (j < maxJ && !luminanceRow.get(j - startX)) { j++; } int currentState = 0; while (j < maxJ) { if (luminanceRow.get(j - startX)) { // Black pixel if (currentState == 1) // Counting black pixels { stateCount[currentState]++; } else // Counting white pixels { if (currentState == 2) // A winner? { if (foundPatternCross(stateCount)) // Yes { AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j); if (confirmed != null) { return(confirmed); } } stateCount[0] = stateCount[2]; stateCount[1] = 1; stateCount[2] = 0; currentState = 1; } else { stateCount[++currentState]++; } } } else // White pixel { if (currentState == 1) // Counting black pixels { currentState++; } stateCount[currentState]++; } j++; } if (foundPatternCross(stateCount)) { AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ); if (confirmed != null) { return(confirmed); } } } // Hmm, nothing we saw was observed and confirmed twice. If we had // any guess at all, return it. if (!(possibleCenters.Count == 0)) { return((AlignmentPattern)possibleCenters[0]); } throw new ReaderException(); }
/** * 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(); }
/** * 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(); }