fix log to reflect reality
[zxing.git] / csharp / common / detector / MonochromeRectangleDetector.cs
1 /*\r
2 * Copyright 2009 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 namespace com.google.zxing.common.detector\r
21 {\r
22         \r
23         /// <summary> <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.\r
24         /// It looks within a mostly white region of an image for a region of black and white, but mostly\r
25         /// black. It returns the four corners of the region, as best it can determine.</p>\r
26         /// \r
27         /// </summary>\r
28         /// <author>  Sean Owen\r
29         /// </author>\r
30         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
31         /// </author>\r
32         public sealed class MonochromeRectangleDetector\r
33         {\r
34                 \r
35                 private const int MAX_MODULES = 32;\r
36                 \r
37                 //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
38                 private BitMatrix image;\r
39                 \r
40                 public MonochromeRectangleDetector(BitMatrix image)\r
41                 {\r
42                         this.image = image;\r
43                 }\r
44                 \r
45                 /// <summary> <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly\r
46                 /// white, in an image.</p>\r
47                 /// \r
48                 /// </summary>\r
49                 /// <returns> {@link ResultPoint}[] describing the corners of the rectangular region. The first and\r
50                 /// last points are opposed on the diagonal, as are the second and third. The first point will be\r
51                 /// the topmost point and the last, the bottommost. The second point will be leftmost and the\r
52                 /// third, the rightmost\r
53                 /// </returns>\r
54                 /// <throws>  ReaderException if no Data Matrix Code can be found </throws>\r
55                 public ResultPoint[] detect()\r
56                 {\r
57                         int height = image.Height;\r
58                         int width = image.Width;\r
59                         int halfHeight = height >> 1;\r
60                         int halfWidth = width >> 1;\r
61                         int deltaY = System.Math.Max(1, height / (MAX_MODULES << 3));\r
62                         int deltaX = System.Math.Max(1, width / (MAX_MODULES << 3));\r
63                         \r
64                         int top = 0;\r
65                         int bottom = height;\r
66                         int left = 0;\r
67                         int right = width;\r
68                         ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 1);\r
69                         //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
70                         top = (int) pointA.Y - 1;\r
71                         ResultPoint pointB = findCornerFromCenter(halfWidth, - deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1);\r
72                         //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
73                         left = (int) pointB.X - 1;\r
74                         ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1);\r
75                         //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
76                         right = (int) pointC.X + 1;\r
77                         ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, deltaY, top, bottom, halfWidth >> 1);\r
78                         //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
79                         bottom = (int) pointD.Y + 1;\r
80                         \r
81                         // Go try to find point A again with better information -- might have been off at first.\r
82                         pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 2);\r
83                         \r
84                         return new ResultPoint[]{pointA, pointB, pointC, pointD};\r
85                 }\r
86                 \r
87                 /// <summary> Attempts to locate a corner of the barcode by scanning up, down, left or right from a center\r
88                 /// point which should be within the barcode.\r
89                 /// \r
90                 /// </summary>\r
91                 /// <param name="centerX">center's x component (horizontal)\r
92                 /// </param>\r
93                 /// <param name="deltaX">same as deltaY but change in x per step instead\r
94                 /// </param>\r
95                 /// <param name="left">minimum value of x\r
96                 /// </param>\r
97                 /// <param name="right">maximum value of x\r
98                 /// </param>\r
99                 /// <param name="centerY">center's y component (vertical)\r
100                 /// </param>\r
101                 /// <param name="deltaY">change in y per step. If scanning up this is negative; down, positive;\r
102                 /// left or right, 0\r
103                 /// </param>\r
104                 /// <param name="top">minimum value of y to search through (meaningless when di == 0)\r
105                 /// </param>\r
106                 /// <param name="bottom">maximum value of y\r
107                 /// </param>\r
108                 /// <param name="maxWhiteRun">maximum run of white pixels that can still be considered to be within\r
109                 /// the barcode\r
110                 /// </param>\r
111                 /// <returns> a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found\r
112                 /// </returns>\r
113                 /// <throws>  com.google.zxing.ReaderException if such a point cannot be found </throws>\r
114                 private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right, int centerY, int deltaY, int top, int bottom, int maxWhiteRun)\r
115                 {\r
116                         int[] lastRange = null;\r
117                         for (int y = centerY, x = centerX; y < bottom && y >= top && x < right && x >= left; y += deltaY, x += deltaX)\r
118                         {\r
119                                 int[] range;\r
120                                 if (deltaX == 0)\r
121                                 {\r
122                                         // horizontal slices, up and down\r
123                                         range = blackWhiteRange(y, maxWhiteRun, left, right, true);\r
124                                 }\r
125                                 else\r
126                                 {\r
127                                         // vertical slices, left and right\r
128                                         range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);\r
129                                 }\r
130                                 if (range == null)\r
131                                 {\r
132                                         if (lastRange == null)\r
133                                         {\r
134                                                 throw ReaderException.Instance;\r
135                                         }\r
136                                         // lastRange was found\r
137                                         if (deltaX == 0)\r
138                                         {\r
139                                                 int lastY = y - deltaY;\r
140                                                 if (lastRange[0] < centerX)\r
141                                                 {\r
142                                                         if (lastRange[1] > centerX)\r
143                                                         {\r
144                                                                 // straddle, choose one or the other based on direction\r
145                                                                 return new ResultPoint(deltaY > 0?lastRange[0]:lastRange[1], lastY);\r
146                                                         }\r
147                                                         return new ResultPoint(lastRange[0], lastY);\r
148                                                 }\r
149                                                 else\r
150                                                 {\r
151                                                         return new ResultPoint(lastRange[1], lastY);\r
152                                                 }\r
153                                         }\r
154                                         else\r
155                                         {\r
156                                                 int lastX = x - deltaX;\r
157                                                 if (lastRange[0] < centerY)\r
158                                                 {\r
159                                                         if (lastRange[1] > centerY)\r
160                                                         {\r
161                                                                 return new ResultPoint(lastX, deltaX < 0?lastRange[0]:lastRange[1]);\r
162                                                         }\r
163                                                         return new ResultPoint(lastX, lastRange[0]);\r
164                                                 }\r
165                                                 else\r
166                                                 {\r
167                                                         return new ResultPoint(lastX, lastRange[1]);\r
168                                                 }\r
169                                         }\r
170                                 }\r
171                                 lastRange = range;\r
172                         }\r
173                         throw ReaderException.Instance;\r
174                 }\r
175                 \r
176                 /// <summary> Computes the start and end of a region of pixels, either horizontally or vertically, that could\r
177                 /// be part of a Data Matrix barcode.\r
178                 /// \r
179                 /// </summary>\r
180                 /// <param name="fixedDimension">if scanning horizontally, this is the row (the fixed vertical location)\r
181                 /// where we are scanning. If scanning vertically it's the column, the fixed horizontal location\r
182                 /// </param>\r
183                 /// <param name="maxWhiteRun">largest run of white pixels that can still be considered part of the\r
184                 /// barcode region\r
185                 /// </param>\r
186                 /// <param name="minDim">minimum pixel location, horizontally or vertically, to consider\r
187                 /// </param>\r
188                 /// <param name="maxDim">maximum pixel location, horizontally or vertically, to consider\r
189                 /// </param>\r
190                 /// <param name="horizontal">if true, we're scanning left-right, instead of up-down\r
191                 /// </param>\r
192                 /// <returns> int[] with start and end of found range, or null if no such range is found\r
193                 /// (e.g. only white was found)\r
194                 /// </returns>\r
195                 private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, bool horizontal)\r
196                 {\r
197                         \r
198                         int center = (minDim + maxDim) >> 1;\r
199                         \r
200                         // Scan left/up first\r
201                         int start = center;\r
202                         while (start >= minDim)\r
203                         {\r
204                                 if (horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start))\r
205                                 {\r
206                                         start--;\r
207                                 }\r
208                                 else\r
209                                 {\r
210                                         int whiteRunStart = start;\r
211                                         do \r
212                                         {\r
213                                                 start--;\r
214                                         }\r
215                                         while (start >= minDim && !(horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start)));\r
216                                         int whiteRunSize = whiteRunStart - start;\r
217                                         if (start < minDim || whiteRunSize > maxWhiteRun)\r
218                                         {\r
219                                                 start = whiteRunStart;\r
220                                                 break;\r
221                                         }\r
222                                 }\r
223                         }\r
224                         start++;\r
225                         \r
226                         // Then try right/down\r
227                         int end = center;\r
228                         while (end < maxDim)\r
229                         {\r
230                                 if (horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end))\r
231                                 {\r
232                                         end++;\r
233                                 }\r
234                                 else\r
235                                 {\r
236                                         int whiteRunStart = end;\r
237                                         do \r
238                                         {\r
239                                                 end++;\r
240                                         }\r
241                                         while (end < maxDim && !(horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end)));\r
242                                         int whiteRunSize = end - whiteRunStart;\r
243                                         if (end >= maxDim || whiteRunSize > maxWhiteRun)\r
244                                         {\r
245                                                 end = whiteRunStart;\r
246                                                 break;\r
247                                         }\r
248                                 }\r
249                         }\r
250                         end--;\r
251                         \r
252                         return end > start?new int[]{start, end}:null;\r
253                 }\r
254         }\r
255 }