New C# port from Suraj Supekar
[zxing.git] / csharp / common / detector / MonochromeRectangleDetector.cs
diff --git a/csharp/common/detector/MonochromeRectangleDetector.cs b/csharp/common/detector/MonochromeRectangleDetector.cs
new file mode 100755 (executable)
index 0000000..eea5cb1
--- /dev/null
@@ -0,0 +1,255 @@
+/*\r
+* Copyright 2009 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
+namespace com.google.zxing.common.detector\r
+{\r
+       \r
+       /// <summary> <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.\r
+       /// It looks within a mostly white region of an image for a region of black and white, but mostly\r
+       /// black. It returns the four corners of the region, as best it can determine.</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 MonochromeRectangleDetector\r
+       {\r
+               \r
+               private const int MAX_MODULES = 32;\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
+               \r
+               public MonochromeRectangleDetector(BitMatrix image)\r
+               {\r
+                       this.image = image;\r
+               }\r
+               \r
+               /// <summary> <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly\r
+               /// white, in an image.</p>\r
+               /// \r
+               /// </summary>\r
+               /// <returns> {@link ResultPoint}[] describing the corners of the rectangular region. The first and\r
+               /// last points are opposed on the diagonal, as are the second and third. The first point will be\r
+               /// the topmost point and the last, the bottommost. The second point will be leftmost and the\r
+               /// third, the rightmost\r
+               /// </returns>\r
+               /// <throws>  ReaderException if no Data Matrix Code can be found </throws>\r
+               public ResultPoint[] detect()\r
+               {\r
+                       int height = image.Height;\r
+                       int width = image.Width;\r
+                       int halfHeight = height >> 1;\r
+                       int halfWidth = width >> 1;\r
+                       int deltaY = System.Math.Max(1, height / (MAX_MODULES << 3));\r
+                       int deltaX = System.Math.Max(1, width / (MAX_MODULES << 3));\r
+                       \r
+                       int top = 0;\r
+                       int bottom = height;\r
+                       int left = 0;\r
+                       int right = width;\r
+                       ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 1);\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
+                       top = (int) pointA.Y - 1;\r
+                       ResultPoint pointB = findCornerFromCenter(halfWidth, - deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1);\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
+                       left = (int) pointB.X - 1;\r
+                       ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1);\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
+                       right = (int) pointC.X + 1;\r
+                       ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, deltaY, top, bottom, halfWidth >> 1);\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
+                       bottom = (int) pointD.Y + 1;\r
+                       \r
+                       // Go try to find point A again with better information -- might have been off at first.\r
+                       pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 2);\r
+                       \r
+                       return new ResultPoint[]{pointA, pointB, pointC, pointD};\r
+               }\r
+               \r
+               /// <summary> Attempts to locate a corner of the barcode by scanning up, down, left or right from a center\r
+               /// point which should be within the barcode.\r
+               /// \r
+               /// </summary>\r
+               /// <param name="centerX">center's x component (horizontal)\r
+               /// </param>\r
+               /// <param name="deltaX">same as deltaY but change in x per step instead\r
+               /// </param>\r
+               /// <param name="left">minimum value of x\r
+               /// </param>\r
+               /// <param name="right">maximum value of x\r
+               /// </param>\r
+               /// <param name="centerY">center's y component (vertical)\r
+               /// </param>\r
+               /// <param name="deltaY">change in y per step. If scanning up this is negative; down, positive;\r
+               /// left or right, 0\r
+               /// </param>\r
+               /// <param name="top">minimum value of y to search through (meaningless when di == 0)\r
+               /// </param>\r
+               /// <param name="bottom">maximum value of y\r
+               /// </param>\r
+               /// <param name="maxWhiteRun">maximum run of white pixels that can still be considered to be within\r
+               /// the barcode\r
+               /// </param>\r
+               /// <returns> a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found\r
+               /// </returns>\r
+               /// <throws>  com.google.zxing.ReaderException if such a point cannot be found </throws>\r
+               private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right, int centerY, int deltaY, int top, int bottom, int maxWhiteRun)\r
+               {\r
+                       int[] lastRange = null;\r
+                       for (int y = centerY, x = centerX; y < bottom && y >= top && x < right && x >= left; y += deltaY, x += deltaX)\r
+                       {\r
+                               int[] range;\r
+                               if (deltaX == 0)\r
+                               {\r
+                                       // horizontal slices, up and down\r
+                                       range = blackWhiteRange(y, maxWhiteRun, left, right, true);\r
+                               }\r
+                               else\r
+                               {\r
+                                       // vertical slices, left and right\r
+                                       range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);\r
+                               }\r
+                               if (range == null)\r
+                               {\r
+                                       if (lastRange == null)\r
+                                       {\r
+                                               throw ReaderException.Instance;\r
+                                       }\r
+                                       // lastRange was found\r
+                                       if (deltaX == 0)\r
+                                       {\r
+                                               int lastY = y - deltaY;\r
+                                               if (lastRange[0] < centerX)\r
+                                               {\r
+                                                       if (lastRange[1] > centerX)\r
+                                                       {\r
+                                                               // straddle, choose one or the other based on direction\r
+                                                               return new ResultPoint(deltaY > 0?lastRange[0]:lastRange[1], lastY);\r
+                                                       }\r
+                                                       return new ResultPoint(lastRange[0], lastY);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       return new ResultPoint(lastRange[1], lastY);\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               int lastX = x - deltaX;\r
+                                               if (lastRange[0] < centerY)\r
+                                               {\r
+                                                       if (lastRange[1] > centerY)\r
+                                                       {\r
+                                                               return new ResultPoint(lastX, deltaX < 0?lastRange[0]:lastRange[1]);\r
+                                                       }\r
+                                                       return new ResultPoint(lastX, lastRange[0]);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       return new ResultPoint(lastX, lastRange[1]);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               lastRange = range;\r
+                       }\r
+                       throw ReaderException.Instance;\r
+               }\r
+               \r
+               /// <summary> Computes the start and end of a region of pixels, either horizontally or vertically, that could\r
+               /// be part of a Data Matrix barcode.\r
+               /// \r
+               /// </summary>\r
+               /// <param name="fixedDimension">if scanning horizontally, this is the row (the fixed vertical location)\r
+               /// where we are scanning. If scanning vertically it's the column, the fixed horizontal location\r
+               /// </param>\r
+               /// <param name="maxWhiteRun">largest run of white pixels that can still be considered part of the\r
+               /// barcode region\r
+               /// </param>\r
+               /// <param name="minDim">minimum pixel location, horizontally or vertically, to consider\r
+               /// </param>\r
+               /// <param name="maxDim">maximum pixel location, horizontally or vertically, to consider\r
+               /// </param>\r
+               /// <param name="horizontal">if true, we're scanning left-right, instead of up-down\r
+               /// </param>\r
+               /// <returns> int[] with start and end of found range, or null if no such range is found\r
+               /// (e.g. only white was found)\r
+               /// </returns>\r
+               private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, bool horizontal)\r
+               {\r
+                       \r
+                       int center = (minDim + maxDim) >> 1;\r
+                       \r
+                       // Scan left/up first\r
+                       int start = center;\r
+                       while (start >= minDim)\r
+                       {\r
+                               if (horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start))\r
+                               {\r
+                                       start--;\r
+                               }\r
+                               else\r
+                               {\r
+                                       int whiteRunStart = start;\r
+                                       do \r
+                                       {\r
+                                               start--;\r
+                                       }\r
+                                       while (start >= minDim && !(horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start)));\r
+                                       int whiteRunSize = whiteRunStart - start;\r
+                                       if (start < minDim || whiteRunSize > maxWhiteRun)\r
+                                       {\r
+                                               start = whiteRunStart;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       start++;\r
+                       \r
+                       // Then try right/down\r
+                       int end = center;\r
+                       while (end < maxDim)\r
+                       {\r
+                               if (horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end))\r
+                               {\r
+                                       end++;\r
+                               }\r
+                               else\r
+                               {\r
+                                       int whiteRunStart = end;\r
+                                       do \r
+                                       {\r
+                                               end++;\r
+                                       }\r
+                                       while (end < maxDim && !(horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end)));\r
+                                       int whiteRunSize = end - whiteRunStart;\r
+                                       if (end >= maxDim || whiteRunSize > maxWhiteRun)\r
+                                       {\r
+                                               end = whiteRunStart;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       end--;\r
+                       \r
+                       return end > start?new int[]{start, end}:null;\r
+               }\r
+       }\r
+}
\ No newline at end of file