import com.google.zxing.qrcode.decoder.Version;
/**
+ * <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
+ * is rotated or skewed, or partially obscured.</p>
+ *
* @author srowen@google.com (Sean Owen)
*/
public final class Detector {
this.image = image;
}
+ /**
+ * <p>Detects a QR Code in an image, simply.</p>
+ *
+ * @return {@link DetectorResult} encapsulating results of detecting a QR Code
+ * @throws ReaderException if no QR Code can be found
+ */
public DetectorResult detect() throws ReaderException {
MonochromeBitmapSource image = this.image;
estAlignmentY,
(float) i);
break;
- } catch (ReaderException de) {
+ } catch (ReaderException re) {
// try next round
}
}
}
GridSampler sampler = GridSampler.getInstance();
- BitMatrix bits = sampler.sampleGrid(image,
- topLeft,
- topRight,
- bottomLeft,
- alignmentPattern,
- dimension);
+ BitMatrix bits = sampler.sampleGrid(image, topLeft, topRight, bottomLeft, alignmentPattern, dimension);
/*
try {
}
}
}
- ImageIO.write(outImage, "PNG",
- new File("/home/srowen/out.png"));
+ ImageIO.write(outImage, "PNG", new File("/tmp/out.png"));
} catch (IOException ioe) {
ioe.printStackTrace();
}
return new DetectorResult(bits, points);
}
+ /**
+ * <p>Computes the dimension (number of modules on a size) of the QR Code based on the position
+ * of the finder patterns and estimated module size.</p>
+ */
private static int computeDimension(ResultPoint topLeft,
ResultPoint topRight,
ResultPoint bottomLeft,
- float moduleSize)
- throws ReaderException {
- int tltrCentersDimension =
- round(FinderPatternFinder.distance(topLeft, topRight) / moduleSize);
- int tlblCentersDimension =
- round(FinderPatternFinder.distance(topLeft, bottomLeft) / moduleSize);
+ float moduleSize) throws ReaderException {
+ int tltrCentersDimension = round(FinderPatternFinder.distance(topLeft, topRight) / moduleSize);
+ int tlblCentersDimension = round(FinderPatternFinder.distance(topLeft, bottomLeft) / moduleSize);
int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
switch (dimension & 0x03) { // mod 4
case 0:
dimension++;
break;
- // 1? do nothing
+ // 1? do nothing
case 2:
dimension--;
break;
return dimension;
}
- private float calculateModuleSize(ResultPoint topLeft,
- ResultPoint topRight,
- ResultPoint bottomLeft) {
+ /**
+ * <p>Computes an average estimated module size based on estimated derived from the positions
+ * of the three finder patterns.</p>
+ */
+ private float calculateModuleSize(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft) {
// Take the average
return (calculateModuleSizeOneWay(topLeft, topRight) +
calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;
}
- private float calculateModuleSizeOneWay(ResultPoint pattern,
- ResultPoint otherPattern) {
+ /**
+ * <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(),
return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
}
+ /**
+ * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
+ * a finder pattern by looking for a black-white-black run from the center in the direction
+ * of another point (another finder pattern center), and in the opposite direction too.</p>
+ */
private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {
float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
result += sizeOfBlackWhiteBlackRun(fromX, fromY, fromX - (toX - fromX), fromY - (toY - fromY));
return result - 1.0f; // -1 because we counted the middle pixel twice
}
+ /**
+ * <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
return Float.NaN;
}
+ /**
+ * <p>Attempts to locate an alignment pattern in a limited region of the image, which is
+ * guessed to contain it. This method uses {@link AlignmentPattern}.</p>
+ *
+ * @param overallEstModuleSize estimated module size so far
+ * @param estAlignmentX x coordinate of center of area probably containing alignment pattern
+ * @param estAlignmentY y coordinate of above
+ * @param allowanceFactor number of pixels in all directons to search from the center
+ * @return {@link AlignmentPattern} if found, or null otherwise
+ * @throws ReaderException if an unexpected error occurs during detection
+ */
private AlignmentPattern findAlignmentInRegion(float overallEstModuleSize,
int estAlignmentX,
int estAlignmentY,
// should be
int allowance = (int) (allowanceFactor * overallEstModuleSize);
int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
- int alignmentAreaRightX = Math.min(image.getWidth() - 1,
- estAlignmentX + allowance);
+ int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance);
int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
- int alignmentAreaBottomY = Math.min(image.getHeight() - 1,
- estAlignmentY + allowance);
+ int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance);
AlignmentPatternFinder alignmentFinder =
new AlignmentPatternFinder(
}
/**
- * Ends up being a bit faster than Math.round()
+ * Ends up being a bit faster than Math.round(). This merely rounds its argument to the nearest int,
+ * where x.5 rounds up.
*/
private static int round(float d) {
return (int) (d + 0.5f);