New C# port from Suraj Supekar
[zxing.git] / csharp / datamatrix / detector / Detector.cs
1 /*\r
2 * Copyright 2008 ZXing authors\r
3 *\r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5 * you may not use this file except in compliance with the License.\r
6 * You may obtain a copy of the License at\r
7 *\r
8 *      http://www.apache.org/licenses/LICENSE-2.0\r
9 *\r
10 * Unless required by applicable law or agreed to in writing, software\r
11 * distributed under the License is distributed on an "AS IS" BASIS,\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13 * See the License for the specific language governing permissions and\r
14 * limitations under the License.\r
15 */\r
16 using System;\r
17 using ReaderException = com.google.zxing.ReaderException;\r
18 using ResultPoint = com.google.zxing.ResultPoint;\r
19 using BitMatrix = com.google.zxing.common.BitMatrix;\r
20 using Collections = com.google.zxing.common.Collections;\r
21 using Comparator = com.google.zxing.common.Comparator;\r
22 using DetectorResult = com.google.zxing.common.DetectorResult;\r
23 using GridSampler = com.google.zxing.common.GridSampler;\r
24 using MonochromeRectangleDetector = com.google.zxing.common.detector.MonochromeRectangleDetector;\r
25 namespace com.google.zxing.datamatrix.detector\r
26 {\r
27         \r
28         /// <summary> <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code\r
29         /// is rotated or skewed, or partially obscured.</p>\r
30         /// \r
31         /// </summary>\r
32         /// <author>  Sean Owen\r
33         /// </author>\r
34         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
35         /// </author>\r
36         public sealed class Detector\r
37         {\r
38                 \r
39                 //private static final int MAX_MODULES = 32;\r
40                 \r
41                 // Trick to avoid creating new Integer objects below -- a sort of crude copy of\r
42                 // the Integer.valueOf(int) optimization added in Java 5, not in J2ME\r
43                 //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
44                 private static readonly System.Int32[] INTEGERS = new System.Int32[]{0, 1, 2, 3, 4};\r
45                 // No, can't use valueOf()\r
46                 \r
47                 //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
48                 private BitMatrix image;\r
49                 //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
50                 private MonochromeRectangleDetector rectangleDetector;\r
51                 \r
52                 public Detector(BitMatrix image)\r
53                 {\r
54                         this.image = image;\r
55                         rectangleDetector = new MonochromeRectangleDetector(image);\r
56                 }\r
57                 \r
58                 /// <summary> <p>Detects a Data Matrix Code in an image.</p>\r
59                 /// \r
60                 /// </summary>\r
61                 /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code\r
62                 /// </returns>\r
63                 /// <throws>  ReaderException if no Data Matrix Code can be found </throws>\r
64                 public DetectorResult detect()\r
65                 {\r
66                         \r
67                         ResultPoint[] cornerPoints = rectangleDetector.detect();\r
68                         ResultPoint pointA = cornerPoints[0];\r
69                         ResultPoint pointB = cornerPoints[1];\r
70                         ResultPoint pointC = cornerPoints[2];\r
71                         ResultPoint pointD = cornerPoints[3];\r
72                         \r
73                         // Point A and D are across the diagonal from one another,\r
74                         // as are B and C. Figure out which are the solid black lines\r
75                         // by counting transitions\r
76                         System.Collections.ArrayList transitions = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(4));\r
77                         transitions.Add(transitionsBetween(pointA, pointB));\r
78                         transitions.Add(transitionsBetween(pointA, pointC));\r
79                         transitions.Add(transitionsBetween(pointB, pointD));\r
80                         transitions.Add(transitionsBetween(pointC, pointD));\r
81                         Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator());\r
82                         \r
83                         // Sort by number of transitions. First two will be the two solid sides; last two\r
84                         // will be the two alternating black/white sides\r
85                         ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions) transitions[0];\r
86                         ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions) transitions[1];\r
87                         \r
88                         // Figure out which point is their intersection by tallying up the number of times we see the\r
89                         // endpoints in the four endpoints. One will show up twice.\r
90                         System.Collections.Hashtable pointCount = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());\r
91                         increment(pointCount, lSideOne.From);\r
92                         increment(pointCount, lSideOne.To);\r
93                         increment(pointCount, lSideTwo.From);\r
94                         increment(pointCount, lSideTwo.To);\r
95                         \r
96                         ResultPoint maybeTopLeft = null;\r
97                         ResultPoint bottomLeft = null;\r
98                         ResultPoint maybeBottomRight = null;\r
99                         System.Collections.IEnumerator points = pointCount.Keys.GetEnumerator();\r
100                         //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
101                         while (points.MoveNext())\r
102                         {\r
103                                 //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
104                                 ResultPoint point = (ResultPoint) points.Current;\r
105                                 System.Int32 value_Renamed = (System.Int32) pointCount[point];\r
106                                 if (value_Renamed == 2)\r
107                                 {\r
108                                         bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides\r
109                                 }\r
110                                 else\r
111                                 {\r
112                                         // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now\r
113                                         if (maybeTopLeft == null)\r
114                                         {\r
115                                                 maybeTopLeft = point;\r
116                                         }\r
117                                         else\r
118                                         {\r
119                                                 maybeBottomRight = point;\r
120                                         }\r
121                                 }\r
122                         }\r
123                         \r
124                         if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null)\r
125                         {\r
126                                 throw ReaderException.Instance;\r
127                         }\r
128                         \r
129                         // Bottom left is correct but top left and bottom right might be switched\r
130                         ResultPoint[] corners = new ResultPoint[]{maybeTopLeft, bottomLeft, maybeBottomRight};\r
131                         // Use the dot product trick to sort them out\r
132                         ResultPoint.orderBestPatterns(corners);\r
133                         \r
134                         // Now we know which is which:\r
135                         ResultPoint bottomRight = corners[0];\r
136                         bottomLeft = corners[1];\r
137                         ResultPoint topLeft = corners[2];\r
138                         \r
139                         // Which point didn't we find in relation to the "L" sides? that's the top right corner\r
140                         ResultPoint topRight;\r
141                         if (!pointCount.ContainsKey(pointA))\r
142                         {\r
143                                 topRight = pointA;\r
144                         }\r
145                         else if (!pointCount.ContainsKey(pointB))\r
146                         {\r
147                                 topRight = pointB;\r
148                         }\r
149                         else if (!pointCount.ContainsKey(pointC))\r
150                         {\r
151                                 topRight = pointC;\r
152                         }\r
153                         else\r
154                         {\r
155                                 topRight = pointD;\r
156                         }\r
157                         \r
158                         // Next determine the dimension by tracing along the top or right side and counting black/white\r
159                         // transitions. Since we start inside a black module, we should see a number of transitions\r
160                         // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to\r
161                         // end on a black module:\r
162                         \r
163                         // The top right point is actually the corner of a module, which is one of the two black modules\r
164                         // adjacent to the white module at the top right. Tracing to that corner from either the top left\r
165                         // or bottom right should work here. The number of transitions could be higher than it should be\r
166                         // due to noise. So we try both and take the min.\r
167                         \r
168                         int dimension = System.Math.Min(transitionsBetween(topLeft, topRight).Transitions, transitionsBetween(bottomRight, topRight).Transitions);\r
169                         if ((dimension & 0x01) == 1)\r
170                         {\r
171                                 // it can't be odd, so, round... up?\r
172                                 dimension++;\r
173                         }\r
174                         dimension += 2;\r
175                         \r
176                         BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);\r
177                         return new DetectorResult(bits, new ResultPoint[]{pointA, pointB, pointC, pointD});\r
178                 }\r
179                 \r
180                 /// <summary> Increments the Integer associated with a key by one.</summary>\r
181                 private static void  increment(System.Collections.Hashtable table, ResultPoint key)\r
182                 {\r
183             //System.Int32 value_Renamed = (System.Int32) table[key];\r
184             ////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
185             //table[key] = value_Renamed == null?INTEGERS[1]:INTEGERS[value_Renamed + 1];\r
186             // Redivivus.in Java to c# Porting update\r
187             // 30/01/2010 \r
188             // Added\r
189             // START\r
190             System.Int32 value_Renamed = 0;\r
191             try\r
192             {\r
193                 if (table.Count > 0)\r
194                     value_Renamed = (System.Int32)table[key];\r
195             }\r
196             catch\r
197             {\r
198                 value_Renamed = 0;\r
199             }\r
200             table[key] = value_Renamed == 0 ? INTEGERS[1] : INTEGERS[value_Renamed + 1];\r
201             //END\r
202                 }\r
203                 \r
204                 private static BitMatrix sampleGrid(BitMatrix image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension)\r
205                 {\r
206                         \r
207                         // We make up the top right point for now, based on the others.\r
208                         // TODO: we actually found a fourth corner above and figured out which of two modules\r
209                         // it was the corner of. We could use that here and adjust for perspective distortion.\r
210                         float topRightX = (bottomRight.X - bottomLeft.X) + topLeft.X;\r
211                         float topRightY = (bottomRight.Y - bottomLeft.Y) + topLeft.Y;\r
212                         \r
213                         // Note that unlike in the QR Code sampler, we didn't find the center of modules, but the\r
214                         // very corners. So there is no 0.5f here; 0.0f is right.\r
215                         GridSampler sampler = GridSampler.Instance;\r
216                         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
217                 }\r
218                 \r
219                 /// <summary> Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.</summary>\r
220                 private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to)\r
221                 {\r
222                         // See QR Code Detector, sizeOfBlackWhiteBlackRun()\r
223                         //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
224                         int fromX = (int) from.X;\r
225                         //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
226                         int fromY = (int) from.Y;\r
227                         //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
228                         int toX = (int) to.X;\r
229                         //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
230                         int toY = (int) to.Y;\r
231                         bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX);\r
232                         if (steep)\r
233                         {\r
234                                 int temp = fromX;\r
235                                 fromX = fromY;\r
236                                 fromY = temp;\r
237                                 temp = toX;\r
238                                 toX = toY;\r
239                                 toY = temp;\r
240                         }\r
241                         \r
242                         int dx = System.Math.Abs(toX - fromX);\r
243                         int dy = System.Math.Abs(toY - fromY);\r
244                         int error = - dx >> 1;\r
245                         int ystep = fromY < toY?1:- 1;\r
246                         int xstep = fromX < toX?1:- 1;\r
247                         int transitions = 0;\r
248                         bool inBlack = image.get_Renamed(steep?fromY:fromX, steep?fromX:fromY);\r
249                         for (int x = fromX, y = fromY; x != toX; x += xstep)\r
250                         {\r
251                                 bool isBlack = image.get_Renamed(steep?y:x, steep?x:y);\r
252                                 if (isBlack != inBlack)\r
253                                 {\r
254                                         transitions++;\r
255                                         inBlack = isBlack;\r
256                                 }\r
257                                 error += dy;\r
258                                 if (error > 0)\r
259                                 {\r
260                                         if (y == toY)\r
261                                         {\r
262                                                 break;\r
263                                         }\r
264                                         y += ystep;\r
265                                         error -= dx;\r
266                                 }\r
267                         }\r
268                         return new ResultPointsAndTransitions(from, to, transitions);\r
269                 }\r
270                 \r
271                 /// <summary> Simply encapsulates two points and a number of transitions between them.</summary>\r
272                 private class ResultPointsAndTransitions\r
273                 {\r
274                         public ResultPoint From\r
275                         {\r
276                                 get\r
277                                 {\r
278                                         return from;\r
279                                 }\r
280                                 \r
281                         }\r
282                         public ResultPoint To\r
283                         {\r
284                                 get\r
285                                 {\r
286                                         return to;\r
287                                 }\r
288                                 \r
289                         }\r
290                         public int Transitions\r
291                         {\r
292                                 get\r
293                                 {\r
294                                         return transitions;\r
295                                 }\r
296                                 \r
297                         }\r
298                         //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
299                         private ResultPoint from;\r
300                         //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
301                         private ResultPoint to;\r
302                         //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
303                         private int transitions;\r
304                         internal ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions)\r
305                         {\r
306                                 this.from = from;\r
307                                 this.to = to;\r
308                                 this.transitions = transitions;\r
309                         }\r
310                         public override System.String ToString()\r
311                         {\r
312                                 return from + "/" + to + '/' + transitions;\r
313                         }\r
314                 }\r
315                 \r
316                 /// <summary> Orders ResultPointsAndTransitions by number of transitions, ascending.</summary>\r
317                 private class ResultPointsAndTransitionsComparator : Comparator\r
318                 {\r
319                         public int compare(System.Object o1, System.Object o2)\r
320                         {\r
321                                 return ((ResultPointsAndTransitions) o1).Transitions - ((ResultPointsAndTransitions) o2).Transitions;\r
322                         }\r
323                 }\r
324         }\r
325 }