private static BitMatrix sampleGrid(MonochromeBitmapSource image, ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension) { float dimMinusThree = (float)dimension - 3.5f; float bottomRightX; float bottomRightY; float sourceBottomRightX; float sourceBottomRightY; if (alignmentPattern != null) { bottomRightX = alignmentPattern.getX(); bottomRightY = alignmentPattern.getY(); sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f; } else { // Don't have an alignment pattern, just make up the bottom-right point bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX(); bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY(); sourceBottomRightX = sourceBottomRightY = dimMinusThree; } GridSampler sampler = GridSampler.Instance; return(sampler.sampleGrid( image, dimension, 3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, sourceBottomRightY, 3.5f, dimMinusThree, topLeft.getX(), topLeft.getY(), topRight.getX(), topRight.getY(), bottomRightX, bottomRightY, bottomLeft.getX(), bottomLeft.getY())); }
/** * Returns the z component of the cross product between vectors BC and BA. */ public static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) { float bX = pointB.getX(); float bY = pointB.getY(); return(((pointC.getX() - bX) * (pointA.getY() - bY)) - ((pointC.getY() - bY) * (pointA.getX() - bX))); }
/** * @return distance between two points */ public static float distance(ResultPoint pattern1, ResultPoint pattern2) { float xDiff = pattern1.getX() - pattern2.getX(); float yDiff = pattern1.getY() - pattern2.getY(); return((float)Math.Sqrt((double)(xDiff * xDiff + yDiff * yDiff))); }
/** * <p>Estimates module size based on two finder patterns -- it uses * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the * width of each, measuring along the axis between their centers.</p> */ private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) { float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern.getX(), (int)pattern.getY(), (int)otherPattern.getX(), (int)otherPattern.getY()); float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern.getX(), (int)otherPattern.getY(), (int)pattern.getX(), (int)pattern.getY()); if (Single.IsNaN(moduleSizeEst1)) { return(moduleSizeEst2); } if (Single.IsNaN(moduleSizeEst2)) { return(moduleSizeEst1); } // Average them, and divide by 7 since we've counted the width of 3 black modules, // and 1 white and 1 black module on either side. Ergo, divide sum by 14. return((moduleSizeEst1 + moduleSizeEst2) / 14.0f); }
private static BitMatrix sampleGrid(MonochromeBitmapSource image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension) { // We make up the top right point for now, based on the others. // TODO: we actually found a fourth corner above and figured out which of two modules // it was the corner of. We could use that here and adjust for perspective distortion. float topRightX = (bottomRight.getX() - bottomLeft.getX()) + topLeft.getX(); float topRightY = (bottomRight.getY() - bottomLeft.getY()) + topLeft.getY(); // Note that unlike in 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( image, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.getX(), topLeft.getY(), topRightX, topRightY, bottomRight.getX(), bottomRight.getY(), bottomLeft.getX(), bottomLeft.getY())); }
/** * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm. */ private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to) { // See QR Code Detector, sizeOfBlackWhiteBlackRun() int fromX = (int)from.getX(); int fromY = (int)from.getY(); int toX = (int)to.getX(); int toY = (int)to.getY(); 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 transitions = 0; bool inBlack = image.isBlack(steep ? fromY : fromX, steep ? fromX : fromY); for (int x = fromX, y = fromY; x != toX; x += xstep) { bool isBlack = image.isBlack(steep ? y : x, steep ? x : y); if (isBlack == !inBlack) { transitions++; inBlack = isBlack; } error += dy; if (error > 0) { y += ystep; error -= dx; } } return(new ResultPointsAndTransitions(from, to, transitions)); }
/** * <p>Detects a Data Matrix Code in an image.</p> * * @return {@link DetectorResult} encapsulating results of detecting a QR Code * @throws ReaderException if no Data Matrix Code can be found */ public DetectorResult detect() { if (!BlackPointEstimationMethod.TWO_D_SAMPLING.Equals(image.getLastEstimationMethod())) { image.estimateBlackPoint(BlackPointEstimationMethod.TWO_D_SAMPLING, 0); } int height = image.getHeight(); int width = image.getWidth(); int halfHeight = height >> 1; int halfWidth = width >> 1; int iSkip = Math.Max(1, height / (MAX_MODULES << 3)); int jSkip = Math.Max(1, width / (MAX_MODULES << 3)); int minI = 0; int maxI = height; int minJ = 0; int maxJ = width; ResultPoint pointA = findCornerFromCenter(halfHeight, -iSkip, minI, maxI, halfWidth, 0, minJ, maxJ, halfWidth >> 1); minI = (int)pointA.getY() - 1; ResultPoint pointB = findCornerFromCenter(halfHeight, 0, minI, maxI, halfWidth, -jSkip, minJ, maxJ, halfHeight >> 1); minJ = (int)pointB.getX() - 1; ResultPoint pointC = findCornerFromCenter(halfHeight, 0, minI, maxI, halfWidth, jSkip, minJ, maxJ, halfHeight >> 1); maxJ = (int)pointC.getX() + 1; ResultPoint pointD = findCornerFromCenter(halfHeight, iSkip, minI, maxI, halfWidth, 0, minJ, maxJ, halfWidth >> 1); maxI = (int)pointD.getY() + 1; // Go try to find point A again with better information -- might have been off at first. pointA = findCornerFromCenter(halfHeight, -iSkip, minI, maxI, halfWidth, 0, minJ, maxJ, halfWidth >> 2); // Point A and D are across the diagonal from one another, // as are B and C. Figure out which are the solid black lines // by counting transitions System.Collections.ArrayList transitions = new System.Collections.ArrayList(4); transitions.Add(transitionsBetween(pointA, pointB)); transitions.Add(transitionsBetween(pointA, pointC)); transitions.Add(transitionsBetween(pointB, pointD)); transitions.Add(transitionsBetween(pointC, pointD)); Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator()); // Sort by number of transitions. First two will be the two solid sides; last two // will be the two alternating black/white sides ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions)transitions[0]; ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions)transitions[1]; // Figure out which point is their intersection by tallying up the number of times we see the // endpoints in the four endpoints. One will show up twice. System.Collections.Hashtable pointCount = new System.Collections.Hashtable(); increment(pointCount, lSideOne.getFrom()); increment(pointCount, lSideOne.getTo()); increment(pointCount, lSideTwo.getFrom()); increment(pointCount, lSideTwo.getTo()); ResultPoint maybeTopLeft = null; ResultPoint bottomLeft = null; ResultPoint maybeBottomRight = null; System.Collections.IEnumerator points = pointCount.GetEnumerator(); while (points.MoveNext()) { ResultPoint point = (ResultPoint)points.Current; int value = (int)pointCount[point]; if (value == 2) { bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides } else { // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now if (maybeTopLeft == null) { maybeTopLeft = point; } else { maybeBottomRight = point; } } } if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) { throw new ReaderException(); } // Bottom left is correct but top left and bottom right might be switched ResultPoint[] corners = { maybeTopLeft, bottomLeft, maybeBottomRight }; // Use the dot product trick to sort them out GenericResultPoint.orderBestPatterns(corners); // Now we know which is which: ResultPoint bottomRight = corners[0]; bottomLeft = corners[1]; ResultPoint topLeft = corners[2]; // Which point didn't we find in relation to the "L" sides? that's the top right corner ResultPoint topRight; if (!pointCount.ContainsKey(pointA)) { topRight = pointA; } else if (!pointCount.ContainsKey(pointB)) { topRight = pointB; } else if (!pointCount.ContainsKey(pointC)) { topRight = pointC; } else { topRight = pointD; } // Next determine the dimension by tracing along the top or right side and counting black/white // transitions. Since we start inside a black module, we should see a number of transitions // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to // end on a black module: // The top right point is actually the corner of a module, which is one of the two black modules // adjacent to the white module at the top right. Tracing to that corner from either the top left // or bottom right should work here, but, one will be more reliable since it's traced straight // up or across, rather than at a slight angle. We use dot products to figure out which is // better to use: int dimension; if (GenericResultPoint.crossProductZ(bottomLeft, bottomRight, topRight) < GenericResultPoint.crossProductZ(topRight, topLeft, bottomLeft)) { dimension = transitionsBetween(topLeft, topRight).getTransitions(); } else { dimension = transitionsBetween(bottomRight, topRight).getTransitions(); } dimension += 2; BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension); return(new DetectorResult(bits, new ResultPoint[] { pointA, pointB, pointC, pointD })); }
/** * Returns the z component of the cross product between vectors BC and BA. */ public static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) { float bX = pointB.getX(); float bY = pointB.getY(); return ((pointC.getX() - bX) * (pointA.getY() - bY)) - ((pointC.getY() - bY) * (pointA.getX() - bX)); }
/** * @return distance between two points */ public static float distance(ResultPoint pattern1, ResultPoint pattern2) { float xDiff = pattern1.getX() - pattern2.getX(); float yDiff = pattern1.getY() - pattern2.getY(); return (float) Math.Sqrt((double) (xDiff * xDiff + yDiff * yDiff)); }