/// <summary> <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p> /// /// </summary> /// <param name="row">Row to read in the mapping matrix /// </param> /// <param name="column">Column to read in the mapping matrix /// </param> /// <param name="numRows">Number of rows in the mapping matrix /// </param> /// <param name="numColumns">Number of columns in the mapping matrix /// </param> /// <returns> value of the given bit in the mapping matrix /// </returns> internal bool readModule(int row, int column, int numRows, int numColumns) { // Adjust the row and column indices based on boundary wrapping if (row < 0) { row += numRows; column += 4 - ((numRows + 4) & 0x07); } if (column < 0) { column += numColumns; row += 4 - ((numColumns + 4) & 0x07); } readMappingMatrix.set_Renamed(column, row); return(mappingBitMatrix.get_Renamed(column, row)); }
/// <summary> <p>Extracts the data region from a {@link BitMatrix} that contains /// alignment patterns.</p> /// /// </summary> /// <param name="bitMatrix">Original {@link BitMatrix} with alignment patterns /// </param> /// <returns> BitMatrix that has the alignment patterns removed /// </returns> internal BitMatrix extractDataRegion(BitMatrix bitMatrix) { int symbolSizeRows = version.SymbolSizeRows; int symbolSizeColumns = version.SymbolSizeColumns; // TODO(bbrown): Make this work with rectangular codes if (bitMatrix.Dimension != symbolSizeRows) { throw new System.ArgumentException("Dimension of bitMarix must match the version size"); } int dataRegionSizeRows = version.DataRegionSizeRows; int dataRegionSizeColumns = version.DataRegionSizeColumns; int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows; int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; // TODO(bbrown): Make this work with rectangular codes BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow); for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; for (int i = 0; i < dataRegionSizeRows; ++i) { int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; int writeRowOffset = dataRegionRowOffset + i; for (int j = 0; j < dataRegionSizeColumns; ++j) { int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; if (bitMatrix.get_Renamed(readColumnOffset, readRowOffset)) { int writeColumnOffset = dataRegionColumnOffset + j; bitMatrixWithoutAlignment.set_Renamed(writeColumnOffset, writeRowOffset); } } } } } return(bitMatrixWithoutAlignment); }
/// <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. int[] 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 int[] { 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); }
/// <summary> <p>This method traces a line from a point in the image, in the direction towards another point. /// It begins in a black region, and keeps going until it finds white, then black, then white again. /// It reports the distance from the start to this point.</p> /// /// <p>This is used when figuring out how wide a finder pattern is, when the finder pattern /// may be skewed or rotated.</p> /// </summary> private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) { // Mild variant of Bresenham's algorithm; // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX); if (steep) { int temp = fromX; fromX = fromY; fromY = temp; temp = toX; toX = toY; toY = temp; } int dx = System.Math.Abs(toX - fromX); int dy = System.Math.Abs(toY - fromY); int error = -dx >> 1; int ystep = fromY < toY?1:-1; int xstep = fromX < toX?1:-1; int state = 0; // In black pixels, looking for white, first or second time for (int x = fromX, y = fromY; x != toX; x += xstep) { int realX = steep?y:x; int realY = steep?x:y; if (state == 1) { // In white pixels, looking for black if (_image.get_Renamed(realX, realY)) { state++; } } else { if (!_image.get_Renamed(realX, realY)) { state++; } } if (state == 3) { // Found black, white, black, and stumbled back onto white; done int diffX = x - fromX; int diffY = y - fromY; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" return((float)System.Math.Sqrt(diffX * diffX + diffY * diffY)); } error += dy; if (error > 0) { if (y == toY) { break; } y += ystep; error -= dx; } } int diffX2 = toX - fromX; int diffY2 = toY - fromY; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" return((float)System.Math.Sqrt(diffX2 * diffX2 + diffY2 * diffY2)); }
/// <summary> <p>Extracts the data region from a {@link BitMatrix} that contains /// alignment patterns.</p> /// /// </summary> /// <param name="bitMatrix">Original {@link BitMatrix} with alignment patterns /// </param> /// <returns> BitMatrix that has the alignment patterns removed /// </returns> internal BitMatrix extractDataRegion(BitMatrix bitMatrix) { int symbolSizeRows = version.SymbolSizeRows; int symbolSizeColumns = version.SymbolSizeColumns; // TODO(bbrown): Make this work with rectangular codes if (bitMatrix.Dimension != symbolSizeRows) { throw new System.ArgumentException("Dimension of bitMarix must match the version size"); } int dataRegionSizeRows = version.DataRegionSizeRows; int dataRegionSizeColumns = version.DataRegionSizeColumns; int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows; int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; // TODO(bbrown): Make this work with rectangular codes BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow); for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; for (int i = 0; i < dataRegionSizeRows; ++i) { int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; int writeRowOffset = dataRegionRowOffset + i; for (int j = 0; j < dataRegionSizeColumns; ++j) { int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; if (bitMatrix.get_Renamed(readColumnOffset, readRowOffset)) { int writeColumnOffset = dataRegionColumnOffset + j; bitMatrixWithoutAlignment.set_Renamed(writeColumnOffset, writeRowOffset); } } } } } return bitMatrixWithoutAlignment; }
/// <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. int[] 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 int[]{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; }
/// <summary> <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> /// /// </summary> /// <returns> {@link AlignmentPattern} if found /// </returns> /// <throws> ReaderException if not found </throws> internal AlignmentPattern find() { int startX = this.startX; int height = this.height; int maxJ = startX + width; int middleI = startY + (height >> 1); // 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)); 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 && !image.get_Renamed(j, i)) { j++; } int currentState = 0; while (j < maxJ) { if (image.get_Renamed(j, i)) { // 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 ReaderException.Instance; }
/// <summary> <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> /// /// </summary> /// <param name="startI">row where an alignment pattern was detected /// </param> /// <param name="centerJ">center of the section that appears to cross an alignment pattern /// </param> /// <param name="maxCount">maximum reasonable number of modules that should be /// observed in any reading state, based on the results of the horizontal scan /// </param> /// <returns> vertical center of alignment pattern, or {@link Float#NaN} if not found /// </returns> private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) { BitMatrix image = this.image; int maxI = image.Height; int[] stateCount = crossCheckStateCount; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; // Start counting up from center int i = startI; while (i >= 0 && image.get_Renamed(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(System.Single.NaN); } while (i >= 0 && !image.get_Renamed(centerJ, i) && stateCount[0] <= maxCount) { stateCount[0]++; i--; } if (stateCount[0] > maxCount) { return(System.Single.NaN); } // Now also count down from center i = startI + 1; while (i < maxI && image.get_Renamed(centerJ, i) && stateCount[1] <= maxCount) { stateCount[1]++; i++; } if (i == maxI || stateCount[1] > maxCount) { return(System.Single.NaN); } while (i < maxI && !image.get_Renamed(centerJ, i) && stateCount[2] <= maxCount) { stateCount[2]++; i++; } if (stateCount[2] > maxCount) { return(System.Single.NaN); } int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; if (5 * System.Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { return(System.Single.NaN); } return(foundPatternCross(stateCount)?centerFromEnd(stateCount, i):System.Single.NaN); }
/// <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(BitMatrix image) { // Now need to determine module size in pixels int height = image.Height; int width = image.Width; int minDimension = System.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; while (moduleEnd < minDimension && image.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 && !image.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 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.get_Renamed(borderWidth + j * moduleSize, iOffset)) { bits.set_Renamed(j, i); } } } return(bits); }
/// <summary> <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, /// except it reads horizontally instead of vertically. This is used to cross-cross /// check a vertical cross check and locate the real center of the alignment pattern.</p> /// </summary> private float crossCheckHorizontal(int startJ, int centerI, int maxCount, int originalStateCountTotal) { BitMatrix image = this.image; int maxJ = image.Width; int[] stateCount = CrossCheckStateCount; int j = startJ; while (j >= 0 && image.get_Renamed(j, centerI)) { stateCount[2]++; j--; } if (j < 0) { return(System.Single.NaN); } while (j >= 0 && !image.get_Renamed(j, centerI) && stateCount[1] <= maxCount) { stateCount[1]++; j--; } if (j < 0 || stateCount[1] > maxCount) { return(System.Single.NaN); } while (j >= 0 && image.get_Renamed(j, centerI) && stateCount[0] <= maxCount) { stateCount[0]++; j--; } if (stateCount[0] > maxCount) { return(System.Single.NaN); } j = startJ + 1; while (j < maxJ && image.get_Renamed(j, centerI)) { stateCount[2]++; j++; } if (j == maxJ) { return(System.Single.NaN); } while (j < maxJ && !image.get_Renamed(j, centerI) && stateCount[3] < maxCount) { stateCount[3]++; j++; } if (j == maxJ || stateCount[3] >= maxCount) { return(System.Single.NaN); } while (j < maxJ && image.get_Renamed(j, centerI) && stateCount[4] < maxCount) { stateCount[4]++; j++; } if (stateCount[4] >= maxCount) { return(System.Single.NaN); } // If we found a finder-pattern-like section, but its size is significantly different than // the original, assume it's a false positive int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; if (5 * System.Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { return(System.Single.NaN); } return(foundPatternCross(stateCount)?centerFromEnd(stateCount, j):System.Single.NaN); }
/// <summary> <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> /// /// </summary> /// <param name="startI">row where a finder pattern was detected /// </param> /// <param name="centerJ">center of the section that appears to cross a finder pattern /// </param> /// <param name="maxCount">maximum reasonable number of modules that should be /// observed in any reading state, based on the results of the horizontal scan /// </param> /// <returns> vertical center of finder pattern, or {@link Float#NaN} if not found /// </returns> private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) { BitMatrix image = this.image; int maxI = image.Height; int[] stateCount = CrossCheckStateCount; // Start counting up from center int i = startI; while (i >= 0 && image.get_Renamed(centerJ, i)) { stateCount[2]++; i--; } if (i < 0) { return(System.Single.NaN); } while (i >= 0 && !image.get_Renamed(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(System.Single.NaN); } while (i >= 0 && image.get_Renamed(centerJ, i) && stateCount[0] <= maxCount) { stateCount[0]++; i--; } if (stateCount[0] > maxCount) { return(System.Single.NaN); } // Now also count down from center i = startI + 1; while (i < maxI && image.get_Renamed(centerJ, i)) { stateCount[2]++; i++; } if (i == maxI) { return(System.Single.NaN); } while (i < maxI && !image.get_Renamed(centerJ, i) && stateCount[3] < maxCount) { stateCount[3]++; i++; } if (i == maxI || stateCount[3] >= maxCount) { return(System.Single.NaN); } while (i < maxI && image.get_Renamed(centerJ, i) && stateCount[4] < maxCount) { stateCount[4]++; i++; } if (stateCount[4] >= maxCount) { return(System.Single.NaN); } // If we found a finder-pattern-like section, but its size is more than 40% different than // the original, assume it's a false positive int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; if (5 * System.Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { return(System.Single.NaN); } return(foundPatternCross(stateCount)?centerFromEnd(stateCount, i):System.Single.NaN); }
/// <summary> <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the /// correct order in order to reconstitute the codewords bytes contained within the /// QR Code.</p> /// /// </summary> /// <returns> bytes encoded within the QR Code /// </returns> /// <throws> ReaderException if the exact number of bytes expected is not read </throws> internal sbyte[] readCodewords() { FormatInformation formatInfo = readFormatInformation(); Version version = readVersion(); // Get the data mask for the format used in this QR Code. This will exclude // some bits from reading as we wind through the bit matrix. DataMask dataMask = DataMask.forReference((int)formatInfo.DataMask); int dimension = bitMatrix.Dimension; dataMask.unmaskBitMatrix(bitMatrix, dimension); BitMatrix functionPattern = version.buildFunctionPattern(); bool readingUp = true; sbyte[] result = new sbyte[version.TotalCodewords]; int resultOffset = 0; int currentByte = 0; int bitsRead = 0; // Read columns in pairs, from right to left for (int j = dimension - 1; j > 0; j -= 2) { if (j == 6) { // Skip whole column with vertical alignment pattern; // saves time and makes the other code proceed more cleanly j--; } // Read alternatingly from bottom to top then top to bottom for (int count = 0; count < dimension; count++) { int i = readingUp?dimension - 1 - count:count; for (int col = 0; col < 2; col++) { // Ignore bits covered by the function pattern if (!functionPattern.get_Renamed(j - col, i)) { // Read a bit bitsRead++; currentByte <<= 1; if (bitMatrix.get_Renamed(j - col, i)) { currentByte |= 1; } // If we've made a whole byte, save it off if (bitsRead == 8) { result[resultOffset++] = (sbyte)currentByte; bitsRead = 0; currentByte = 0; } } } } readingUp ^= true; // readingUp = !readingUp; // switch directions } if (resultOffset != version.TotalCodewords) { throw ReaderException.Instance; } return(result); }
private int copyBit(int i, int j, int versionBits) { return(bitMatrix.get_Renamed(i, j)?(versionBits << 1) | 0x1:versionBits << 1); }
public FinderPatternInfo[] findMulti(System.Collections.Hashtable hints) { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); BitMatrix image = Image; int maxI = image.Height; int maxJ = image.Width; // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" int iSkip = (int)(maxI / (MAX_MODULES * 4.0f) * 3); if (iSkip < MIN_SKIP || tryHarder) { iSkip = MIN_SKIP; } int[] stateCount = new int[5]; for (int i = iSkip - 1; i < maxI; i += iSkip) { // Get a row of black/white values stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; int currentState = 0; for (int j = 0; j < maxJ; j++) { if (image.get_Renamed(j, i)) { // Black pixel if ((currentState & 1) == 1) { // Counting white pixels currentState++; } stateCount[currentState]++; } else { // White pixel if ((currentState & 1) == 0) { // Counting black pixels if (currentState == 4) { // A winner? if (foundPatternCross(stateCount)) { // Yes bool confirmed = handlePossibleCenter(stateCount, i, j); if (!confirmed) { do { // Advance to next black pixel j++; }while (j < maxJ && !image.get_Renamed(j, i)); j--; // back up to that last white pixel } // Clear state to start looking again currentState = 0; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; } else { // No, shift counts back by two stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; } } else { stateCount[++currentState]++; } } else { // Counting white pixels stateCount[currentState]++; } } } // for j=... if (foundPatternCross(stateCount)) { handlePossibleCenter(stateCount, i, maxJ); } // end if foundPatternCross } // for i=iSkip-1 ... FinderPattern[][] patternInfo = selectBestPatterns(); System.Collections.ArrayList result = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); for (int i = 0; i < patternInfo.Length; i++) { FinderPattern[] pattern = patternInfo[i]; ResultPoint.orderBestPatterns(pattern); result.Add(new FinderPatternInfo(pattern)); } if ((result.Count == 0)) { return(EMPTY_RESULT_ARRAY); } else { FinderPatternInfo[] resultArray = new FinderPatternInfo[result.Count]; for (int i = 0; i < result.Count; i++) { resultArray[i] = (FinderPatternInfo)result[i]; } return(resultArray); } }
/// <summary> <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns) /// in the correct order in order to reconstitute the codewords bytes contained within the /// Data Matrix Code.</p> /// /// </summary> /// <returns> bytes encoded within the Data Matrix Code /// </returns> /// <throws> ReaderException if the exact number of bytes expected is not read </throws> internal sbyte[] readCodewords() { sbyte[] result = new sbyte[version.TotalCodewords]; int resultOffset = 0; int row = 4; int column = 0; // TODO(bbrown): Data Matrix can be rectangular, assuming square for now int numRows = mappingBitMatrix.Dimension; int numColumns = numRows; bool corner1Read = false; bool corner2Read = false; bool corner3Read = false; bool corner4Read = false; // Read all of the codewords do { // Check the four corner cases if ((row == numRows) && (column == 0) && !corner1Read) { result[resultOffset++] = (sbyte)readCorner1(numRows, numColumns); row -= 2; column += 2; corner1Read = true; } else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) { result[resultOffset++] = (sbyte)readCorner2(numRows, numColumns); row -= 2; column += 2; corner2Read = true; } else if ((row == numRows + 4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) { result[resultOffset++] = (sbyte)readCorner3(numRows, numColumns); row -= 2; column += 2; corner3Read = true; } else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) { result[resultOffset++] = (sbyte)readCorner4(numRows, numColumns); row -= 2; column += 2; corner4Read = true; } else { // Sweep upward diagonally to the right do { if ((row < numRows) && (column >= 0) && !readMappingMatrix.get_Renamed(column, row)) { result[resultOffset++] = (sbyte)readUtah(row, column, numRows, numColumns); } row -= 2; column += 2; }while ((row >= 0) && (column < numColumns)); row += 1; column += 3; // Sweep downward diagonally to the left do { if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get_Renamed(column, row)) { result[resultOffset++] = (sbyte)readUtah(row, column, numRows, numColumns); } row += 2; column -= 2; }while ((row < numRows) && (column >= 0)); row += 3; column += 1; } }while ((row < numRows) || (column < numColumns)); if (resultOffset != version.TotalCodewords) { throw ReaderException.Instance; } return(result); }
/// <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 = System.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 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.get_Renamed(borderWidth + j * moduleSize, iOffset)) { bits.set_Renamed(j, i); } } } return bits; }