New C# port from Suraj Supekar
[zxing.git] / csharp / datamatrix / detector / Detector.cs
diff --git a/csharp/datamatrix/detector/Detector.cs b/csharp/datamatrix/detector/Detector.cs
new file mode 100755 (executable)
index 0000000..31278d0
--- /dev/null
@@ -0,0 +1,325 @@
+/*\r
+* Copyright 2008 ZXing authors\r
+*\r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+* you may not use this file except in compliance with the License.\r
+* You may obtain a copy of the License at\r
+*\r
+*      http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+using System;\r
+using ReaderException = com.google.zxing.ReaderException;\r
+using ResultPoint = com.google.zxing.ResultPoint;\r
+using BitMatrix = com.google.zxing.common.BitMatrix;\r
+using Collections = com.google.zxing.common.Collections;\r
+using Comparator = com.google.zxing.common.Comparator;\r
+using DetectorResult = com.google.zxing.common.DetectorResult;\r
+using GridSampler = com.google.zxing.common.GridSampler;\r
+using MonochromeRectangleDetector = com.google.zxing.common.detector.MonochromeRectangleDetector;\r
+namespace com.google.zxing.datamatrix.detector\r
+{\r
+       \r
+       /// <summary> <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code\r
+       /// is rotated or skewed, or partially obscured.</p>\r
+       /// \r
+       /// </summary>\r
+       /// <author>  Sean Owen\r
+       /// </author>\r
+       /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
+       /// </author>\r
+       public sealed class Detector\r
+       {\r
+               \r
+               //private static final int MAX_MODULES = 32;\r
+               \r
+               // Trick to avoid creating new Integer objects below -- a sort of crude copy of\r
+               // the Integer.valueOf(int) optimization added in Java 5, not in J2ME\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'INTEGERS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private static readonly System.Int32[] INTEGERS = new System.Int32[]{0, 1, 2, 3, 4};\r
+               // No, can't use valueOf()\r
+               \r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private BitMatrix image;\r
+               //UPGRADE_NOTE: Final was removed from the declaration of 'rectangleDetector '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+               private MonochromeRectangleDetector rectangleDetector;\r
+               \r
+               public Detector(BitMatrix image)\r
+               {\r
+                       this.image = image;\r
+                       rectangleDetector = new MonochromeRectangleDetector(image);\r
+               }\r
+               \r
+               /// <summary> <p>Detects a Data Matrix Code in an image.</p>\r
+               /// \r
+               /// </summary>\r
+               /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code\r
+               /// </returns>\r
+               /// <throws>  ReaderException if no Data Matrix Code can be found </throws>\r
+               public DetectorResult detect()\r
+               {\r
+                       \r
+                       ResultPoint[] cornerPoints = rectangleDetector.detect();\r
+                       ResultPoint pointA = cornerPoints[0];\r
+                       ResultPoint pointB = cornerPoints[1];\r
+                       ResultPoint pointC = cornerPoints[2];\r
+                       ResultPoint pointD = cornerPoints[3];\r
+                       \r
+                       // Point A and D are across the diagonal from one another,\r
+                       // as are B and C. Figure out which are the solid black lines\r
+                       // by counting transitions\r
+                       System.Collections.ArrayList transitions = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(4));\r
+                       transitions.Add(transitionsBetween(pointA, pointB));\r
+                       transitions.Add(transitionsBetween(pointA, pointC));\r
+                       transitions.Add(transitionsBetween(pointB, pointD));\r
+                       transitions.Add(transitionsBetween(pointC, pointD));\r
+                       Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator());\r
+                       \r
+                       // Sort by number of transitions. First two will be the two solid sides; last two\r
+                       // will be the two alternating black/white sides\r
+                       ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions) transitions[0];\r
+                       ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions) transitions[1];\r
+                       \r
+                       // Figure out which point is their intersection by tallying up the number of times we see the\r
+                       // endpoints in the four endpoints. One will show up twice.\r
+                       System.Collections.Hashtable pointCount = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());\r
+                       increment(pointCount, lSideOne.From);\r
+                       increment(pointCount, lSideOne.To);\r
+                       increment(pointCount, lSideTwo.From);\r
+                       increment(pointCount, lSideTwo.To);\r
+                       \r
+                       ResultPoint maybeTopLeft = null;\r
+                       ResultPoint bottomLeft = null;\r
+                       ResultPoint maybeBottomRight = null;\r
+                       System.Collections.IEnumerator points = pointCount.Keys.GetEnumerator();\r
+                       //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'"\r
+                       while (points.MoveNext())\r
+                       {\r
+                               //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'"\r
+                               ResultPoint point = (ResultPoint) points.Current;\r
+                               System.Int32 value_Renamed = (System.Int32) pointCount[point];\r
+                               if (value_Renamed == 2)\r
+                               {\r
+                                       bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides\r
+                               }\r
+                               else\r
+                               {\r
+                                       // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now\r
+                                       if (maybeTopLeft == null)\r
+                                       {\r
+                                               maybeTopLeft = point;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               maybeBottomRight = point;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null)\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       // Bottom left is correct but top left and bottom right might be switched\r
+                       ResultPoint[] corners = new ResultPoint[]{maybeTopLeft, bottomLeft, maybeBottomRight};\r
+                       // Use the dot product trick to sort them out\r
+                       ResultPoint.orderBestPatterns(corners);\r
+                       \r
+                       // Now we know which is which:\r
+                       ResultPoint bottomRight = corners[0];\r
+                       bottomLeft = corners[1];\r
+                       ResultPoint topLeft = corners[2];\r
+                       \r
+                       // Which point didn't we find in relation to the "L" sides? that's the top right corner\r
+                       ResultPoint topRight;\r
+                       if (!pointCount.ContainsKey(pointA))\r
+                       {\r
+                               topRight = pointA;\r
+                       }\r
+                       else if (!pointCount.ContainsKey(pointB))\r
+                       {\r
+                               topRight = pointB;\r
+                       }\r
+                       else if (!pointCount.ContainsKey(pointC))\r
+                       {\r
+                               topRight = pointC;\r
+                       }\r
+                       else\r
+                       {\r
+                               topRight = pointD;\r
+                       }\r
+                       \r
+                       // Next determine the dimension by tracing along the top or right side and counting black/white\r
+                       // transitions. Since we start inside a black module, we should see a number of transitions\r
+                       // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to\r
+                       // end on a black module:\r
+                       \r
+                       // The top right point is actually the corner of a module, which is one of the two black modules\r
+                       // adjacent to the white module at the top right. Tracing to that corner from either the top left\r
+                       // or bottom right should work here. The number of transitions could be higher than it should be\r
+                       // due to noise. So we try both and take the min.\r
+                       \r
+                       int dimension = System.Math.Min(transitionsBetween(topLeft, topRight).Transitions, transitionsBetween(bottomRight, topRight).Transitions);\r
+                       if ((dimension & 0x01) == 1)\r
+                       {\r
+                               // it can't be odd, so, round... up?\r
+                               dimension++;\r
+                       }\r
+                       dimension += 2;\r
+                       \r
+                       BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);\r
+                       return new DetectorResult(bits, new ResultPoint[]{pointA, pointB, pointC, pointD});\r
+               }\r
+               \r
+               /// <summary> Increments the Integer associated with a key by one.</summary>\r
+               private static void  increment(System.Collections.Hashtable table, ResultPoint key)\r
+               {\r
+            //System.Int32 value_Renamed = (System.Int32) table[key];\r
+            ////UPGRADE_TODO: The 'System.Int32' structure does not have an equivalent to NULL. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1291'"\r
+            //table[key] = value_Renamed == null?INTEGERS[1]:INTEGERS[value_Renamed + 1];\r
+            // Redivivus.in Java to c# Porting update\r
+            // 30/01/2010 \r
+            // Added\r
+            // START\r
+            System.Int32 value_Renamed = 0;\r
+            try\r
+            {\r
+                if (table.Count > 0)\r
+                    value_Renamed = (System.Int32)table[key];\r
+            }\r
+            catch\r
+            {\r
+                value_Renamed = 0;\r
+            }\r
+            table[key] = value_Renamed == 0 ? INTEGERS[1] : INTEGERS[value_Renamed + 1];\r
+            //END\r
+               }\r
+               \r
+               private static BitMatrix sampleGrid(BitMatrix image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension)\r
+               {\r
+                       \r
+                       // We make up the top right point for now, based on the others.\r
+                       // TODO: we actually found a fourth corner above and figured out which of two modules\r
+                       // it was the corner of. We could use that here and adjust for perspective distortion.\r
+                       float topRightX = (bottomRight.X - bottomLeft.X) + topLeft.X;\r
+                       float topRightY = (bottomRight.Y - bottomLeft.Y) + topLeft.Y;\r
+                       \r
+                       // Note that unlike in the QR Code sampler, we didn't find the center of modules, but the\r
+                       // very corners. So there is no 0.5f here; 0.0f is right.\r
+                       GridSampler sampler = GridSampler.Instance;\r
+                       return sampler.sampleGrid(image, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.X, topLeft.Y, topRightX, topRightY, bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y);\r
+               }\r
+               \r
+               /// <summary> Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.</summary>\r
+               private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to)\r
+               {\r
+                       // See QR Code Detector, sizeOfBlackWhiteBlackRun()\r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       int fromX = (int) from.X;\r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       int fromY = (int) from.Y;\r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       int toX = (int) to.X;\r
+                       //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"\r
+                       int toY = (int) to.Y;\r
+                       bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX);\r
+                       if (steep)\r
+                       {\r
+                               int temp = fromX;\r
+                               fromX = fromY;\r
+                               fromY = temp;\r
+                               temp = toX;\r
+                               toX = toY;\r
+                               toY = temp;\r
+                       }\r
+                       \r
+                       int dx = System.Math.Abs(toX - fromX);\r
+                       int dy = System.Math.Abs(toY - fromY);\r
+                       int error = - dx >> 1;\r
+                       int ystep = fromY < toY?1:- 1;\r
+                       int xstep = fromX < toX?1:- 1;\r
+                       int transitions = 0;\r
+                       bool inBlack = image.get_Renamed(steep?fromY:fromX, steep?fromX:fromY);\r
+                       for (int x = fromX, y = fromY; x != toX; x += xstep)\r
+                       {\r
+                               bool isBlack = image.get_Renamed(steep?y:x, steep?x:y);\r
+                               if (isBlack != inBlack)\r
+                               {\r
+                                       transitions++;\r
+                                       inBlack = isBlack;\r
+                               }\r
+                               error += dy;\r
+                               if (error > 0)\r
+                               {\r
+                                       if (y == toY)\r
+                                       {\r
+                                               break;\r
+                                       }\r
+                                       y += ystep;\r
+                                       error -= dx;\r
+                               }\r
+                       }\r
+                       return new ResultPointsAndTransitions(from, to, transitions);\r
+               }\r
+               \r
+               /// <summary> Simply encapsulates two points and a number of transitions between them.</summary>\r
+               private class ResultPointsAndTransitions\r
+               {\r
+                       public ResultPoint From\r
+                       {\r
+                               get\r
+                               {\r
+                                       return from;\r
+                               }\r
+                               \r
+                       }\r
+                       public ResultPoint To\r
+                       {\r
+                               get\r
+                               {\r
+                                       return to;\r
+                               }\r
+                               \r
+                       }\r
+                       public int Transitions\r
+                       {\r
+                               get\r
+                               {\r
+                                       return transitions;\r
+                               }\r
+                               \r
+                       }\r
+                       //UPGRADE_NOTE: Final was removed from the declaration of 'from '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+                       private ResultPoint from;\r
+                       //UPGRADE_NOTE: Final was removed from the declaration of 'to '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+                       private ResultPoint to;\r
+                       //UPGRADE_NOTE: Final was removed from the declaration of 'transitions '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
+                       private int transitions;\r
+                       internal ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions)\r
+                       {\r
+                               this.from = from;\r
+                               this.to = to;\r
+                               this.transitions = transitions;\r
+                       }\r
+                       public override System.String ToString()\r
+                       {\r
+                               return from + "/" + to + '/' + transitions;\r
+                       }\r
+               }\r
+               \r
+               /// <summary> Orders ResultPointsAndTransitions by number of transitions, ascending.</summary>\r
+               private class ResultPointsAndTransitionsComparator : Comparator\r
+               {\r
+                       public int compare(System.Object o1, System.Object o2)\r
+                       {\r
+                               return ((ResultPointsAndTransitions) o1).Transitions - ((ResultPointsAndTransitions) o2).Transitions;\r
+                       }\r
+               }\r
+       }\r
+}
\ No newline at end of file