public BinaryBitmap(Binarizer binarizer) { if (binarizer == null) { throw new ArgumentException("Binarizer must be non-null."); } this.binarizer = binarizer; matrix = null; }
/// <param name="bitMatrix">{@link BitMatrix} to parse /// </param> /// <throws> ReaderException if dimension is not >= 21 and 1 mod 4 </throws> internal BitMatrixParser(BitMatrix bitMatrix) { int dimension = bitMatrix.Dimension; if (dimension < 21 || (dimension & 0x03) != 1) { throw ReaderException.Instance; } this.bitMatrix = bitMatrix; }
/// <summary> <p>Implementations of this method reverse the data masking process applied to a QR Code and /// make its bits ready to read.</p> /// /// </summary> /// <param name="bits">representation of QR Code bits /// </param> /// <param name="dimension">dimension of QR Code, represented by bits, being unmasked /// </param> internal void unmaskBitMatrix(BitMatrix bits, int dimension) { for (int i = 0; i < dimension; i++) { for (int j = 0; j < dimension; j++) { if (isMasked(i, j)) { bits.flip(j, i); } } } }
public override BitMatrix sampleGrid(BitMatrix image, int dimension, float p1ToX, float p1ToY, float p2ToX, float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX, float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) { PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); return sampleGrid(image, dimension, transform); }
//private final ReedSolomonDecoder rsDecoder; /// <summary> <p>Convenience method that can decode a PDF417 Code represented as a 2D array of booleans. /// "true" is taken to mean a black module.</p> /// /// </summary> /// <param name="image">booleans representing white/black PDF417 modules /// </param> /// <returns> text and bytes encoded within the PDF417 Code /// </returns> /// <throws> ReaderException if the PDF417 Code cannot be decoded </throws> public DecoderResult decode(bool[][] image) { int dimension = image.Length; var bits = new BitMatrix(dimension); for (int i = 0; i < dimension; i++) { for (int j = 0; j < dimension; j++) { if (image[j][i]) { bits.set_Renamed(j, i); } } } return decode(bits); }
public override BitMatrix sampleGrid(BitMatrix image, int dimension, PerspectiveTransform transform) { var bits = new BitMatrix(dimension); var points = new float[dimension << 1]; for (int y = 0; y < dimension; y++) { int max = points.Length; float iValue = y + 0.5f; for (int x = 0; x < max; x += 2) { points[x] = (x >> 1) + 0.5f; points[x + 1] = iValue; } transform.transformPoints(points); // Quick check to see if points transformed to something inside the image; // sufficient to check the endpoints checkAndNudgePoints(image, points); try { for (int x = 0; x < max; x += 2) { if (image.get_Renamed((int) points[x], (int) points[x + 1])) { // Black(-ish) pixel bits.set_Renamed(x >> 1, y); } } } catch (IndexOutOfRangeException) { // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting // transform gets "twisted" such that it maps a straight line of points to a set of points // whose endpoints are in bounds, but others are not. There is probably some mathematical // way to detect this about the transformation that I don't know yet. // This results in an ugly runtime exception despite our clever checks above -- can't have // that. We could check each point's coordinates but that feels duplicative. We settle for // catching and wrapping ArrayIndexOutOfBoundsException. throw ReaderException.Instance; } } return bits; }
/// <summary> <p>Decodes a PDF417 Code represented as a {@link BitMatrix}. /// A 1 or "true" is taken to mean a black module.</p> /// /// </summary> /// <param name="bits">booleans representing white/black PDF417 Code modules /// </param> /// <returns> text and bytes encoded within the PDF417 Code /// </returns> /// <throws> ReaderException if the PDF417 Code cannot be decoded </throws> public DecoderResult decode(BitMatrix bits) { // Construct a parser to read the data codewords and error-correction level var parser = new BitMatrixParser(bits); int[] codewords = parser.readCodewords(); if (codewords == null || codewords.Length == 0) { throw ReaderException.Instance; } int ecLevel = parser.ECLevel; int numECCodewords = 1 << (ecLevel + 1); int[] erasures = parser.Erasures; correctErrors(codewords, erasures, numECCodewords); verifyCodewordCount(codewords, numECCodewords); // Decode the codewords return DecodedBitStreamParser.decode(codewords); }
/// <summary> <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken /// to mean a black module.</p> /// /// </summary> /// <param name="bits">booleans representing white/black Data Matrix Code modules /// </param> /// <returns> text and bytes encoded within the Data Matrix Code /// </returns> /// <throws> ReaderException if the Data Matrix Code cannot be decoded </throws> public DecoderResult decode(BitMatrix bits) { // Construct a parser and read version, error-correction level var parser = new BitMatrixParser(bits); Version version = parser.readVersion(bits); // Read codewords sbyte[] codewords = parser.readCodewords(); // Separate into data blocks DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version); // Count total number of data bytes int totalBytes = 0; for (int i = 0; i < dataBlocks.Length; i++) { totalBytes += dataBlocks[i].NumDataCodewords; } var resultBytes = new sbyte[totalBytes]; int resultOffset = 0; // Error-correct and copy data blocks together into a stream of bytes for (int j = 0; j < dataBlocks.Length; j++) { DataBlock dataBlock = dataBlocks[j]; sbyte[] codewordBytes = dataBlock.Codewords; int numDataCodewords = dataBlock.NumDataCodewords; correctErrors(codewordBytes, numDataCodewords); for (int i = 0; i < numDataCodewords; i++) { resultBytes[resultOffset++] = codewordBytes[i]; } } // Decode the contents of that stream of bytes return DecodedBitStreamParser.decode(resultBytes); }
private static BitMatrix sampleGrid(BitMatrix image, PerspectiveTransform transform, int dimension) { GridSampler sampler = GridSampler.Instance; return sampler.sampleGrid(image, dimension, transform); }
/// <summary> This method detects a barcode in a "pure" image -- that is, pure monochrome image /// which contains only an unrotated, unskewed, image of a barcode, with some white border /// around it. This is a specialized method that works exceptionally fast in this special /// case. /// </summary> private static BitMatrix extractPureBits(BinaryBitmap image) { // Now need to determine module size in pixels BitMatrix matrix = image.BlackMatrix; int height = matrix.Height; int width = matrix.Width; 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 && !matrix.get_Renamed(borderWidth, borderWidth)) { borderWidth++; } if (borderWidth == minDimension) { throw ReaderException.Instance; } // And then keep tracking across the top-left black module to determine module size int moduleEnd = borderWidth; while (moduleEnd < minDimension && matrix.get_Renamed(moduleEnd, moduleEnd)) { moduleEnd++; } if (moduleEnd == minDimension) { throw ReaderException.Instance; } int moduleSize = moduleEnd - borderWidth; // And now find where the rightmost black module on the first row ends int rowEndOfSymbol = width - 1; while (rowEndOfSymbol >= 0 && !matrix.get_Renamed(rowEndOfSymbol, borderWidth)) { rowEndOfSymbol--; } if (rowEndOfSymbol < 0) { throw ReaderException.Instance; } rowEndOfSymbol++; // Make sure width of barcode is a multiple of module size if ((rowEndOfSymbol - borderWidth)%moduleSize != 0) { throw ReaderException.Instance; } int dimension = (rowEndOfSymbol - 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 ReaderException.Instance; } // Now just read off the bits var bits = new BitMatrix(dimension); for (int y = 0; y < dimension; y++) { int iOffset = borderWidth + y*moduleSize; for (int x = 0; x < dimension; x++) { if (matrix.get_Renamed(borderWidth + x*moduleSize, iOffset)) { bits.set_Renamed(x, y); } } } return bits; }
/// <param name="matrix">row of black/white values to search /// </param> /// <param name="column">x position to start search /// </param> /// <param name="row">y position to start search /// </param> /// <param name="width">the number of pixels to search on this row /// </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> private static int[] findGuardPattern(BitMatrix matrix, int column, int row, int width, bool whiteFirst, int[] pattern) { int patternLength = pattern.Length; // TODO: Find a way to cache this array, as this method is called hundreds of times // per image, and we want to allocate as seldom as possible. var counters = new int[patternLength]; bool isWhite = whiteFirst; int counterPosition = 0; int patternStart = column; for (int x = column; x < column + width; x++) { bool pixel = matrix.get_Renamed(x, row); if (pixel ^ isWhite) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { return new[] {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; } } return null; }
private static BitMatrix sampleGrid(BitMatrix matrix, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint topRight, ResultPoint bottomRight, int dimension) { // Note that unlike the QR Code sampler, we didn't find the center of modules, but the // very corners. So there is no 0.5f here; 0.0f is right. GridSampler sampler = GridSampler.Instance; return sampler.sampleGrid(matrix, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y); // p4FromY }
/// <summary> Locate the vertices and the codewords area of a black blob using the Start /// and Stop patterns as locators. This assumes that the image is rotated 180 /// degrees and if it locates the start and stop patterns at it will re-map /// the vertices for a 0 degree rotation. /// TODO: Change assumption about barcode location. /// TODO: Scanning every row is very expensive. We should only do this for TRY_HARDER. /// /// </summary> /// <param name="matrix">the scanned barcode image. /// </param> /// <returns> an array containing the vertices: /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area /// </returns> private static ResultPoint[] findVertices180(BitMatrix matrix) { int height = matrix.Height; int width = matrix.Width; int halfWidth = width >> 1; var result = new ResultPoint[8]; bool found = false; // Top Left for (int i = height - 1; i > 0; i--) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); if (loc != null) { result[0] = new ResultPoint(loc[1], i); result[4] = new ResultPoint(loc[0], i); found = true; break; } } // Bottom Left if (found) { // Found the Top Left vertex found = false; for (int i = 0; i < height; i++) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); if (loc != null) { result[1] = new ResultPoint(loc[1], i); result[5] = new ResultPoint(loc[0], i); found = true; break; } } } // Top Right if (found) { // Found the Bottom Left vertex found = false; for (int i = height - 1; i > 0; i--) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); if (loc != null) { result[2] = new ResultPoint(loc[0], i); result[6] = new ResultPoint(loc[1], i); found = true; break; } } } // Bottom Right if (found) { // Found the Top Right vertex found = false; for (int i = 0; i < height; i++) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); if (loc != null) { result[3] = new ResultPoint(loc[0], i); result[7] = new ResultPoint(loc[1], i); found = true; break; } } } return found ? result : null; }
public Detector(BitMatrix image) { this.image = image; }
/// <summary> 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. /// </summary> private static BitMatrix extractPureBits(BitMatrix image) { // Now need to determine module size in pixels int height = image.Height; int width = image.Width; 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.get_Renamed(borderWidth, borderWidth)) { borderWidth++; } if (borderWidth == minDimension) { throw ReaderException.Instance; } // And then keep tracking across the top-left black module to determine module size int moduleEnd = borderWidth + 1; while (moduleEnd < width && image.get_Renamed(moduleEnd, borderWidth)) { moduleEnd++; } if (moduleEnd == width) { throw ReaderException.Instance; } 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.get_Renamed(borderWidth, columnEndOfSymbol)) { columnEndOfSymbol--; } if (columnEndOfSymbol < 0) { throw ReaderException.Instance; } columnEndOfSymbol++; // Make sure width of barcode is a multiple of module size if ((columnEndOfSymbol - borderWidth)%moduleSize != 0) { throw ReaderException.Instance; } 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 ReaderException.Instance; } // Now just read off the bits var 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.get_Renamed(borderWidth + j*moduleSize, iOffset)) { bits.set_Renamed(j, i); } } } return bits; }
internal BitMatrixParser(BitMatrix bitMatrix) { this.bitMatrix = bitMatrix; }
/// <summary> See ISO 18004:2006 Annex E</summary> internal BitMatrix buildFunctionPattern() { int dimension = DimensionForVersion; var bitMatrix = new BitMatrix(dimension); // Top left finder pattern + separator + format bitMatrix.setRegion(0, 0, 9, 9); // Top right finder pattern + separator + format bitMatrix.setRegion(dimension - 8, 0, 8, 9); // Bottom left finder pattern + separator + format bitMatrix.setRegion(0, dimension - 8, 9, 8); // Alignment patterns int max = alignmentPatternCenters.Length; for (int x = 0; x < max; x++) { int i = alignmentPatternCenters[x] - 2; for (int y = 0; y < max; y++) { if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) { // No alignment patterns near the three finder paterns continue; } bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5); } } // Vertical timing pattern bitMatrix.setRegion(6, 9, 1, dimension - 17); // Horizontal timing pattern bitMatrix.setRegion(9, 6, dimension - 17, 1); if (versionNumber > 6) { // Version info, top right bitMatrix.setRegion(dimension - 11, 0, 3, 6); // Version info, bottom left bitMatrix.setRegion(0, dimension - 11, 6, 3); } return bitMatrix; }