Improved datamatrix reader with new algorithm
[zxing.git] / core / src / com / google / zxing / datamatrix / detector / Detector.java
index 8649a65..206e518 100644 (file)
 
 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.GridSampler;
-import com.google.zxing.common.detector.MonochromeRectangleDetector;
+import com.google.zxing.common.detector.WhiteRectangleDetector;
 
 import java.util.Enumeration;
 import java.util.Hashtable;
@@ -38,28 +37,27 @@ import java.util.Vector;
  */
 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];
@@ -110,7 +108,7 @@ public final class Detector {
     }
 
     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
@@ -142,15 +140,69 @@ public final class Detector {
 
     // 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())));
   }
 
   /**
@@ -161,40 +213,33 @@ public final class Detector {
     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());
   }
 
   /**
@@ -222,15 +267,18 @@ public final class Detector {
     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;
       }