/*
- * Copyright 2007 Google Inc.
+ * Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import com.google.zxing.ReaderException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.DetectorResult;
+import com.google.zxing.common.GridSampler;
import com.google.zxing.qrcode.decoder.Version;
+import java.util.Hashtable;
+
/**
* <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)
+ * @author Sean Owen
*/
-public final class Detector {
+public class Detector {
private final MonochromeBitmapSource image;
this.image = image;
}
+ protected MonochromeBitmapSource getImage() {
+ return image;
+ }
+
/**
* <p>Detects a QR Code in an image, simply.</p>
*
* @throws ReaderException if no QR Code can be found
*/
public DetectorResult detect() throws ReaderException {
+ return detect(null);
+ }
+
+ /**
+ * <p>Detects a QR Code in an image, simply.</p>
+ *
+ * @param hints optional hints to detector
+ * @return {@link DetectorResult} encapsulating results of detecting a QR Code
+ * @throws ReaderException if no QR Code can be found
+ */
+ public DetectorResult detect(Hashtable hints) throws ReaderException {
MonochromeBitmapSource image = this.image;
if (!BlackPointEstimationMethod.TWO_D_SAMPLING.equals(image.getLastEstimationMethod())) {
}
FinderPatternFinder finder = new FinderPatternFinder(image);
- FinderPatternInfo info = finder.find();
+ FinderPatternInfo info = finder.find(hints);
+
+ return processFinderPatternInfo(info);
+ }
+
+ protected DetectorResult processFinderPatternInfo(FinderPatternInfo info) throws ReaderException {
FinderPattern topLeft = info.getTopLeft();
FinderPattern topRight = info.getTopRight();
FinderPattern bottomLeft = info.getBottomLeft();
float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
+ if (moduleSize < 1.0f) {
+ throw ReaderException.getInstance();
+ }
int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7;
- // Guess where a "bottom right" finder pattern would have been
- float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();
- float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();
-
AlignmentPattern alignmentPattern = null;
// Anything above version 1 has an alignment pattern
if (provisionalVersion.getAlignmentPatternCenters().length > 0) {
+ // Guess where a "bottom right" finder pattern would have been
+ float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();
+ float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();
+
// Estimate that alignment pattern is closer by 3 modules
// from "bottom right" to known top left location
float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters;
// try next round
}
}
- if (alignmentPattern == null) {
- throw new ReaderException("Could not find alignment pattern");
- }
-
+ // If we didn't find alignment pattern... well try anyway without it
}
- GridSampler sampler = GridSampler.getInstance();
- BitMatrix bits = sampler.sampleGrid(image, topLeft, topRight, bottomLeft, alignmentPattern, dimension);
+ BitMatrix bits = sampleGrid(image, topLeft, topRight, bottomLeft, alignmentPattern, dimension);
ResultPoint[] points;
if (alignmentPattern == null) {
return new DetectorResult(bits, points);
}
+ private static BitMatrix sampleGrid(MonochromeBitmapSource image,
+ ResultPoint topLeft,
+ ResultPoint topRight,
+ ResultPoint bottomLeft,
+ ResultPoint alignmentPattern,
+ int dimension) throws ReaderException {
+ 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.getInstance();
+ 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());
+ }
+
/**
* <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>
ResultPoint topRight,
ResultPoint bottomLeft,
float moduleSize) throws ReaderException {
- int tltrCentersDimension = round(FinderPatternFinder.distance(topLeft, topRight) / moduleSize);
- int tlblCentersDimension = round(FinderPatternFinder.distance(topLeft, bottomLeft) / moduleSize);
+ int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize);
+ int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize);
int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
switch (dimension & 0x03) { // mod 4
case 0:
dimension--;
break;
case 3:
- throw new ReaderException("Bad dimension: " + dimension);
+ throw ReaderException.getInstance();
}
return dimension;
}
// Now count other way -- don't run off image though of course
int otherToX = fromX - (toX - fromX);
if (otherToX < 0) {
- otherToX = 0;
+ // "to" should the be the first value not included, so, the first value off
+ // the edge is -1
+ otherToX = -1;
} else if (otherToX >= image.getWidth()) {
otherToX = image.getWidth();
}
int otherToY = fromY - (toY - fromY);
if (otherToY < 0) {
- otherToY = 0;
+ otherToY = -1;
} else if (otherToY >= image.getHeight()) {
otherToY = image.getHeight();
}
int allowance = (int) (allowanceFactor * overallEstModuleSize);
int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance);
+ if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
+ throw ReaderException.getInstance();
+ }
+
int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance);