package com.google.zxing.datamatrix.detector;
-import com.google.zxing.MonochromeBitmapSource;
-import com.google.zxing.ReaderException;
+import com.google.zxing.NotFoundException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.Collections;
import com.google.zxing.common.Comparator;
import com.google.zxing.common.DetectorResult;
-import com.google.zxing.common.GenericResultPoint;
import com.google.zxing.common.GridSampler;
-import com.google.zxing.common.detector.MonochromeRectangleDetector;
+import com.google.zxing.common.detector.WhiteRectangleDetector;
import java.util.Enumeration;
import java.util.Hashtable;
*/
public final class Detector {
- private static final int MAX_MODULES = 32;
-
// Trick to avoid creating new Integer objects below -- a sort of crude copy of
// the Integer.valueOf(int) optimization added in Java 5, not in J2ME
private static final Integer[] INTEGERS =
{ new Integer(0), new Integer(1), new Integer(2), new Integer(3), new Integer(4) };
+ // No, can't use valueOf()
- private final MonochromeBitmapSource image;
- private final MonochromeRectangleDetector rectangleDetector;
+ private final BitMatrix image;
+ private final WhiteRectangleDetector rectangleDetector;
- public Detector(MonochromeBitmapSource image) {
+ public Detector(BitMatrix image) {
this.image = image;
- rectangleDetector = new MonochromeRectangleDetector(image);
+ rectangleDetector = new WhiteRectangleDetector(image);
}
/**
* <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
+ * @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code
+ * @throws NotFoundException if no Data Matrix Code can be found
*/
- public DetectorResult detect() throws ReaderException {
+ public DetectorResult detect() throws NotFoundException {
ResultPoint[] cornerPoints = rectangleDetector.detect();
ResultPoint pointA = cornerPoints[0];
}
if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) {
- throw ReaderException.getInstance();
+ throw NotFoundException.getNotFoundInstance();
}
// 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);
+ ResultPoint.orderBestPatterns(corners);
// Now we know which is which:
ResultPoint bottomRight = corners[0];
// 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. The number of transitions could be higher than it should be
- // due to noise. So we try both and take the min.
-
- int dimension = Math.min(transitionsBetween(topLeft, topRight).getTransitions(),
+ // or bottom right should work here.
+ int dimension = Math.min(transitionsBetween(topLeft, topRight).getTransitions(),
transitionsBetween(bottomRight, topRight).getTransitions());
+ if ((dimension & 0x01) == 1) {
+ // it can't be odd, so, round... up?
+ dimension++;
+ }
dimension += 2;
- BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);
- return new DetectorResult(bits, new ResultPoint[] {pointA, pointB, pointC, pointD});
+ //correct top right point to match the white module
+ ResultPoint correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
+
+ //We redetermine the dimension using the corrected top right point
+ int dimension2 = Math.max(transitionsBetween(topLeft, correctedTopRight).getTransitions(),
+ transitionsBetween(bottomRight, correctedTopRight).getTransitions());
+ dimension2++;
+ if ((dimension2 & 0x01) == 1) {
+ dimension2++;
+ }
+
+ BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimension2);
+
+ return new DetectorResult(bits, new ResultPoint[]{topLeft, bottomLeft, bottomRight, correctedTopRight});
+ }
+
+ /**
+ * Calculates the position of the white top right module using the output of the rectangle detector
+ */
+ private ResultPoint correctTopRight(ResultPoint bottomLeft,
+ ResultPoint bottomRight,
+ ResultPoint topLeft,
+ ResultPoint topRight,
+ int dimension) {
+
+ float corr = distance(bottomLeft, bottomRight) / (float)dimension;
+ int norm = distance(topLeft, topRight);
+ float cos = (topRight.getX() - topLeft.getX()) / norm;
+ float sin = (topRight.getY() - topLeft.getY()) / norm;
+
+ ResultPoint c1 = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin);
+
+ corr = distance(bottomLeft, bottomRight) / (float)dimension;
+ norm = distance(bottomRight, topRight);
+ cos = (topRight.getX() - bottomRight.getX()) / norm;
+ sin = (topRight.getY() - bottomRight.getY()) / norm;
+
+ ResultPoint c2 = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin);
+
+ int l1 = Math.abs(transitionsBetween(topLeft, c1).getTransitions() - transitionsBetween(bottomRight, c1).getTransitions());
+ int l2 = Math.abs(transitionsBetween(topLeft, c2).getTransitions() - transitionsBetween(bottomRight, c2).getTransitions());
+
+ if (l1 <= l2){
+ return c1;
+ }
+
+ return c2;
+ }
+
+ // L2 distance
+ private static int distance(ResultPoint a, ResultPoint b) {
+ return (int) Math.round(Math.sqrt((a.getX() - b.getX())
+ * (a.getX() - b.getX()) + (a.getY() - b.getY())
+ * (a.getY() - b.getY())));
}
/**
table.put(key, value == null ? INTEGERS[1] : INTEGERS[value.intValue() + 1]);
}
- private static BitMatrix sampleGrid(MonochromeBitmapSource image,
+ private static BitMatrix sampleGrid(BitMatrix image,
ResultPoint topLeft,
ResultPoint bottomLeft,
ResultPoint bottomRight,
- int dimension) throws ReaderException {
-
- // 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();
+ ResultPoint topRight,
+ int dimension) throws NotFoundException {
- // 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.getInstance();
- 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());
+
+ return sampler.sampleGrid(image,
+ dimension,
+ 0.5f,
+ 0.5f,
+ dimension - 0.5f,
+ 0.5f,
+ dimension - 0.5f,
+ dimension - 0.5f,
+ 0.5f,
+ dimension - 0.5f,
+ topLeft.getX(),
+ topLeft.getY(),
+ topRight.getX(),
+ topRight.getY(),
+ bottomRight.getX(),
+ bottomRight.getY(),
+ bottomLeft.getX(),
+ bottomLeft.getY());
}
/**
int ystep = fromY < toY ? 1 : -1;
int xstep = fromX < toX ? 1 : -1;
int transitions = 0;
- boolean inBlack = image.isBlack(steep ? fromY : fromX, steep ? fromX : fromY);
+ boolean inBlack = image.get(steep ? fromY : fromX, steep ? fromX : fromY);
for (int x = fromX, y = fromY; x != toX; x += xstep) {
- boolean isBlack = image.isBlack(steep ? y : x, steep ? x : y);
+ boolean isBlack = image.get(steep ? y : x, steep ? x : y);
if (isBlack != inBlack) {
transitions++;
inBlack = isBlack;
}
error += dy;
if (error > 0) {
+ if (y == toY) {
+ break;
+ }
y += ystep;
error -= dx;
}