/** * <p>Detects a QR Code in an image, simply.</p> * * @param hints optional hints to detector * @return {@link DetectorResult} encapsulating results of detecting a QR Code * @throws ReaderException if no QR Code can be found */ public DetectorResult detect(System.Collections.Hashtable hints) { MonochromeBitmapSource image = this.image; if (!BlackPointEstimationMethod.TWO_D_SAMPLING.Equals(image.getLastEstimationMethod())) { image.estimateBlackPoint(BlackPointEstimationMethod.TWO_D_SAMPLING, 0); } FinderPatternFinder finder = new FinderPatternFinder(image); FinderPatternInfo info = finder.find(hints); FinderPattern topLeft = info.getTopLeft(); FinderPattern topRight = info.getTopRight(); FinderPattern bottomLeft = info.getBottomLeft(); float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); if (moduleSize < 1.0f) { throw new ReaderException(); } int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension); int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7; AlignmentPattern alignmentPattern = null; // Anything above version 1 has an alignment pattern if (provisionalVersion.getAlignmentPatternCenters().Length > 0) { // Guess where a "bottom right" finder pattern would have been float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX(); float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY(); // Estimate that alignment pattern is closer by 3 modules // from "bottom right" to known top left location float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters; int estAlignmentX = (int)(topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX())); int estAlignmentY = (int)(topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY())); // Kind of arbitrary -- expand search radius before giving up for (int i = 4; i <= 16; i <<= 1) { try { alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i); break; } catch (ReaderException re) { // try next round } } if (alignmentPattern == null) { throw new ReaderException(); } } BitMatrix bits = sampleGrid(image, topLeft, topRight, bottomLeft, alignmentPattern, dimension); ResultPoint[] points; if (alignmentPattern == null) { points = new ResultPoint[] { bottomLeft, topLeft, topRight }; } else { points = new ResultPoint[] { bottomLeft, topLeft, topRight, alignmentPattern }; } return(new DetectorResult(bits, points)); }
/** * 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(); }