2 * Copyright 2008 ZXing authors
\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
8 * http://www.apache.org/licenses/LICENSE-2.0
\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
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
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
32 /// <author> Sean Owen
\r
34 /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
\r
36 public sealed class Detector
\r
39 //private static final int MAX_MODULES = 32;
\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
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
52 public Detector(BitMatrix image)
\r
55 rectangleDetector = new MonochromeRectangleDetector(image);
\r
58 /// <summary> <p>Detects a Data Matrix Code in an image.</p>
\r
61 /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code
\r
63 /// <throws> ReaderException if no Data Matrix Code can be found </throws>
\r
64 public DetectorResult detect()
\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
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
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
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
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
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
108 bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides
\r
112 // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now
\r
113 if (maybeTopLeft == null)
\r
115 maybeTopLeft = point;
\r
119 maybeBottomRight = point;
\r
124 if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null)
\r
126 throw ReaderException.Instance;
\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
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
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
145 else if (!pointCount.ContainsKey(pointB))
\r
149 else if (!pointCount.ContainsKey(pointC))
\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
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
168 int dimension = System.Math.Min(transitionsBetween(topLeft, topRight).Transitions, transitionsBetween(bottomRight, topRight).Transitions);
\r
169 if ((dimension & 0x01) == 1)
\r
171 // it can't be odd, so, round... up?
\r
176 BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);
\r
177 return new DetectorResult(bits, new ResultPoint[]{pointA, pointB, pointC, pointD});
\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
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
190 System.Int32 value_Renamed = 0;
\r
193 if (table.Count > 0)
\r
194 value_Renamed = (System.Int32)table[key];
\r
200 table[key] = value_Renamed == 0 ? INTEGERS[1] : INTEGERS[value_Renamed + 1];
\r
204 private static BitMatrix sampleGrid(BitMatrix image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension)
\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
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
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
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
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
251 bool isBlack = image.get_Renamed(steep?y:x, steep?x:y);
\r
252 if (isBlack != inBlack)
\r
268 return new ResultPointsAndTransitions(from, to, transitions);
\r
271 /// <summary> Simply encapsulates two points and a number of transitions between them.</summary>
\r
272 private class ResultPointsAndTransitions
\r
274 public ResultPoint From
\r
282 public ResultPoint To
\r
290 public int Transitions
\r
294 return transitions;
\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
308 this.transitions = transitions;
\r
310 public override System.String ToString()
\r
312 return from + "/" + to + '/' + transitions;
\r
316 /// <summary> Orders ResultPointsAndTransitions by number of transitions, ascending.</summary>
\r
317 private class ResultPointsAndTransitionsComparator : Comparator
\r
319 public int compare(System.Object o1, System.Object o2)
\r
321 return ((ResultPointsAndTransitions) o1).Transitions - ((ResultPointsAndTransitions) o2).Transitions;
\r