Represents a 2D matrix of bits. In function arguments below, and throughout the common module, x is the column position, and y is the row position. The ordering is always x, y. The origin is at the top-left.

Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins with a new int. This is done intentionally so that we can copy out a row into a BitArray very efficiently.

The ordering of bits is row-major. Within each int, the least significant bits are used first, meaning they represent lower x values. This is compatible with BitArray's implementation.

예제 #1
0
 public BinaryBitmap(Binarizer binarizer)
 {
     if (binarizer == null)
     {
         throw new ArgumentException("Binarizer must be non-null.");
     }
     this.binarizer = binarizer;
     matrix = null;
 }
예제 #2
0
 /// <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;
 }
예제 #3
0
 /// <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);
             }
         }
     }
 }
예제 #4
0
        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);
        }
예제 #5
0
        //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);
        }
예제 #6
0
        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;
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
 private static BitMatrix sampleGrid(BitMatrix image, PerspectiveTransform transform, int dimension)
 {
     GridSampler sampler = GridSampler.Instance;
     return sampler.sampleGrid(image, dimension, transform);
 }
예제 #10
0
        /// <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;
        }
예제 #11
0
        /// <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;
        }
예제 #12
0
        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
        }
예제 #13
0
        /// <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;
        }
예제 #14
0
 public Detector(BitMatrix image)
 {
     this.image = image;
 }
예제 #15
0
        /// <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;
        }
예제 #16
0
 internal BitMatrixParser(BitMatrix bitMatrix)
 {
     this.bitMatrix = bitMatrix;
 }
예제 #17
0
        /// <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;
        }