Issue 563: Support non-rectangular Data Matrix
authordav.olivier@gmail.com <dav.olivier@gmail.com@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Tue, 5 Oct 2010 20:19:53 +0000 (20:19 +0000)
committerdav.olivier@gmail.com <dav.olivier@gmail.com@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Tue, 5 Oct 2010 20:19:53 +0000 (20:19 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@1616 59b500cc-1b3d-0410-9834-0bbf25fbcc57

19 files changed:
core/src/com/google/zxing/common/DefaultGridSampler.java
core/src/com/google/zxing/common/GridSampler.java
core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
core/src/com/google/zxing/datamatrix/decoder/Version.java
core/src/com/google/zxing/datamatrix/detector/Detector.java
core/test/data/blackbox/datamatrix-1/abcd-18x8.png [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-18x8.txt [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-26x12.png [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-26x12.txt [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-32x8.png [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-32x8.txt [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-36x12.png [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-36x12.txt [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-36x16.png [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-36x16.txt [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-48x16.png [new file with mode: 0644]
core/test/data/blackbox/datamatrix-1/abcd-48x16.txt [new file with mode: 0644]
core/test/src/com/google/zxing/datamatrix/DataMatrixBlackBox1TestCase.java
core/test/src/com/google/zxing/datamatrix/DataMatrixBlackBox2TestCase.java

index 9a2a683..01722b3 100644 (file)
@@ -38,44 +38,63 @@ public final class DefaultGridSampler extends GridSampler {
         p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
         p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
 
-    return sampleGrid(image, dimension, transform);
+    return sampleGrid(image, dimension, dimension, transform);
   }
-
+  
   public BitMatrix sampleGrid(BitMatrix image,
-                              int dimension,
-                              PerspectiveTransform transform) throws NotFoundException {
-    BitMatrix bits = new BitMatrix(dimension);
-    float[] points = new float[dimension << 1];
-    for (int y = 0; y < dimension; y++) {
-      int max = points.length;
-      float iValue = (float) y + 0.5f;
-      for (int x = 0; x < max; x += 2) {
-        points[x] = (float) (x >> 1) + 0.5f;
-        points[x + 1] = iValue;
-      }
-      transform.transformPoints(points);
-      // Quick check to see if points transformed to something inside the image;
-      // sufficient to check the endpoints
-      checkAndNudgePoints(image, points);
-      try {
-        for (int x = 0; x < max; x += 2) {
-          if (image.get((int) points[x], (int) points[x + 1])) {
-            // Black(-ish) pixel
-            bits.set(x >> 1, y);
-          }
-        }
-      } catch (ArrayIndexOutOfBoundsException aioobe) {
-        // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
-        // transform gets "twisted" such that it maps a straight line of points to a set of points
-        // whose endpoints are in bounds, but others are not. There is probably some mathematical
-        // way to detect this about the transformation that I don't know yet.
-        // This results in an ugly runtime exception despite our clever checks above -- can't have
-        // that. We could check each point's coordinates but that feels duplicative. We settle for
-        // catching and wrapping ArrayIndexOutOfBoundsException.
-        throw NotFoundException.getNotFoundInstance();
-      }
-    }
-    return bits;
-  }
+          int dimensionX,
+          int dimensionY,
+          float p1ToX, float p1ToY,
+          float p2ToX, float p2ToY,
+          float p3ToX, float p3ToY,
+          float p4ToX, float p4ToY,
+          float p1FromX, float p1FromY,
+          float p2FromX, float p2FromY,
+          float p3FromX, float p3FromY,
+          float p4FromX, float p4FromY) throws NotFoundException {
+
+PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(
+p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
+p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
 
+return sampleGrid(image, dimensionX, dimensionY, transform);
+}
+
+  public BitMatrix sampleGrid(BitMatrix image,
+          int dimensionX, int dimensionY,
+          PerspectiveTransform transform) throws NotFoundException {
+BitMatrix bits = new BitMatrix(dimensionX, dimensionY);
+float[] points = new float[dimensionX << 1];
+for (int y = 0; y < dimensionY; y++) {
+int max = points.length;
+float iValue = (float) y + 0.5f;
+for (int x = 0; x < max; x += 2) {
+points[x] = (float) (x >> 1) + 0.5f;
+points[x + 1] = iValue;
+}
+transform.transformPoints(points);
+// Quick check to see if points transformed to something inside the image;
+// sufficient to check the endpoints
+checkAndNudgePoints(image, points);
+try {
+for (int x = 0; x < max; x += 2) {
+if (image.get((int) points[x], (int) points[x + 1])) {
+// Black(-ish) pixel
+bits.set(x >> 1, y);
+}
+}
+} catch (ArrayIndexOutOfBoundsException aioobe) {
+// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
+// transform gets "twisted" such that it maps a straight line of points to a set of points
+// whose endpoints are in bounds, but others are not. There is probably some mathematical
+// way to detect this about the transformation that I don't know yet.
+// This results in an ugly runtime exception despite our clever checks above -- can't have
+// that. We could check each point's coordinates but that feels duplicative. We settle for
+// catching and wrapping ArrayIndexOutOfBoundsException.
+throw NotFoundException.getNotFoundInstance();
+}
+}
+return bits;
+}
+  
 }
index 3b55eb6..1a79b93 100644 (file)
@@ -92,6 +92,28 @@ public abstract class GridSampler {
                                        float p3FromX, float p3FromY,
                                        float p4FromX, float p4FromY) throws NotFoundException;
 
+  /**
+   * Samples an image for a rectangular matrix of bits of the given dimension.
+   * @param image image to sample
+   * @param dimensionX width of {@link BitMatrix} to sample from image
+   * @param dimensionY height of {@link BitMatrix} to sample from image
+   * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
+   *   defined by the "from" parameters
+   * @throws NotFoundException if image can't be sampled, for example, if the transformation defined
+   *   by the given points is invalid or results in sampling outside the image boundaries
+   */
+  public abstract BitMatrix sampleGrid(BitMatrix image,
+          int dimensionX,
+          int dimensionY,
+          float p1ToX, float p1ToY,
+          float p2ToX, float p2ToY,
+          float p3ToX, float p3ToY,
+          float p4ToX, float p4ToY,
+          float p1FromX, float p1FromY,
+          float p2FromX, float p2FromY,
+          float p3FromX, float p3FromY,
+          float p4FromX, float p4FromY) throws NotFoundException;
+  
   public BitMatrix sampleGrid(BitMatrix image,
                               int dimension,
                               PerspectiveTransform transform) throws NotFoundException {
index 96032b4..3fe11a2 100644 (file)
@@ -30,18 +30,17 @@ final class BitMatrixParser {
 \r
   /**\r
    * @param bitMatrix {@link BitMatrix} to parse\r
-   * @throws FormatException if dimension is < 10 or > 144 or not 0 mod 2\r
+   * @throws FormatException if dimension is < 8 or > 144 or not 0 mod 2\r
    */\r
   BitMatrixParser(BitMatrix bitMatrix) throws FormatException {\r
     int dimension = bitMatrix.getHeight();\r
-    if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {\r
+    if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) {\r
       throw FormatException.getFormatInstance();\r
     }\r
     \r
     version = readVersion(bitMatrix);\r
     this.mappingBitMatrix = extractDataRegion(bitMatrix);\r
-    // TODO(bbrown): Make this work for rectangular symbols\r
-    this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getHeight());\r
+    this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getWidth(), this.mappingBitMatrix.getHeight());\r
   }\r
 \r
   /**\r
@@ -61,9 +60,8 @@ final class BitMatrixParser {
       return version;\r
     }\r
 \r
-    // TODO(bbrown): make this work for rectangular dimensions as well.\r
     int numRows = bitMatrix.getHeight();\r
-    int numColumns = numRows;\r
+    int numColumns = bitMatrix.getWidth();\r
     \r
     return Version.getVersionForDimensions(numRows, numColumns);\r
   }\r
@@ -83,9 +81,9 @@ final class BitMatrixParser {
     \r
     int row = 4;\r
     int column = 0;\r
-    // TODO(bbrown): Data Matrix can be rectangular, assuming square for now\r
+\r
     int numRows = mappingBitMatrix.getHeight();\r
-    int numColumns = numRows;\r
+    int numColumns = mappingBitMatrix.getWidth();\r
     \r
     boolean corner1Read = false;\r
     boolean corner2Read = false;\r
@@ -407,7 +405,6 @@ final class BitMatrixParser {
     int symbolSizeRows = version.getSymbolSizeRows();\r
     int symbolSizeColumns = version.getSymbolSizeColumns();\r
     \r
-    // TODO(bbrown): Make this work with rectangular codes\r
     if (bitMatrix.getHeight() != symbolSizeRows) {\r
       throw new IllegalArgumentException("Dimension of bitMarix must match the version size");\r
     }\r
@@ -419,10 +416,10 @@ final class BitMatrixParser {
     int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;\r
     \r
     int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;\r
-    //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;\r
+    int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;\r
     \r
     // TODO(bbrown): Make this work with rectangular codes\r
-    BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow);\r
+    BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow);\r
     for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {\r
       int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;\r
       for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {\r
index d2b712b..5f97854 100644 (file)
@@ -232,7 +232,7 @@ public final class Version {
             new ECBlocks(14, new ECB(1, 16))),\r
         new Version(28, 12, 36, 10, 16,\r
             new ECBlocks(18, new ECB(1, 22))),\r
-        new Version(29, 16, 36, 10, 16,\r
+        new Version(29, 16, 36, 14, 16,\r
             new ECBlocks(24, new ECB(1, 32))),\r
         new Version(30, 16, 48, 14, 22,\r
             new ECBlocks(28, new ECB(1, 49)))\r
index 59ffbad..6c43ef7 100644 (file)
@@ -141,35 +141,118 @@ 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.
-    int dimension = Math.min(transitionsBetween(topLeft, topRight).getTransitions(),
-                             transitionsBetween(bottomRight, topRight).getTransitions());
-    if ((dimension & 0x01) == 1) {
+    
+    
+    int dimensionTop = transitionsBetween(topLeft, topRight).getTransitions();
+    int dimensionRight = transitionsBetween(bottomRight, topRight).getTransitions();
+    
+    if ((dimensionTop & 0x01) == 1) {
       // it can't be odd, so, round... up?
-      dimension++;
+      dimensionTop++;
     }
-    dimension += 2;
+    dimensionTop += 2;
+    
+    if ((dimensionRight & 0x01) == 1) {
+        // it can't be odd, so, round... up?
+        dimensionRight++;
+      }
+      dimensionRight += 2;
+
+    BitMatrix bits = null;
+    ResultPoint correctedTopRight = null;
+      
+    if (dimensionTop >= dimensionRight * 2 || dimensionRight >= dimensionTop * 2){
+       //The matrix is rectangular
+       
+        correctedTopRight = correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight, dimensionTop, dimensionRight);
+        if (correctedTopRight == null){
+               correctedTopRight = topRight;
+        }
+        
+        dimensionTop = transitionsBetween(topLeft, correctedTopRight).getTransitions();
+        dimensionRight = transitionsBetween(bottomRight, correctedTopRight).getTransitions();
+        
+        if ((dimensionTop & 0x01) == 1) {
+          // it can't be odd, so, round... up?
+          dimensionTop++;
+        }
+        
+        if ((dimensionRight & 0x01) == 1) {
+            // it can't be odd, so, round... up?
+            dimensionRight++;
+          }
+        
+          bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimensionTop, dimensionRight);
+          
+    } else {
+       //The matrix is square
+        
+       int dimension = Math.min(dimensionRight, dimensionTop);
+        //correct top right point to match the white module
+        correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
+        if (correctedTopRight == null){
+               correctedTopRight = topRight;
+        }
 
-    //correct top right point to match the white module
-    ResultPoint correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
-    if (correctedTopRight == null){
-       correctedTopRight = topRight;
-    }
+        //We redetermine the dimension using the corrected top right point
+        int dimensionCorrected = Math.max(transitionsBetween(topLeft, correctedTopRight).getTransitions(),
+                                  transitionsBetween(bottomRight, correctedTopRight).getTransitions());
+        dimensionCorrected++;
+        if ((dimensionCorrected & 0x01) == 1) {
+          dimensionCorrected++;
+        }
 
-    //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++;
+        bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimensionCorrected, dimensionCorrected);
     }
 
-    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
+   * Calculates the position of the white top right module using the output of the rectangle detector for a rectangular matrix
+   */
+  private ResultPoint correctTopRightRectangular(ResultPoint bottomLeft,
+               ResultPoint bottomRight, ResultPoint topLeft, ResultPoint topRight,
+               int dimensionTop, int dimensionRight) {
+         
+               float corr = distance(bottomLeft, bottomRight) / (float)dimensionTop;
+               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, topLeft) / (float)dimensionRight;
+               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);
+
+               if (!isValid(c1)){
+                       if (isValid(c2)){
+                               return c2;
+                       }
+                       return null;
+               } else if (!isValid(c2)){
+                       return c1;
+               }
+               
+               int l1 = Math.abs(dimensionTop - transitionsBetween(topLeft, c1).getTransitions()) + 
+                                       Math.abs(dimensionRight - transitionsBetween(bottomRight, c1).getTransitions());
+               int l2 = Math.abs(dimensionTop - transitionsBetween(topLeft, c2).getTransitions()) + 
+               Math.abs(dimensionRight - transitionsBetween(bottomRight, c2).getTransitions());
+               
+               if (l1 <= l2){
+                       return c1;
+               }
+               
+               return c2;
+}
+
+/**
+   * Calculates the position of the white top right module using the output of the rectangle detector for a square matrix
    */
   private ResultPoint correctTopRight(ResultPoint bottomLeft,
                                       ResultPoint bottomRight,
@@ -242,20 +325,22 @@ public final class Detector {
                                       ResultPoint bottomLeft,
                                       ResultPoint bottomRight,
                                       ResultPoint topRight,
-                                      int dimension) throws NotFoundException {
+                                      int dimensionX,
+                                      int dimensionY) throws NotFoundException {
 
     GridSampler sampler = GridSampler.getInstance();
 
     return sampler.sampleGrid(image,
-                              dimension,
+                              dimensionX,
+                              dimensionY,
                               0.5f,
                               0.5f,
-                              dimension - 0.5f,
+                              dimensionX - 0.5f,
                               0.5f,
-                              dimension - 0.5f,
-                              dimension - 0.5f,
+                              dimensionX - 0.5f,
+                              dimensionY - 0.5f,
                               0.5f,
-                              dimension - 0.5f,
+                              dimensionY - 0.5f,
                               topLeft.getX(),
                               topLeft.getY(),
                               topRight.getX(),
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-18x8.png b/core/test/data/blackbox/datamatrix-1/abcd-18x8.png
new file mode 100644 (file)
index 0000000..0c0b73a
Binary files /dev/null and b/core/test/data/blackbox/datamatrix-1/abcd-18x8.png differ
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-18x8.txt b/core/test/data/blackbox/datamatrix-1/abcd-18x8.txt
new file mode 100644 (file)
index 0000000..6a81654
--- /dev/null
@@ -0,0 +1 @@
+abcde
\ No newline at end of file
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-26x12.png b/core/test/data/blackbox/datamatrix-1/abcd-26x12.png
new file mode 100644 (file)
index 0000000..b305320
Binary files /dev/null and b/core/test/data/blackbox/datamatrix-1/abcd-26x12.png differ
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-26x12.txt b/core/test/data/blackbox/datamatrix-1/abcd-26x12.txt
new file mode 100644 (file)
index 0000000..f9fb130
--- /dev/null
@@ -0,0 +1 @@
+abcdefghijklm
\ No newline at end of file
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-32x8.png b/core/test/data/blackbox/datamatrix-1/abcd-32x8.png
new file mode 100644 (file)
index 0000000..51ebd57
Binary files /dev/null and b/core/test/data/blackbox/datamatrix-1/abcd-32x8.png differ
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-32x8.txt b/core/test/data/blackbox/datamatrix-1/abcd-32x8.txt
new file mode 100644 (file)
index 0000000..d96dc95
--- /dev/null
@@ -0,0 +1 @@
+abcdef
\ No newline at end of file
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-36x12.png b/core/test/data/blackbox/datamatrix-1/abcd-36x12.png
new file mode 100644 (file)
index 0000000..db428ba
Binary files /dev/null and b/core/test/data/blackbox/datamatrix-1/abcd-36x12.png differ
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-36x12.txt b/core/test/data/blackbox/datamatrix-1/abcd-36x12.txt
new file mode 100644 (file)
index 0000000..3005ca6
--- /dev/null
@@ -0,0 +1 @@
+abcdefghijklmnopq
\ No newline at end of file
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-36x16.png b/core/test/data/blackbox/datamatrix-1/abcd-36x16.png
new file mode 100644 (file)
index 0000000..90ba269
Binary files /dev/null and b/core/test/data/blackbox/datamatrix-1/abcd-36x16.png differ
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-36x16.txt b/core/test/data/blackbox/datamatrix-1/abcd-36x16.txt
new file mode 100644 (file)
index 0000000..e85d5b4
--- /dev/null
@@ -0,0 +1 @@
+abcdefghijklmnopqrstuvwxyz
\ No newline at end of file
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-48x16.png b/core/test/data/blackbox/datamatrix-1/abcd-48x16.png
new file mode 100644 (file)
index 0000000..e2e4a44
Binary files /dev/null and b/core/test/data/blackbox/datamatrix-1/abcd-48x16.png differ
diff --git a/core/test/data/blackbox/datamatrix-1/abcd-48x16.txt b/core/test/data/blackbox/datamatrix-1/abcd-48x16.txt
new file mode 100644 (file)
index 0000000..7997473
--- /dev/null
@@ -0,0 +1 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW
\ No newline at end of file
index 5dc4c85..52a9461 100644 (file)
@@ -27,10 +27,10 @@ public final class DataMatrixBlackBox1TestCase extends AbstractBlackBoxTestCase
   public DataMatrixBlackBox1TestCase() {
     // TODO use MultiFormatReader here once Data Matrix decoder is done
     super("test/data/blackbox/datamatrix-1", new DataMatrixReader(), BarcodeFormat.DATA_MATRIX);
-    addTest(7, 7, 0.0f);
-    addTest(7, 7, 90.0f);
-    addTest(7, 7, 180.0f);
-    addTest(7, 7, 270.0f);
+    addTest(13, 13, 0.0f);
+    addTest(13, 13, 90.0f);
+    addTest(13, 13, 180.0f);
+    addTest(13, 13, 270.0f);
   }
 
 }
\ No newline at end of file
index a08f0be..ff49603 100644 (file)
@@ -30,7 +30,7 @@ public final class DataMatrixBlackBox2TestCase extends AbstractBlackBoxTestCase
     addTest(10, 10, 0.0f);
     addTest(13, 13, 90.0f);
     addTest(16, 16, 180.0f);
-    addTest(12, 12, 270.0f);
+    addTest(13, 13, 270.0f);
   }
 
 }