New C# port from Suraj Supekar
[zxing.git] / csharp / qrcode / detector / Detector.cs
diff --git a/csharp/qrcode/detector/Detector.cs b/csharp/qrcode/detector/Detector.cs
new file mode 100755 (executable)
index 0000000..08f4008
--- /dev/null
@@ -0,0 +1,420 @@
+/*\r
+* Copyright 2007 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 DecodeHintType = com.google.zxing.DecodeHintType;\r
+using ReaderException = com.google.zxing.ReaderException;\r
+using ResultPoint = com.google.zxing.ResultPoint;\r
+using ResultPointCallback = com.google.zxing.ResultPointCallback;\r
+using BitMatrix = com.google.zxing.common.BitMatrix;\r
+using DetectorResult = com.google.zxing.common.DetectorResult;\r
+using GridSampler = com.google.zxing.common.GridSampler;\r
+using PerspectiveTransform = com.google.zxing.common.PerspectiveTransform;\r
+using Version = com.google.zxing.qrcode.decoder.Version;\r
+namespace com.google.zxing.qrcode.detector\r
+{\r
+       \r
+       /// <summary> <p>Encapsulates logic that can detect a QR Code in an image, even if the QR 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 class Detector\r
+       {\r
+               virtual protected internal BitMatrix Image\r
+               {\r
+                       get\r
+                       {\r
+                               return image;\r
+                       }\r
+                       \r
+               }\r
+               virtual protected internal ResultPointCallback ResultPointCallback\r
+               {\r
+                       get\r
+                       {\r
+                               return resultPointCallback;\r
+                       }\r
+                       \r
+               }\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
+               private ResultPointCallback resultPointCallback;\r
+               \r
+               public Detector(BitMatrix image)\r
+               {\r
+                       this.image = image;\r
+               }\r
+               \r
+               /// <summary> <p>Detects a QR Code in an image, simply.</p>\r
+               /// \r
+               /// </summary>\r
+               /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code\r
+               /// </returns>\r
+               /// <throws>  ReaderException if no QR Code can be found </throws>\r
+               public virtual DetectorResult detect()\r
+               {\r
+                       return detect(null);\r
+               }\r
+               \r
+               /// <summary> <p>Detects a QR Code in an image, simply.</p>\r
+               /// \r
+               /// </summary>\r
+               /// <param name="hints">optional hints to detector\r
+               /// </param>\r
+               /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code\r
+               /// </returns>\r
+               /// <throws>  ReaderException if no QR Code can be found </throws>\r
+               public virtual DetectorResult detect(System.Collections.Hashtable hints)\r
+               {\r
+                       \r
+                       resultPointCallback = hints == null?null:(ResultPointCallback) hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK];\r
+                       \r
+                       FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);\r
+                       FinderPatternInfo info = finder.find(hints);\r
+                       \r
+                       return processFinderPatternInfo(info);\r
+               }\r
+               \r
+               protected internal virtual DetectorResult processFinderPatternInfo(FinderPatternInfo info)\r
+               {\r
+                       \r
+                       FinderPattern topLeft = info.TopLeft;\r
+                       FinderPattern topRight = info.TopRight;\r
+                       FinderPattern bottomLeft = info.BottomLeft;\r
+                       \r
+                       float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);\r
+                       if (moduleSize < 1.0f)\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);\r
+                       Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);\r
+                       int modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7;\r
+                       \r
+                       AlignmentPattern alignmentPattern = null;\r
+                       // Anything above version 1 has an alignment pattern\r
+                       if (provisionalVersion.AlignmentPatternCenters.Length > 0)\r
+                       {\r
+                               \r
+                               // Guess where a "bottom right" finder pattern would have been\r
+                               float bottomRightX = topRight.X - topLeft.X + bottomLeft.X;\r
+                               float bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y;\r
+                               \r
+                               // Estimate that alignment pattern is closer by 3 modules\r
+                               // from "bottom right" to known top left location\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
+                               float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters;\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 estAlignmentX = (int) (topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.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 estAlignmentY = (int) (topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y));\r
+                               \r
+                               // Kind of arbitrary -- expand search radius before giving up\r
+                               for (int i = 4; i <= 16; i <<= 1)\r
+                               {\r
+                                       try\r
+                                       {\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
+                                               alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float) i);\r
+                                               break;\r
+                                       }\r
+                                       catch (ReaderException re)\r
+                                       {\r
+                                               // try next round\r
+                                       }\r
+                               }\r
+                               // If we didn't find alignment pattern... well try anyway without it\r
+                       }\r
+                       \r
+                       PerspectiveTransform transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);\r
+                       \r
+                       BitMatrix bits = sampleGrid(image, transform, dimension);\r
+                       \r
+                       ResultPoint[] points;\r
+                       if (alignmentPattern == null)\r
+                       {\r
+                               points = new ResultPoint[]{bottomLeft, topLeft, topRight};\r
+                       }\r
+                       else\r
+                       {\r
+                               points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};\r
+                       }\r
+                       return new DetectorResult(bits, points);\r
+               }\r
+               \r
+               public virtual PerspectiveTransform createTransform(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension)\r
+               {\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
+                       float dimMinusThree = (float) dimension - 3.5f;\r
+                       float bottomRightX;\r
+                       float bottomRightY;\r
+                       float sourceBottomRightX;\r
+                       float sourceBottomRightY;\r
+                       if (alignmentPattern != null)\r
+                       {\r
+                               bottomRightX = alignmentPattern.X;\r
+                               bottomRightY = alignmentPattern.Y;\r
+                               sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;\r
+                       }\r
+                       else\r
+                       {\r
+                               // Don't have an alignment pattern, just make up the bottom-right point\r
+                               bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X;\r
+                               bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y;\r
+                               sourceBottomRightX = sourceBottomRightY = dimMinusThree;\r
+                       }\r
+                       \r
+                       PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, sourceBottomRightY, 3.5f, dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX, bottomRightY, bottomLeft.X, bottomLeft.Y);\r
+                       \r
+                       return transform;\r
+               }\r
+               \r
+               private static BitMatrix sampleGrid(BitMatrix image, PerspectiveTransform transform, int dimension)\r
+               {\r
+                       \r
+                       GridSampler sampler = GridSampler.Instance;\r
+                       return sampler.sampleGrid(image, dimension, transform);\r
+               }\r
+               \r
+               /// <summary> <p>Computes the dimension (number of modules on a size) of the QR Code based on the position\r
+               /// of the finder patterns and estimated module size.</p>\r
+               /// </summary>\r
+               protected internal static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize)\r
+               {\r
+                       int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize);\r
+                       int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize);\r
+                       int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;\r
+                       switch (dimension & 0x03)\r
+                       {\r
+                               \r
+                               // mod 4\r
+                               case 0: \r
+                                       dimension++;\r
+                                       break;\r
+                                       // 1? do nothing\r
+                               \r
+                               case 2: \r
+                                       dimension--;\r
+                                       break;\r
+                               \r
+                               case 3: \r
+                                       throw ReaderException.Instance;\r
+                               }\r
+                       return dimension;\r
+               }\r
+               \r
+               /// <summary> <p>Computes an average estimated module size based on estimated derived from the positions\r
+               /// of the three finder patterns.</p>\r
+               /// </summary>\r
+               protected internal virtual float calculateModuleSize(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft)\r
+               {\r
+                       // Take the average\r
+                       return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;\r
+               }\r
+               \r
+               /// <summary> <p>Estimates module size based on two finder patterns -- it uses\r
+               /// {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the\r
+               /// width of each, measuring along the axis between their centers.</p>\r
+               /// </summary>\r
+               private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern)\r
+               {\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
+                       float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.X, (int) pattern.Y, (int) otherPattern.X, (int) otherPattern.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
+                       float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.X, (int) otherPattern.Y, (int) pattern.X, (int) pattern.Y);\r
+                       if (System.Single.IsNaN(moduleSizeEst1))\r
+                       {\r
+                               return moduleSizeEst2 / 7.0f;\r
+                       }\r
+                       if (System.Single.IsNaN(moduleSizeEst2))\r
+                       {\r
+                               return moduleSizeEst1 / 7.0f;\r
+                       }\r
+                       // Average them, and divide by 7 since we've counted the width of 3 black modules,\r
+                       // and 1 white and 1 black module on either side. Ergo, divide sum by 14.\r
+                       return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;\r
+               }\r
+               \r
+               /// <summary> See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of\r
+               /// a finder pattern by looking for a black-white-black run from the center in the direction\r
+               /// of another point (another finder pattern center), and in the opposite direction too.</p>\r
+               /// </summary>\r
+               private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY)\r
+               {\r
+                       \r
+                       float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);\r
+                       \r
+                       // Now count other way -- don't run off image though of course\r
+                       float scale = 1.0f;\r
+                       int otherToX = fromX - (toX - fromX);\r
+                       if (otherToX < 0)\r
+                       {\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
+                               scale = (float) fromX / (float) (fromX - otherToX);\r
+                               otherToX = 0;\r
+                       }\r
+                       else if (otherToX >= image.Width)\r
+                       {\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
+                               scale = (float) (image.Width - 1 - fromX) / (float) (otherToX - fromX);\r
+                               otherToX = image.Width - 1;\r
+                       }\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 otherToY = (int) (fromY - (toY - fromY) * scale);\r
+                       \r
+                       scale = 1.0f;\r
+                       if (otherToY < 0)\r
+                       {\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
+                               scale = (float) fromY / (float) (fromY - otherToY);\r
+                               otherToY = 0;\r
+                       }\r
+                       else if (otherToY >= image.Height)\r
+                       {\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
+                               scale = (float) (image.Height - 1 - fromY) / (float) (otherToY - fromY);\r
+                               otherToY = image.Height - 1;\r
+                       }\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
+                       otherToX = (int) (fromX + (otherToX - fromX) * scale);\r
+                       \r
+                       result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);\r
+                       return result - 1.0f; // -1 because we counted the middle pixel twice\r
+               }\r
+               \r
+               /// <summary> <p>This method traces a line from a point in the image, in the direction towards another point.\r
+               /// It begins in a black region, and keeps going until it finds white, then black, then white again.\r
+               /// It reports the distance from the start to this point.</p>\r
+               /// \r
+               /// <p>This is used when figuring out how wide a finder pattern is, when the finder pattern\r
+               /// may be skewed or rotated.</p>\r
+               /// </summary>\r
+               private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY)\r
+               {\r
+                       // Mild variant of Bresenham's algorithm;\r
+                       // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm\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 state = 0; // In black pixels, looking for white, first or second time\r
+                       for (int x = fromX, y = fromY; x != toX; x += xstep)\r
+                       {\r
+                               \r
+                               int realX = steep?y:x;\r
+                               int realY = steep?x:y;\r
+                               if (state == 1)\r
+                               {\r
+                                       // In white pixels, looking for black\r
+                                       if (image.get_Renamed(realX, realY))\r
+                                       {\r
+                                               state++;\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       if (!image.get_Renamed(realX, realY))\r
+                                       {\r
+                                               state++;\r
+                                       }\r
+                               }\r
+                               \r
+                               if (state == 3)\r
+                               {\r
+                                       // Found black, white, black, and stumbled back onto white; done\r
+                                       int diffX = x - fromX;\r
+                                       int diffY = y - fromY;\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
+                                       return (float) System.Math.Sqrt((double) (diffX * diffX + diffY * diffY));\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
+                       int diffX2 = toX - fromX;\r
+                       int diffY2 = toY - fromY;\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
+                       return (float) System.Math.Sqrt((double) (diffX2 * diffX2 + diffY2 * diffY2));\r
+               }\r
+               \r
+               /// <summary> <p>Attempts to locate an alignment pattern in a limited region of the image, which is\r
+               /// guessed to contain it. This method uses {@link AlignmentPattern}.</p>\r
+               /// \r
+               /// </summary>\r
+               /// <param name="overallEstModuleSize">estimated module size so far\r
+               /// </param>\r
+               /// <param name="estAlignmentX">x coordinate of center of area probably containing alignment pattern\r
+               /// </param>\r
+               /// <param name="estAlignmentY">y coordinate of above\r
+               /// </param>\r
+               /// <param name="allowanceFactor">number of pixels in all directions to search from the center\r
+               /// </param>\r
+               /// <returns> {@link AlignmentPattern} if found, or null otherwise\r
+               /// </returns>\r
+               /// <throws>  ReaderException if an unexpected error occurs during detection </throws>\r
+               protected internal virtual AlignmentPattern findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, float allowanceFactor)\r
+               {\r
+                       // Look for an alignment pattern (3 modules in size) around where it\r
+                       // should be\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 allowance = (int) (allowanceFactor * overallEstModuleSize);\r
+                       int alignmentAreaLeftX = System.Math.Max(0, estAlignmentX - allowance);\r
+                       int alignmentAreaRightX = System.Math.Min(image.Width - 1, estAlignmentX + allowance);\r
+                       if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3)\r
+                       {\r
+                               throw ReaderException.Instance;\r
+                       }\r
+                       \r
+                       int alignmentAreaTopY = System.Math.Max(0, estAlignmentY - allowance);\r
+                       int alignmentAreaBottomY = System.Math.Min(image.Height - 1, estAlignmentY + allowance);\r
+                       \r
+                       AlignmentPatternFinder alignmentFinder = new AlignmentPatternFinder(image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, resultPointCallback);\r
+                       return alignmentFinder.find();\r
+               }\r
+               \r
+               /// <summary> Ends up being a bit faster than Math.round(). This merely rounds its argument to the nearest int,\r
+               /// where x.5 rounds up.\r
+               /// </summary>\r
+               private static int round(float d)\r
+               {\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
+                       return (int) (d + 0.5f);\r
+               }\r
+       }\r
+}
\ No newline at end of file