/** * 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. */ private static BitMatrix extractPureBits(MonochromeBitmapSource image) { // Now need to determine module size in pixels int height = image.getHeight(); int width = image.getWidth(); 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.isBlack(borderWidth, borderWidth)) { borderWidth++; } if (borderWidth == minDimension) { throw new ReaderException(); } // And then keep tracking across the top-left black module to determine module size int moduleEnd = borderWidth + 1; while (moduleEnd < width && image.isBlack(moduleEnd, borderWidth)) { moduleEnd++; } if (moduleEnd == width) { throw new ReaderException(); } 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.isBlack(borderWidth, columnEndOfSymbol)) { columnEndOfSymbol--; } if (columnEndOfSymbol < 0) { throw new ReaderException(); } columnEndOfSymbol++; // Make sure width of barcode is a multiple of module size if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) { throw new ReaderException(); } 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 new ReaderException(); } // 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.isBlack(borderWidth + j * moduleSize, iOffset)) { bits.set(i, j); } } } return(bits); }
/** * 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. */ private static BitMatrix extractPureBits(MonochromeBitmapSource image) { // Now need to determine module size in pixels int height = image.getHeight(); int width = image.getWidth(); 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.isBlack(borderWidth, borderWidth)) { borderWidth++; } if (borderWidth == minDimension) { throw new ReaderException(); } // And then keep tracking across the top-left black module to determine module size int moduleEnd = borderWidth + 1; while (moduleEnd < width && image.isBlack(moduleEnd, borderWidth)) { moduleEnd++; } if (moduleEnd == width) { throw new ReaderException(); } 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.isBlack(borderWidth, columnEndOfSymbol)) { columnEndOfSymbol--; } if (columnEndOfSymbol < 0) { throw new ReaderException(); } columnEndOfSymbol++; // Make sure width of barcode is a multiple of module size if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) { throw new ReaderException(); } 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 new ReaderException(); } // 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.isBlack(borderWidth + j * moduleSize, iOffset)) { bits.set(i, j); } } } return bits; }
/** * <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> */ 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 = Math.Abs(toY - fromY) > Math.Abs(toX - fromX); if (steep) { int temp = fromX; fromX = fromY; fromY = temp; temp = toX; toX = toY; toY = temp; } int dx = Math.Abs(toX - fromX); int dy = 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 int diffX = 0; int diffY = 0; 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.isBlack(realX, realY)) { state++; } } else { if (!image.isBlack(realX, realY)) { state++; } } if (state == 3) // Found black, white, black, and stumbled back onto white; done { diffX = x - fromX; diffY = y - fromY; return((float)Math.Sqrt((double)(diffX * diffX + diffY * diffY))); } error += dy; if (error > 0) { y += ystep; error -= dx; } } diffX = toX - fromX; diffY = toY - fromY; return((float)Math.Sqrt((double)(diffX * diffX + diffY * diffY))); }
/** * <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> * * @param startI row where an alignment pattern was detected * @param centerJ center of the section that appears to cross an alignment pattern * @param maxCount maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * @return vertical center of alignment pattern, or {@link Float#NaN} if not found */ private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) { MonochromeBitmapSource image = this.image; int maxI = image.getHeight(); int[] stateCount = crossCheckStateCount; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; // Start counting up from center int i = startI; while (i >= 0 && image.isBlack(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(float.NaN); } while (i >= 0 && !image.isBlack(centerJ, i) && stateCount[0] <= maxCount) { stateCount[0]++; i--; } if (stateCount[0] > maxCount) { return(float.NaN); } // Now also count down from center i = startI + 1; while (i < maxI && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) { stateCount[1]++; i++; } if (i == maxI || stateCount[1] > maxCount) { return(float.NaN); } while (i < maxI && !image.isBlack(centerJ, i) && stateCount[2] <= maxCount) { stateCount[2]++; i++; } if (stateCount[2] > maxCount) { return(float.NaN); } int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { return(float.NaN); } return(foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : float.NaN); }
/** * <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> */ private float crossCheckHorizontal(int startJ, int centerI, int maxCount, int originalStateCountTotal) { MonochromeBitmapSource image = this.image; int maxJ = image.getWidth(); int[] stateCount = getCrossCheckStateCount(); int j = startJ; while (j >= 0 && image.isBlack(j, centerI)) { stateCount[2]++; j--; } if (j < 0) { return(float.NaN); } while (j >= 0 && !image.isBlack(j, centerI) && stateCount[1] <= maxCount) { stateCount[1]++; j--; } if (j < 0 || stateCount[1] > maxCount) { return(float.NaN); } while (j >= 0 && image.isBlack(j, centerI) && stateCount[0] <= maxCount) { stateCount[0]++; j--; } if (stateCount[0] > maxCount) { return(float.NaN); } j = startJ + 1; while (j < maxJ && image.isBlack(j, centerI)) { stateCount[2]++; j++; } if (j == maxJ) { return(float.NaN); } while (j < maxJ && !image.isBlack(j, centerI) && stateCount[3] < maxCount) { stateCount[3]++; j++; } if (j == maxJ || stateCount[3] >= maxCount) { return(float.NaN); } while (j < maxJ && image.isBlack(j, centerI) && stateCount[4] < maxCount) { stateCount[4]++; j++; } if (stateCount[4] >= maxCount) { return(float.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 * Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { return(float.NaN); } return(foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : float.NaN); }
/** * <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> * * @param startI row where a finder pattern was detected * @param centerJ center of the section that appears to cross a finder pattern * @param maxCount maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * @return vertical center of finder pattern, or {@link Float#NaN} if not found */ private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) { MonochromeBitmapSource image = this.image; int maxI = image.getHeight(); int[] stateCount = getCrossCheckStateCount(); // Start counting up from center int i = startI; while (i >= 0 && image.isBlack(centerJ, i)) { stateCount[2]++; i--; } if (i < 0) { return(float.NaN); } while (i >= 0 && !image.isBlack(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(float.NaN); } while (i >= 0 && image.isBlack(centerJ, i) && stateCount[0] <= maxCount) { stateCount[0]++; i--; } if (stateCount[0] > maxCount) { return(float.NaN); } // Now also count down from center i = startI + 1; while (i < maxI && image.isBlack(centerJ, i)) { stateCount[2]++; i++; } if (i == maxI) { return(float.NaN); } while (i < maxI && !image.isBlack(centerJ, i) && stateCount[3] < maxCount) { stateCount[3]++; i++; } if (i == maxI || stateCount[3] >= maxCount) { return(float.NaN); } while (i < maxI && image.isBlack(centerJ, i) && stateCount[4] < maxCount) { stateCount[4]++; i++; } if (stateCount[4] >= maxCount) { return(float.NaN); } // If we found a finder-pattern-like section, but its size is more than 20% different than // the original, assume it's a false positive int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { return(float.NaN); } return(foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : float.NaN); }