--- /dev/null
+/*\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