/// <summary> <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly /// white, in an image.</p> /// /// </summary> /// <returns> {@link ResultPoint}[] describing the corners of the rectangular region. The first and /// last points are opposed on the diagonal, as are the second and third. The first point will be /// the topmost point and the last, the bottommost. The second point will be leftmost and the /// third, the rightmost /// </returns> /// <throws> ReaderException if no Data Matrix Code can be found </throws> public ResultPoint[] detect() { int height = image.Height; int width = image.Width; int halfHeight = height >> 1; int halfWidth = width >> 1; int deltaY = System.Math.Max(1, height / (MAX_MODULES << 3)); int deltaX = System.Math.Max(1, width / (MAX_MODULES << 3)); int top = 0; int bottom = height; int left = 0; int right = width; ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, -deltaY, top, bottom, halfWidth >> 1); //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'" top = (int)pointA.Y - 1; ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1); //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'" left = (int)pointB.X - 1; ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1); //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'" right = (int)pointC.X + 1; ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, deltaY, top, bottom, halfWidth >> 1); //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'" bottom = (int)pointD.Y + 1; // Go try to find point A again with better information -- might have been off at first. pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, -deltaY, top, bottom, halfWidth >> 2); return(new ResultPoint[] { pointA, pointB, pointC, pointD }); }
public virtual PerspectiveTransform createTransform(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension) { //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'" float dimMinusThree = (float)dimension - 3.5f; float bottomRightX; float bottomRightY; float sourceBottomRightX; float sourceBottomRightY; if (alignmentPattern != null) { bottomRightX = alignmentPattern.X; bottomRightY = alignmentPattern.Y; sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f; } else { // Don't have an alignment pattern, just make up the bottom-right point bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X; bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y; sourceBottomRightX = sourceBottomRightY = dimMinusThree; } 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); return(transform); }
private static BitMatrix sampleGrid(BitMatrix matrix, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint topRight, ResultPoint bottomRight, int dimension) { // Note that unlike the QR Code sampler, we didn't find the center of modules, but the // very corners. So there is no 0.5f here; 0.0f is right. GridSampler sampler = GridSampler.Instance; return(sampler.sampleGrid(matrix, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y)); // p4FromY }
/// <summary> <p>Estimates module size (pixels in a module) based on the Start and End /// finder patterns.</p> /// /// </summary> /// <param name="vertices">an array of vertices: /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area /// </param> /// <returns> the module size. /// </returns> private static float computeModuleWidth(ResultPoint[] vertices) { float pixels1 = ResultPoint.distance(vertices[0], vertices[4]); float pixels2 = ResultPoint.distance(vertices[1], vertices[5]); float moduleWidth1 = (pixels1 + pixels2) / (17 * 2.0f); float pixels3 = ResultPoint.distance(vertices[6], vertices[2]); float pixels4 = ResultPoint.distance(vertices[7], vertices[3]); float moduleWidth2 = (pixels3 + pixels4) / (18 * 2.0f); return((moduleWidth1 + moduleWidth2) / 2.0f); }
private static Result translateResultPoints(Result result, int xOffset, int yOffset) { ResultPoint[] oldResultPoints = result.ResultPoints; ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.Length]; for (int i = 0; i < oldResultPoints.Length; i++) { ResultPoint oldPoint = oldResultPoints[i]; newResultPoints[i] = new ResultPoint(oldPoint.X + xOffset, oldPoint.Y + yOffset); } return(new Result(result.Text, result.RawBytes, newResultPoints, result.BarcodeFormat)); }
/// <summary> Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.</summary> private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to) { // See QR Code Detector, sizeOfBlackWhiteBlackRun() //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'" int fromX = (int)from.X; //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'" int fromY = (int)from.Y; //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'" int toX = (int)to.X; //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'" int toY = (int)to.Y; bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX); if (steep) { int temp = fromX; fromX = fromY; fromY = temp; temp = toX; toX = toY; toY = temp; } int dx = System.Math.Abs(toX - fromX); int dy = System.Math.Abs(toY - fromY); int error = -dx >> 1; int ystep = fromY < toY?1:-1; int xstep = fromX < toX?1:-1; int transitions = 0; bool inBlack = image.get_Renamed(steep?fromY:fromX, steep?fromX:fromY); for (int x = fromX, y = fromY; x != toX; x += xstep) { bool isBlack = image.get_Renamed(steep?y:x, steep?x:y); if (isBlack != inBlack) { transitions++; inBlack = isBlack; } error += dy; if (error > 0) { if (y == toY) { break; } y += ystep; error -= dx; } } return(new ResultPointsAndTransitions(from, to, transitions)); }
/// <summary> Because we scan horizontally to detect the start and stop patterns, the vertical component of /// the codeword coordinates will be slightly wrong if there is any skew or rotation in the image. /// This method moves those points back onto the edges of the theoretically perfect bounding /// quadrilateral if needed. /// /// </summary> /// <param name="vertices">The eight vertices located by findVertices(). /// </param> private static void correctCodeWordVertices(ResultPoint[] vertices, bool upsideDown) { float skew = vertices[4].Y - vertices[6].Y; if (upsideDown) { skew = -skew; } if (skew > SKEW_THRESHOLD) { // Fix v4 float length = vertices[4].X - vertices[0].X; float deltax = vertices[6].X - vertices[0].X; float deltay = vertices[6].Y - vertices[0].Y; float correction = length * deltay / deltax; vertices[4] = new ResultPoint(vertices[4].X, vertices[4].Y + correction); } else if (-skew > SKEW_THRESHOLD) { // Fix v6 float length = vertices[2].X - vertices[6].X; float deltax = vertices[2].X - vertices[4].X; float deltay = vertices[2].Y - vertices[4].Y; float correction = length * deltay / deltax; vertices[6] = new ResultPoint(vertices[6].X, vertices[6].Y - correction); } skew = vertices[7].Y - vertices[5].Y; if (upsideDown) { skew = -skew; } if (skew > SKEW_THRESHOLD) { // Fix v5 float length = vertices[5].X - vertices[1].X; float deltax = vertices[7].X - vertices[1].X; float deltay = vertices[7].Y - vertices[1].Y; float correction = length * deltay / deltax; vertices[5] = new ResultPoint(vertices[5].X, vertices[5].Y + correction); } else if (-skew > SKEW_THRESHOLD) { // Fix v7 float length = vertices[3].X - vertices[7].X; float deltax = vertices[3].X - vertices[5].X; float deltay = vertices[3].Y - vertices[5].Y; float correction = length * deltay / deltax; vertices[7] = new ResultPoint(vertices[7].X, vertices[7].Y - correction); } }
public void AddPossibleResultPoint(com.google.zxing.ResultPoint point) { var points = possibleResultPoints; lock (points) { points.Add(point); var size = points.Count; if (size > MAX_RESULT_POINTS) { points.RemoveRange(0, size - MAX_RESULT_POINTS / 2); } } }
private static BitMatrix sampleGrid(BitMatrix image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension) { // We make up the top right point for now, based on the others. // TODO: we actually found a fourth corner above and figured out which of two modules // it was the corner of. We could use that here and adjust for perspective distortion. float topRightX = (bottomRight.X - bottomLeft.X) + topLeft.X; float topRightY = (bottomRight.Y - bottomLeft.Y) + topLeft.Y; // Note that unlike in the QR Code sampler, we didn't find the center of modules, but the // very corners. So there is no 0.5f here; 0.0f is right. GridSampler sampler = GridSampler.Instance; 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)); }
/// <summary> Computes the dimension (number of modules in a row) of the PDF417 Code /// based on vertices of the codeword area and estimated module size. /// /// </summary> /// <param name="topLeft"> of codeword area /// </param> /// <param name="topRight"> of codeword area /// </param> /// <param name="bottomLeft"> of codeword area /// </param> /// <param name="bottomRight">of codeword are /// </param> /// <param name="moduleWidth">estimated module size /// </param> /// <returns> the number of modules in a row. /// </returns> private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) { int topRowDimension = round(ResultPoint.distance(topLeft, topRight) / moduleWidth); int bottomRowDimension = round(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth); return(((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17); /* * int topRowDimension = round(ResultPoint.distance(topLeft, * topRight)); //moduleWidth); int bottomRowDimension = * round(ResultPoint.distance(bottomLeft, bottomRight)); // * moduleWidth); int dimension = ((topRowDimension + bottomRowDimension) * >> 1); // Round up to nearest 17 modules i.e. there are 17 modules per * codeword //int dimension = ((((topRowDimension + bottomRowDimension) >> * 1) + 8) / 17) * 17; return dimension; */ }
// Note that we don't try rotation without the try harder flag, even if rotation was supported. public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints) { var retval = doDecode(image, hints); if (retval.HasValue()) { return(retval); } else { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); if (tryHarder && image.RotateSupported) { BinaryBitmap rotatedImage = image.rotateCounterClockwise(); Result result = doDecode(rotatedImage, hints); if (result == null) { return(null); } // Record that we found it rotated 90 degrees CCW / 270 degrees CW System.Collections.Hashtable metadata = result.ResultMetadata; int orientation = 270; if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION)) { // But if we found it reversed in doDecode(), add in that result here: orientation = (orientation + ((System.Int32)metadata[ResultMetadataType.ORIENTATION])) % 360; } result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object)orientation); // Update result points ResultPoint[] points = result.ResultPoints; int height = rotatedImage.Height; for (int i = 0; i < points.Length; i++) { points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X); } return(result); } else { return(null); } } }
/// <summary> <p>Estimates module size based on two finder patterns -- it uses /// {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the /// width of each, measuring along the axis between their centers.</p> /// </summary> private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) { //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'" float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern.X, (int)pattern.Y, (int)otherPattern.X, (int)otherPattern.Y); //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'" float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern.X, (int)otherPattern.Y, (int)pattern.X, (int)pattern.Y); if (System.Single.IsNaN(moduleSizeEst1)) { return(moduleSizeEst2 / 7.0f); } if (System.Single.IsNaN(moduleSizeEst2)) { return(moduleSizeEst1 / 7.0f); } // Average them, and divide by 7 since we've counted the width of 3 black modules, // and 1 white and 1 black module on either side. Ergo, divide sum by 14. return((moduleSizeEst1 + moduleSizeEst2) / 14.0f); }
// Note that we don't try rotation without the try harder flag, even if rotation was supported. // public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) public virtual Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary <Object, Object> hints) // added by .net follower (http://dotnetfollower.com) { try { return(doDecode(image, hints)); } catch (ReaderException re) { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); if (tryHarder && image.RotateSupported) { BinaryBitmap rotatedImage = image.rotateCounterClockwise(); Result result = doDecode(rotatedImage, hints); // Record that we found it rotated 90 degrees CCW / 270 degrees CW // System.Collections.Hashtable metadata = result.ResultMetadata; // commented by .net follower (http://dotnetfollower.com) System.Collections.Generic.Dictionary <Object, Object> metadata = result.ResultMetadata; // added by .net follower (http://dotnetfollower.com) int orientation = 270; if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION)) { // But if we found it reversed in doDecode(), add in that result here: orientation = (orientation + ((System.Int32)metadata[ResultMetadataType.ORIENTATION])) % 360; } result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object)orientation); // Update result points ResultPoint[] points = result.ResultPoints; int height = rotatedImage.Height; for (int i = 0; i < points.Length; i++) { points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X); } return(result); } else { throw re; } } }
/// <summary> <p>Computes the dimension (number of modules on a size) of the QR Code based on the position /// of the finder patterns and estimated module size.</p> /// </summary> protected internal static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize) { int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize); int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; switch (dimension & 0x03) { // mod 4 case 0: dimension++; break; // 1? do nothing case 2: dimension--; break; case 3: throw ReaderException.Instance; } return(dimension); }
// Note that we don't try rotation without the try harder flag, even if rotation was supported. public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints) { try { return doDecode(image, hints); } catch (ReaderException re) { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); if (tryHarder && image.RotateSupported) { BinaryBitmap rotatedImage = image.rotateCounterClockwise(); Result result = doDecode(rotatedImage, hints); // Record that we found it rotated 90 degrees CCW / 270 degrees CW System.Collections.Hashtable metadata = result.ResultMetadata; int orientation = 270; if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION)) { // But if we found it reversed in doDecode(), add in that result here: orientation = (orientation + ((System.Int32) metadata[ResultMetadataType.ORIENTATION])) % 360; } result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) orientation); // Update result points ResultPoint[] points = result.ResultPoints; int height = rotatedImage.Height; for (int i = 0; i < points.Length; i++) { points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X); } return result; } else { throw re; } } }
/// <summary> Increments the Integer associated with a key by one.</summary> private static void increment(System.Collections.Hashtable table, ResultPoint key) { //System.Int32 value_Renamed = (System.Int32) table[key]; ////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'" //table[key] = value_Renamed == null?INTEGERS[1]:INTEGERS[value_Renamed + 1]; // Redivivus.in Java to c# Porting update // 30/01/2010 // Added // START System.Int32 value_Renamed = 0; try { if (table.Count > 0) { value_Renamed = (System.Int32)table[key]; } } catch { value_Renamed = 0; } table[key] = value_Renamed == 0 ? INTEGERS[1] : INTEGERS[value_Renamed + 1]; //END }
protected internal virtual DetectorResult processFinderPatternInfo(FinderPatternInfo info) { FinderPattern topLeft = info.TopLeft; FinderPattern topRight = info.TopRight; FinderPattern bottomLeft = info.BottomLeft; float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); if (moduleSize < 1.0f) { throw ReaderException.Instance; } int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension); int modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7; AlignmentPattern alignmentPattern = null; // Anything above version 1 has an alignment pattern if (provisionalVersion.AlignmentPatternCenters.Length > 0) { // Guess where a "bottom right" finder pattern would have been float bottomRightX = topRight.X - topLeft.X + bottomLeft.X; float bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y; // Estimate that alignment pattern is closer by 3 modules // from "bottom right" to known top left location //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'" float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters; //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'" int estAlignmentX = (int) (topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X)); //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'" int estAlignmentY = (int) (topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y)); // Kind of arbitrary -- expand search radius before giving up for (int i = 4; i <= 16; i <<= 1) { try { //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'" alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float) i); break; } catch (ReaderException) { // try next round } } // If we didn't find alignment pattern... well try anyway without it } PerspectiveTransform transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); BitMatrix bits = sampleGrid(image, transform, dimension); ResultPoint[] points; if (alignmentPattern == null) { points = new ResultPoint[]{bottomLeft, topLeft, topRight}; } else { points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern}; } return new DetectorResult(bits, points); }
/// <summary> <p>Computes the dimension (number of modules on a size) of the QR Code based on the position /// of the finder patterns and estimated module size.</p> /// </summary> protected internal static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize) { int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize); int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; switch (dimension & 0x03) { // mod 4 case 0: dimension++; break; // 1? do nothing case 2: dimension--; break; case 3: throw ReaderException.Instance; } return dimension; }
private static PerspectiveTransform createTransform(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension) { float dimMinusThree = (float) dimension - 3.5f; float bottomRightX; float bottomRightY; float sourceBottomRightX; float sourceBottomRightY; if (alignmentPattern != null) { bottomRightX = alignmentPattern.X; bottomRightY = alignmentPattern.Y; sourceBottomRightX = dimMinusThree - 3.0f; sourceBottomRightY = sourceBottomRightX; } else { // Don't have an alignment pattern, just make up the bottom-right point bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X; bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y; sourceBottomRightX = dimMinusThree; sourceBottomRightY = dimMinusThree; } return 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); }
/// <returns> the 3 best {@link FinderPattern}s from our list of candidates. The "best" are /// those that have been detected at least {@link #CENTER_QUORUM} times, and whose module /// size differs from the average among those patterns the least /// </returns> /// <throws> ReaderException if 3 such finder patterns do not exist </throws> private FinderPattern[][] selectBestPatterns() { // System.Collections.ArrayList possibleCenters = PossibleCenters; // commented by .net follower (http://dotnetfollower.com) System.Collections.Generic.List <Object> possibleCenters = PossibleCenters; // added by .net follower (http://dotnetfollower.com) int size = possibleCenters.Count; if (size < 3) { // Couldn't find enough finder patterns throw ReaderException.Instance; } /* * Begin HE modifications to safely detect multiple codes of equal size */ if (size == 3) { return(new FinderPattern[][] { new FinderPattern[] { (FinderPattern)possibleCenters[0], (FinderPattern)possibleCenters[1], (FinderPattern)possibleCenters[2] } }); } // Sort by estimated module size to speed up the upcoming checks Collections.insertionSort(possibleCenters, new ModuleSizeComparator()); /* * Now lets start: build a list of tuples of three finder locations that * - feature similar module sizes * - are placed in a distance so the estimated module count is within the QR specification * - have similar distance between upper left/right and left top/bottom finder patterns * - form a triangle with 90° angle (checked by comparing top right/bottom left distance * with pythagoras) * * Note: we allow each point to be used for more than one code region: this might seem * counterintuitive at first, but the performance penalty is not that big. At this point, * we cannot make a good quality decision whether the three finders actually represent * a QR code, or are just by chance layouted so it looks like there might be a QR code there. * So, if the layout seems right, lets have the decoder try to decode. */ // System.Collections.ArrayList results = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // holder for the results // commented by .net follower (http://dotnetfollower.com) System.Collections.Generic.List <Object> results = new System.Collections.Generic.List <Object>(10); // holder for the results // added by .net follower (http://dotnetfollower.com) for (int i1 = 0; i1 < (size - 2); i1++) { FinderPattern p1 = (FinderPattern)possibleCenters[i1]; if (p1 == null) { continue; } for (int i2 = i1 + 1; i2 < (size - 1); i2++) { FinderPattern p2 = (FinderPattern)possibleCenters[i2]; if (p2 == null) { continue; } // Compare the expected module sizes; if they are really off, skip float vModSize12 = (p1.EstimatedModuleSize - p2.EstimatedModuleSize) / (System.Math.Min(p1.EstimatedModuleSize, p2.EstimatedModuleSize)); float vModSize12A = System.Math.Abs(p1.EstimatedModuleSize - p2.EstimatedModuleSize); if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) { // break, since elements are ordered by the module size deviation there cannot be // any more interesting elements for the given p1. break; } for (int i3 = i2 + 1; i3 < size; i3++) { FinderPattern p3 = (FinderPattern)possibleCenters[i3]; if (p3 == null) { continue; } // Compare the expected module sizes; if they are really off, skip float vModSize23 = (p2.EstimatedModuleSize - p3.EstimatedModuleSize) / (System.Math.Min(p2.EstimatedModuleSize, p3.EstimatedModuleSize)); float vModSize23A = System.Math.Abs(p2.EstimatedModuleSize - p3.EstimatedModuleSize); if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) { // break, since elements are ordered by the module size deviation there cannot be // any more interesting elements for the given p1. break; } FinderPattern[] test = new FinderPattern[] { p1, p2, p3 }; ResultPoint.orderBestPatterns(test); // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal FinderPatternInfo info = new FinderPatternInfo(test); float dA = ResultPoint.distance(info.TopLeft, info.BottomLeft); float dC = ResultPoint.distance(info.TopRight, info.BottomLeft); float dB = ResultPoint.distance(info.TopLeft, info.TopRight); // Check the sizes float estimatedModuleCount = ((dA + dB) / p1.EstimatedModuleSize) / 2; if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) { continue; } // Calculate the difference of the edge lengths in percent float vABBC = System.Math.Abs(((dA - dB) / System.Math.Min(dA, dB))); if (vABBC >= 0.1f) { continue; } // Calculate the diagonal length by assuming a 90° angle at topleft //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'" float dCpy = (float)System.Math.Sqrt(dA * dA + dB * dB); // Compare to the real distance in % float vPyC = System.Math.Abs(((dC - dCpy) / System.Math.Min(dC, dCpy))); if (vPyC >= 0.1f) { continue; } // All tests passed! results.Add(test); } // end iterate p3 } // end iterate p2 } // end iterate p1 if (!(results.Count == 0)) { FinderPattern[][] resultArray = new FinderPattern[results.Count][]; for (int i = 0; i < results.Count; i++) { resultArray[i] = (FinderPattern[])results[i]; } return(resultArray); } // Nothing found! throw ReaderException.Instance; }
/// <summary> <p>Detects a Data Matrix Code in an image.</p> /// /// </summary> /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code /// </returns> /// <throws> ReaderException if no Data Matrix Code can be found </throws> public DetectorResult detect() { ResultPoint[] cornerPoints = rectangleDetector.detect(); ResultPoint pointA = cornerPoints[0]; ResultPoint pointB = cornerPoints[1]; ResultPoint pointC = cornerPoints[2]; ResultPoint pointD = cornerPoints[3]; // Point A and D are across the diagonal from one another, // as are B and C. Figure out which are the solid black lines // by counting transitions System.Collections.Generic.List <Object> transitions = new System.Collections.Generic.List <Object>(4); //GregBray: Removed Synchronized wrapper transitions.Add(transitionsBetween(pointA, pointB)); transitions.Add(transitionsBetween(pointA, pointC)); transitions.Add(transitionsBetween(pointB, pointD)); transitions.Add(transitionsBetween(pointC, pointD)); Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator()); // Sort by number of transitions. First two will be the two solid sides; last two // will be the two alternating black/white sides ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions)transitions[0]; ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions)transitions[1]; // Figure out which point is their intersection by tallying up the number of times we see the // endpoints in the four endpoints. One will show up twice. System.Collections.Generic.Dictionary <Object, Object> pointCount = new System.Collections.Generic.Dictionary <Object, Object>(); increment(pointCount, lSideOne.From); increment(pointCount, lSideOne.To); increment(pointCount, lSideTwo.From); increment(pointCount, lSideTwo.To); ResultPoint maybeTopLeft = null; ResultPoint bottomLeft = null; ResultPoint maybeBottomRight = null; System.Collections.IEnumerator points = pointCount.Keys.GetEnumerator(); //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'" while (points.MoveNext()) { //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'" ResultPoint point = (ResultPoint)points.Current; System.Int32 value_Renamed = (System.Int32)pointCount[point]; if (value_Renamed == 2) { bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides } else { // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now if (maybeTopLeft == null) { maybeTopLeft = point; } else { maybeBottomRight = point; } } } if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) { throw ReaderException.Instance; } // Bottom left is correct but top left and bottom right might be switched ResultPoint[] corners = new ResultPoint[] { maybeTopLeft, bottomLeft, maybeBottomRight }; // Use the dot product trick to sort them out ResultPoint.orderBestPatterns(corners); // Now we know which is which: ResultPoint bottomRight = corners[0]; bottomLeft = corners[1]; ResultPoint topLeft = corners[2]; // Which point didn't we find in relation to the "L" sides? that's the top right corner ResultPoint topRight; if (!pointCount.ContainsKey(pointA)) { topRight = pointA; } else if (!pointCount.ContainsKey(pointB)) { topRight = pointB; } else if (!pointCount.ContainsKey(pointC)) { topRight = pointC; } else { topRight = pointD; } // Next determine the dimension by tracing along the top or right side and counting black/white // transitions. Since we start inside a black module, we should see a number of transitions // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to // end on a black module: // The top right point is actually the corner of a module, which is one of the two black modules // adjacent to the white module at the top right. Tracing to that corner from either the top left // or bottom right should work here. The number of transitions could be higher than it should be // due to noise. So we try both and take the min. int dimension = System.Math.Min(transitionsBetween(topLeft, topRight).Transitions, transitionsBetween(bottomRight, topRight).Transitions); if ((dimension & 0x01) == 1) { // it can't be odd, so, round... up? dimension++; } dimension += 2; BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension); return(new DetectorResult(bits, new ResultPoint[] { pointA, pointB, pointC, pointD })); }
/// <summary> /// <p>Estimates module size based on two finder patterns -- it uses /// <seealso cref="#sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)"/> to figure the /// width of each, measuring along the axis between their centers.</p> /// </summary> private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) { float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.X, (int) pattern.Y, (int) otherPattern.X, (int) otherPattern.Y); float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.X, (int) otherPattern.Y, (int) pattern.X, (int) pattern.Y); if (float.IsNaN(moduleSizeEst1)) { return moduleSizeEst2 / 7.0f; } if (float.IsNaN(moduleSizeEst2)) { return moduleSizeEst1 / 7.0f; } // Average them, and divide by 7 since we've counted the width of 3 black modules, // and 1 white and 1 black module on either side. Ergo, divide sum by 14. return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; }
/// <summary> <p>Computes an average estimated module size based on estimated derived from the positions /// of the three finder patterns.</p> /// </summary> protected internal virtual float calculateModuleSize(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft) { // Take the average return((calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f); }
/// <summary> /// Because we scan horizontally to detect the start and stop patterns, the vertical component of /// the codeword coordinates will be slightly wrong if there is any skew or rotation in the image. /// This method moves those points back onto the edges of the theoretically perfect bounding /// quadrilateral if needed. /// </summary> /// <param name="vertices"> The eight vertices located by findVertices(). </param> private static void correctCodeWordVertices(ResultPoint[] vertices, bool upsideDown) { float v0x = vertices[0].X; float v0y = vertices[0].Y; float v2x = vertices[2].X; float v2y = vertices[2].Y; float v4x = vertices[4].X; float v4y = vertices[4].Y; float v6x = vertices[6].X; float v6y = vertices[6].Y; float skew = v4y - v6y; if (upsideDown) { skew = -skew; } if (skew > SKEW_THRESHOLD) { // Fix v4 float deltax = v6x - v0x; float deltay = v6y - v0y; float delta2 = deltax * deltax + deltay * deltay; float correction = (v4x - v0x) * deltax / delta2; vertices[4] = new ResultPoint(v0x + correction * deltax, v0y + correction * deltay); } else if (-skew > SKEW_THRESHOLD) { // Fix v6 float deltax = v2x - v4x; float deltay = v2y - v4y; float delta2 = deltax * deltax + deltay * deltay; float correction = (v2x - v6x) * deltax / delta2; vertices[6] = new ResultPoint(v2x - correction * deltax, v2y - correction * deltay); } float v1x = vertices[1].X; float v1y = vertices[1].Y; float v3x = vertices[3].X; float v3y = vertices[3].Y; float v5x = vertices[5].X; float v5y = vertices[5].Y; float v7x = vertices[7].X; float v7y = vertices[7].Y; skew = v7y - v5y; if (upsideDown) { skew = -skew; } if (skew > SKEW_THRESHOLD) { // Fix v5 float deltax = v7x - v1x; float deltay = v7y - v1y; float delta2 = deltax * deltax + deltay * deltay; float correction = (v5x - v1x) * deltax / delta2; vertices[5] = new ResultPoint(v1x + correction * deltax, v1y + correction * deltay); } else if (-skew > SKEW_THRESHOLD) { // Fix v7 float deltax = v3x - v5x; float deltay = v3y - v5y; float delta2 = deltax * deltax + deltay * deltay; float correction = (v3x - v7x) * deltax / delta2; vertices[7] = new ResultPoint(v3x - correction * deltax, v3y - correction * deltay); } }
/// <summary> /// Locate the vertices and the codewords area of a black blob using the Start /// and Stop patterns as locators. This assumes that the image is rotated 180 /// degrees and if it locates the start and stop patterns at it will re-map /// the vertices for a 0 degree rotation. /// TODO: Change assumption about barcode location. /// </summary> /// <param name="matrix"> the scanned barcode image. </param> /// <returns> an array containing the vertices: /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area </returns> private static ResultPoint[] findVertices180(BitMatrix matrix, bool tryHarder) { int height = matrix.Height; int width = matrix.Width; int halfWidth = width >> 1; ResultPoint[] result = new ResultPoint[8]; bool found = false; int[] counters = new int[START_PATTERN_REVERSE.Length]; int rowStep = Math.Max(1, height >> (tryHarder ? 9 : 7)); // Top Left for (int i = height - 1; i > 0; i -= rowStep) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE, counters); if (loc != null) { result[0] = new ResultPoint(loc[1], i); result[4] = new ResultPoint(loc[0], i); found = true; break; } } // Bottom Left if (found) // Found the Top Left vertex { found = false; for (int i = 0; i < height; i += rowStep) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE, counters); if (loc != null) { result[1] = new ResultPoint(loc[1], i); result[5] = new ResultPoint(loc[0], i); found = true; break; } } } counters = new int[STOP_PATTERN_REVERSE.Length]; // Top Right if (found) // Found the Bottom Left vertex { found = false; for (int i = height - 1; i > 0; i -= rowStep) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE, counters); if (loc != null) { result[2] = new ResultPoint(loc[0], i); result[6] = new ResultPoint(loc[1], i); found = true; break; } } } // Bottom Right if (found) // Found the Top Right vertex { found = false; for (int i = 0; i < height; i += rowStep) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE, counters); if (loc != null) { result[3] = new ResultPoint(loc[0], i); result[7] = new ResultPoint(loc[1], i); found = true; break; } } } return found ? result : null; }
/// <summary> /// Computes the y dimension (number of modules in a column) of the PDF417 Code /// based on vertices of the codeword area and estimated module size. /// </summary> /// <param name="topLeft"> of codeword area </param> /// <param name="topRight"> of codeword area </param> /// <param name="bottomLeft"> of codeword area </param> /// <param name="bottomRight"> of codeword are </param> /// <param name="moduleWidth"> estimated module size </param> /// <returns> the number of modules in a row. </returns> private static int computeYDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) { int leftColumnDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleWidth); int rightColumnDimension = MathUtils.round(ResultPoint.distance(topRight, bottomRight) / moduleWidth); return (leftColumnDimension + rightColumnDimension) >> 1; }
/// <summary> /// Computes the dimension (number of modules in a row) of the PDF417 Code /// based on vertices of the codeword area and estimated module size. /// </summary> /// <param name="topLeft"> of codeword area </param> /// <param name="topRight"> of codeword area </param> /// <param name="bottomLeft"> of codeword area </param> /// <param name="bottomRight"> of codeword are </param> /// <param name="moduleWidth"> estimated module size </param> /// <returns> the number of modules in a row. </returns> private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) { int topRowDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleWidth); int bottomRowDimension = MathUtils.round(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth); return ((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17; }
internal ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions) { this.from = from; this.to = to; this.transitions = transitions; }
public virtual PerspectiveTransform createTransform(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension) { //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'" float dimMinusThree = (float) dimension - 3.5f; float bottomRightX; float bottomRightY; float sourceBottomRightX; float sourceBottomRightY; if (alignmentPattern != null) { bottomRightX = alignmentPattern.X; bottomRightY = alignmentPattern.Y; sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f; } else { // Don't have an alignment pattern, just make up the bottom-right point bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X; bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y; sourceBottomRightX = sourceBottomRightY = dimMinusThree; } 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); return transform; }
/// <summary> Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.</summary> private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to) { // See QR Code Detector, sizeOfBlackWhiteBlackRun() //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'" int fromX = (int) from.X; //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'" int fromY = (int) from.Y; //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'" int toX = (int) to.X; //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'" int toY = (int) to.Y; bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX); if (steep) { int temp = fromX; fromX = fromY; fromY = temp; temp = toX; toX = toY; toY = temp; } int dx = System.Math.Abs(toX - fromX); int dy = System.Math.Abs(toY - fromY); int error = - dx >> 1; int ystep = fromY < toY?1:- 1; int xstep = fromX < toX?1:- 1; int transitions = 0; bool inBlack = image.get_Renamed(steep?fromY:fromX, steep?fromX:fromY); for (int x = fromX, y = fromY; x != toX; x += xstep) { bool isBlack = image.get_Renamed(steep?y:x, steep?x:y); if (isBlack != inBlack) { transitions++; inBlack = isBlack; } error += dy; if (error > 0) { if (y == toY) { break; } y += ystep; error -= dx; } } return new ResultPointsAndTransitions(from, to, transitions); }
/// <summary> /// <p>Computes the dimension (number of modules on a size) of the QR Code based on the position /// of the finder patterns and estimated module size.</p> /// </summary> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private static int computeDimension(com.google.zxing.ResultPoint topLeft, com.google.zxing.ResultPoint topRight, com.google.zxing.ResultPoint bottomLeft, float moduleSize) throws com.google.zxing.NotFoundException private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize) { int tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize); int tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; switch (dimension & 0x03) // mod 4 { case 0: dimension++; break; // 1? do nothing case 2: dimension--; break; case 3: throw NotFoundException.NotFoundInstance; } return dimension; }
private static BitMatrix sampleGrid(BitMatrix image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension) { // We make up the top right point for now, based on the others. // TODO: we actually found a fourth corner above and figured out which of two modules // it was the corner of. We could use that here and adjust for perspective distortion. float topRightX = (bottomRight.X - bottomLeft.X) + topLeft.X; float topRightY = (bottomRight.Y - bottomLeft.Y) + topLeft.Y; // Note that unlike in the QR Code sampler, we didn't find the center of modules, but the // very corners. So there is no 0.5f here; 0.0f is right. GridSampler sampler = GridSampler.Instance; 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); }
/// <summary> <p>Detects a Data Matrix Code in an image.</p> /// /// </summary> /// <returns> {@link DetectorResult} encapsulating results of detecting a QR Code /// </returns> /// <throws> ReaderException if no Data Matrix Code can be found </throws> public DetectorResult detect() { ResultPoint[] cornerPoints = rectangleDetector.detect(); ResultPoint pointA = cornerPoints[0]; ResultPoint pointB = cornerPoints[1]; ResultPoint pointC = cornerPoints[2]; ResultPoint pointD = cornerPoints[3]; // Point A and D are across the diagonal from one another, // as are B and C. Figure out which are the solid black lines // by counting transitions System.Collections.ArrayList transitions = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(4)); transitions.Add(transitionsBetween(pointA, pointB)); transitions.Add(transitionsBetween(pointA, pointC)); transitions.Add(transitionsBetween(pointB, pointD)); transitions.Add(transitionsBetween(pointC, pointD)); Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator()); // Sort by number of transitions. First two will be the two solid sides; last two // will be the two alternating black/white sides ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions) transitions[0]; ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions) transitions[1]; // Figure out which point is their intersection by tallying up the number of times we see the // endpoints in the four endpoints. One will show up twice. System.Collections.Hashtable pointCount = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); increment(pointCount, lSideOne.From); increment(pointCount, lSideOne.To); increment(pointCount, lSideTwo.From); increment(pointCount, lSideTwo.To); ResultPoint maybeTopLeft = null; ResultPoint bottomLeft = null; ResultPoint maybeBottomRight = null; System.Collections.IEnumerator points = pointCount.Keys.GetEnumerator(); //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'" while (points.MoveNext()) { //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'" ResultPoint point = (ResultPoint) points.Current; System.Int32 value_Renamed = (System.Int32) pointCount[point]; if (value_Renamed == 2) { bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides } else { // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now if (maybeTopLeft == null) { maybeTopLeft = point; } else { maybeBottomRight = point; } } } if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) { throw ReaderException.Instance; } // Bottom left is correct but top left and bottom right might be switched ResultPoint[] corners = new ResultPoint[]{maybeTopLeft, bottomLeft, maybeBottomRight}; // Use the dot product trick to sort them out ResultPoint.orderBestPatterns(corners); // Now we know which is which: ResultPoint bottomRight = corners[0]; bottomLeft = corners[1]; ResultPoint topLeft = corners[2]; // Which point didn't we find in relation to the "L" sides? that's the top right corner ResultPoint topRight; if (!pointCount.ContainsKey(pointA)) { topRight = pointA; } else if (!pointCount.ContainsKey(pointB)) { topRight = pointB; } else if (!pointCount.ContainsKey(pointC)) { topRight = pointC; } else { topRight = pointD; } // Next determine the dimension by tracing along the top or right side and counting black/white // transitions. Since we start inside a black module, we should see a number of transitions // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to // end on a black module: // The top right point is actually the corner of a module, which is one of the two black modules // adjacent to the white module at the top right. Tracing to that corner from either the top left // or bottom right should work here. The number of transitions could be higher than it should be // due to noise. So we try both and take the min. int dimension = System.Math.Min(transitionsBetween(topLeft, topRight).Transitions, transitionsBetween(bottomRight, topRight).Transitions); if ((dimension & 0x01) == 1) { // it can't be odd, so, round... up? dimension++; } dimension += 2; BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension); return new DetectorResult(bits, new ResultPoint[]{pointA, pointB, pointC, pointD}); }
public DetectorResult(BitMatrix bits, ResultPoint[] points) { this.bits = bits; this.points = points; }
internal ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions) { this.from = from; this.to = to; this.transitions = transitions; }
protected internal virtual DetectorResult processFinderPatternInfo(FinderPatternInfo info) { FinderPattern topLeft = info.TopLeft; FinderPattern topRight = info.TopRight; FinderPattern bottomLeft = info.BottomLeft; float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); if (moduleSize < 1.0f) { throw ReaderException.Instance; } int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension); int modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7; AlignmentPattern alignmentPattern = null; // Anything above version 1 has an alignment pattern if (provisionalVersion.AlignmentPatternCenters.Length > 0) { // Guess where a "bottom right" finder pattern would have been float bottomRightX = topRight.X - topLeft.X + bottomLeft.X; float bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y; // Estimate that alignment pattern is closer by 3 modules // from "bottom right" to known top left location //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'" float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters; //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'" int estAlignmentX = (int)(topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X)); //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'" int estAlignmentY = (int)(topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y)); // Kind of arbitrary -- expand search radius before giving up for (int i = 4; i <= 16; i <<= 1) { try { //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'" alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i); break; } catch (ReaderException re) { // try next round } } // If we didn't find alignment pattern... well try anyway without it } PerspectiveTransform transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); BitMatrix bits = sampleGrid(image, transform, dimension); ResultPoint[] points; if (alignmentPattern == null) { points = new ResultPoint[] { bottomLeft, topLeft, topRight }; } else { points = new ResultPoint[] { bottomLeft, topLeft, topRight, alignmentPattern }; } return(new DetectorResult(bits, points)); }
/// <summary> We're going to examine rows from the middle outward, searching alternately above and below the /// middle, and farther out each time. rowStep is the number of rows between each successive /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then /// middle + rowStep, then middle - (2 * rowStep), etc. /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the /// image if "trying harder". /// /// </summary> /// <param name="image">The image to decode /// </param> /// <param name="hints">Any hints that were requested /// </param> /// <returns> The contents of the decoded barcode /// </returns> /// <throws> ReaderException Any spontaneous errors which occur </throws> private Result doDecode(BinaryBitmap image, Dictionary <object, object> hints) { int width = image.Width; int height = image.Height; BitArray row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = System.Math.Max(1, height >> (tryHarder?7:4)); int maxLines; if (tryHarder) { maxLines = height; // Look at the whole image, not just the center } else { maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image } for (int x = 0; x < maxLines; x++) { // Scanning from the middle out. Determine which row we're looking at next: int rowStepsAboveOrBelow = (x + 1) >> 1; bool isAbove = (x & 0x01) == 0; // i.e. is x even? int rowNumber = middle + rowStep * (isAbove?rowStepsAboveOrBelow:-rowStepsAboveOrBelow); if (rowNumber < 0 || rowNumber >= height) { // Oops, if we run off the top or bottom, stop break; } // Estimate black point for this row and load it: try { row = image.getBlackRow(rowNumber, row); } catch (ReaderException) { continue; } // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to // handle decoding upside down barcodes. for (int attempt = 0; attempt < 2; attempt++) { if (attempt == 1) { // trying again? row.reverse(); // reverse the row and continue // This means we will only ever draw result points *once* in the life of this method // since we want to avoid drawing the wrong points after flipping the row, and, // don't want to clutter with noise from every single row scan -- just the scans // that start on the center line. if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { Dictionary <object, object> newHints = new Dictionary <object, object>(); System.Collections.IEnumerator hintEnum = hints.Keys.GetEnumerator(); //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'" while (hintEnum.MoveNext()) { //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'" System.Object key = hintEnum.Current; if (!key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { newHints[key] = hints[key]; } } hints = newHints; } } try { // Look for a barcode Result result = decodeRow(rowNumber, row, hints); // We found our barcode if (attempt == 1) { // But it was upside down, so note that result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.ResultPoints; points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y); points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y); } return(result); } catch (ReaderException) { // continue -- just couldn't decode this row } } } throw ReaderException.Instance; }
// Note that we don't try rotation without the try harder flag, even if rotation was supported. //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: public com.google.zxing.Result decode(com.google.zxing.BinaryBitmap image, java.util.Map<com.google.zxing.DecodeHintType,?> hints) throws com.google.zxing.NotFoundException, com.google.zxing.FormatException public virtual Result decode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints) { try { return doDecode(image, hints); } catch (NotFoundException nfe) { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); if (tryHarder && image.RotateSupported) { BinaryBitmap rotatedImage = image.rotateCounterClockwise(); Result result = doDecode(rotatedImage, hints); // Record that we found it rotated 90 degrees CCW / 270 degrees CW //JAVA TO C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET: //ORIGINAL LINE: java.util.Map<com.google.zxing.ResultMetadataType,?> metadata = result.getResultMetadata(); IDictionary<ResultMetadataType, object> metadata = result.ResultMetadata; int orientation = 270; if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION)) { // But if we found it reversed in doDecode(), add in that result here: orientation = (orientation + (int) metadata[ResultMetadataType.ORIENTATION]) % 360; } result.putMetadata(ResultMetadataType.ORIENTATION, orientation); // Update result points ResultPoint[] points = result.ResultPoints; if (points != null) { int height = rotatedImage.Height; for (int i = 0; i < points.Length; i++) { points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X); } } return result; } else { throw nfe; } } }
// public FinderPatternInfo[] findMulti(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) public FinderPatternInfo[] findMulti(System.Collections.Generic.Dictionary <Object, Object> hints) // added by .net follower (http://dotnetfollower.com) { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); BitMatrix image = Image; int maxI = image.Height; int maxJ = image.Width; // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. //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'" int iSkip = (int)(maxI / (MAX_MODULES * 4.0f) * 3); if (iSkip < MIN_SKIP || tryHarder) { iSkip = MIN_SKIP; } int[] stateCount = new int[5]; for (int i = iSkip - 1; i < maxI; i += iSkip) { // Get a row of black/white values stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; int currentState = 0; for (int j = 0; j < maxJ; j++) { if (image.get_Renamed(j, i)) { // Black pixel if ((currentState & 1) == 1) { // Counting white pixels currentState++; } stateCount[currentState]++; } else { // White pixel if ((currentState & 1) == 0) { // Counting black pixels if (currentState == 4) { // A winner? if (foundPatternCross(stateCount)) { // Yes bool confirmed = handlePossibleCenter(stateCount, i, j); if (!confirmed) { do { // Advance to next black pixel j++; }while (j < maxJ && !image.get_Renamed(j, i)); j--; // back up to that last white pixel } // Clear state to start looking again currentState = 0; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; } else { // No, shift counts back by two stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; } } else { stateCount[++currentState]++; } } else { // Counting white pixels stateCount[currentState]++; } } } // for j=... if (foundPatternCross(stateCount)) { handlePossibleCenter(stateCount, i, maxJ); } // end if foundPatternCross } // for i=iSkip-1 ... FinderPattern[][] patternInfo = selectBestPatterns(); // System.Collections.ArrayList result = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) System.Collections.Generic.List <Object> result = new System.Collections.Generic.List <Object>(10); // added by .net follower (http://dotnetfollower.com) for (int i = 0; i < patternInfo.Length; i++) { FinderPattern[] pattern = patternInfo[i]; ResultPoint.orderBestPatterns(pattern); result.Add(new FinderPatternInfo(pattern)); } if ((result.Count == 0)) { return(EMPTY_RESULT_ARRAY); } else { FinderPatternInfo[] resultArray = new FinderPatternInfo[result.Count]; for (int i = 0; i < result.Count; i++) { resultArray[i] = (FinderPatternInfo)result[i]; } return(resultArray); } }
/// <summary> Locate the vertices and the codewords area of a black blob using the Start /// and Stop patterns as locators. This assumes that the image is rotated 180 /// degrees and if it locates the start and stop patterns at it will re-map /// the vertices for a 0 degree rotation. /// TODO: Change assumption about barcode location. /// TODO: Scanning every row is very expensive. We should only do this for TRY_HARDER. /// /// </summary> /// <param name="matrix">the scanned barcode image. /// </param> /// <returns> an array containing the vertices: /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area /// </returns> private static ResultPoint[] findVertices180(BitMatrix matrix) { int height = matrix.Height; int width = matrix.Width; int halfWidth = width >> 1; ResultPoint[] result = new ResultPoint[8]; bool found = false; // Top Left for (int i = height - 1; i > 0; i--) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); if (loc != null) { result[0] = new ResultPoint(loc[1], i); result[4] = new ResultPoint(loc[0], i); found = true; break; } } // Bottom Left if (found) { // Found the Top Left vertex found = false; for (int i = 0; i < height; i++) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); if (loc != null) { result[1] = new ResultPoint(loc[1], i); result[5] = new ResultPoint(loc[0], i); found = true; break; } } } // Top Right if (found) { // Found the Bottom Left vertex found = false; for (int i = height - 1; i > 0; i--) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); if (loc != null) { result[2] = new ResultPoint(loc[0], i); result[6] = new ResultPoint(loc[1], i); found = true; break; } } } // Bottom Right if (found) { // Found the Top Right vertex found = false; for (int i = 0; i < height; i++) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); if (loc != null) { result[3] = new ResultPoint(loc[0], i); result[7] = new ResultPoint(loc[1], i); found = true; break; } } } return found?result:null; }
private void doDecodeMultiple(BinaryBitmap image, System.Collections.Hashtable hints, System.Collections.ArrayList results, int xOffset, int yOffset) { Result result; try { result = delegate_Renamed.decode(image, hints); } catch (ReaderException) { return; } bool alreadyFound = false; for (int i = 0; i < results.Count; i++) { Result existingResult = (Result)results[i]; if (existingResult.Text.Equals(result.Text)) { alreadyFound = true; break; } } if (alreadyFound) { return; } results.Add(translateResultPoints(result, xOffset, yOffset)); ResultPoint[] resultPoints = result.ResultPoints; if (resultPoints == null || resultPoints.Length == 0) { return; } int width = image.Width; int height = image.Height; float minX = width; float minY = height; float maxX = 0.0f; float maxY = 0.0f; for (int i = 0; i < resultPoints.Length; i++) { ResultPoint point = resultPoints[i]; float x = point.X; float y = point.Y; if (x < minX) { minX = x; } if (y < minY) { minY = y; } if (x > maxX) { maxX = x; } if (y > maxY) { maxY = y; } } // Decode left of barcode if (minX > MIN_DIMENSION_TO_RECUR) { //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'" doDecodeMultiple(image.crop(0, 0, (int)minX, height), hints, results, xOffset, yOffset); } // Decode above barcode if (minY > MIN_DIMENSION_TO_RECUR) { //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'" doDecodeMultiple(image.crop(0, 0, width, (int)minY), hints, results, xOffset, yOffset); } // Decode right of barcode if (maxX < width - MIN_DIMENSION_TO_RECUR) { //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'" doDecodeMultiple(image.crop((int)maxX, 0, width - (int)maxX, height), hints, results, xOffset + (int)maxX, yOffset); } // Decode below barcode if (maxY < height - MIN_DIMENSION_TO_RECUR) { //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'" doDecodeMultiple(image.crop(0, (int)maxY, width, height - (int)maxY), hints, results, xOffset, yOffset + (int)maxY); } }
/// <summary> Because we scan horizontally to detect the start and stop patterns, the vertical component of /// the codeword coordinates will be slightly wrong if there is any skew or rotation in the image. /// This method moves those points back onto the edges of the theoretically perfect bounding /// quadrilateral if needed. /// /// </summary> /// <param name="vertices">The eight vertices located by findVertices(). /// </param> private static void correctCodeWordVertices(ResultPoint[] vertices, bool upsideDown) { float skew = vertices[4].Y - vertices[6].Y; if (upsideDown) { skew = - skew; } if (skew > SKEW_THRESHOLD) { // Fix v4 float length = vertices[4].X - vertices[0].X; float deltax = vertices[6].X - vertices[0].X; float deltay = vertices[6].Y - vertices[0].Y; float correction = length * deltay / deltax; vertices[4] = new ResultPoint(vertices[4].X, vertices[4].Y + correction); } else if (- skew > SKEW_THRESHOLD) { // Fix v6 float length = vertices[2].X - vertices[6].X; float deltax = vertices[2].X - vertices[4].X; float deltay = vertices[2].Y - vertices[4].Y; float correction = length * deltay / deltax; vertices[6] = new ResultPoint(vertices[6].X, vertices[6].Y - correction); } skew = vertices[7].Y - vertices[5].Y; if (upsideDown) { skew = - skew; } if (skew > SKEW_THRESHOLD) { // Fix v5 float length = vertices[5].X - vertices[1].X; float deltax = vertices[7].X - vertices[1].X; float deltay = vertices[7].Y - vertices[1].Y; float correction = length * deltay / deltax; vertices[5] = new ResultPoint(vertices[5].X, vertices[5].Y + correction); } else if (- skew > SKEW_THRESHOLD) { // Fix v7 float length = vertices[3].X - vertices[7].X; float deltax = vertices[3].X - vertices[5].X; float deltay = vertices[3].Y - vertices[5].Y; float correction = length * deltay / deltax; vertices[7] = new ResultPoint(vertices[7].X, vertices[7].Y - correction); } }
/// <summary> <p>Computes an average estimated module size based on estimated derived from the positions /// of the three finder patterns.</p> /// </summary> protected internal virtual float calculateModuleSize(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft) { // Take the average return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; }
/// <summary> <p>Estimates module size (pixels in a module) based on the Start and End /// finder patterns.</p> /// /// </summary> /// <param name="vertices">an array of vertices: /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area /// </param> /// <returns> the module size. /// </returns> private static float computeModuleWidth(ResultPoint[] vertices) { float pixels1 = ResultPoint.distance(vertices[0], vertices[4]); float pixels2 = ResultPoint.distance(vertices[1], vertices[5]); float moduleWidth1 = (pixels1 + pixels2) / (17 * 2.0f); float pixels3 = ResultPoint.distance(vertices[6], vertices[2]); float pixels4 = ResultPoint.distance(vertices[7], vertices[3]); float moduleWidth2 = (pixels3 + pixels4) / (18 * 2.0f); return (moduleWidth1 + moduleWidth2) / 2.0f; }
/// <summary> <p>Estimates module size based on two finder patterns -- it uses /// {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the /// width of each, measuring along the axis between their centers.</p> /// </summary> private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) { //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'" float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.X, (int) pattern.Y, (int) otherPattern.X, (int) otherPattern.Y); //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'" float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.X, (int) otherPattern.Y, (int) pattern.X, (int) pattern.Y); if (System.Single.IsNaN(moduleSizeEst1)) { return moduleSizeEst2 / 7.0f; } if (System.Single.IsNaN(moduleSizeEst2)) { return moduleSizeEst1 / 7.0f; } // Average them, and divide by 7 since we've counted the width of 3 black modules, // and 1 white and 1 black module on either side. Ergo, divide sum by 14. return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; }
/// <summary> Computes the dimension (number of modules in a row) of the PDF417 Code /// based on vertices of the codeword area and estimated module size. /// /// </summary> /// <param name="topLeft"> of codeword area /// </param> /// <param name="topRight"> of codeword area /// </param> /// <param name="bottomLeft"> of codeword area /// </param> /// <param name="bottomRight">of codeword are /// </param> /// <param name="moduleWidth">estimated module size /// </param> /// <returns> the number of modules in a row. /// </returns> private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) { int topRowDimension = round(ResultPoint.distance(topLeft, topRight) / moduleWidth); int bottomRowDimension = round(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth); return ((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17; /* * int topRowDimension = round(ResultPoint.distance(topLeft, * topRight)); //moduleWidth); int bottomRowDimension = * round(ResultPoint.distance(bottomLeft, bottomRight)); // * moduleWidth); int dimension = ((topRowDimension + bottomRowDimension) * >> 1); // Round up to nearest 17 modules i.e. there are 17 modules per * codeword //int dimension = ((((topRowDimension + bottomRowDimension) >> * 1) + 8) / 17) * 17; return dimension; */ }
private static Result translateResultPoints(Result result, int xOffset, int yOffset) { ResultPoint[] oldResultPoints = result.ResultPoints; ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.Length]; for (int i = 0; i < oldResultPoints.Length; i++) { ResultPoint oldPoint = oldResultPoints[i]; newResultPoints[i] = new ResultPoint(oldPoint.X + xOffset, oldPoint.Y + yOffset); } return new Result(result.Text, result.RawBytes, newResultPoints, result.BarcodeFormat); }
private static BitMatrix sampleGrid(BitMatrix matrix, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint topRight, ResultPoint bottomRight, int dimension) { // Note that unlike the QR Code sampler, we didn't find the center of modules, but the // very corners. So there is no 0.5f here; 0.0f is right. GridSampler sampler = GridSampler.Instance; return sampler.sampleGrid(matrix, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y); // p4FromY }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: protected final com.google.zxing.common.DetectorResult processFinderPatternInfo(FinderPatternInfo info) throws com.google.zxing.NotFoundException, com.google.zxing.FormatException protected internal DetectorResult processFinderPatternInfo(FinderPatternInfo info) { FinderPattern topLeft = info.TopLeft; FinderPattern topRight = info.TopRight; FinderPattern bottomLeft = info.BottomLeft; float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); if (moduleSize < 1.0f) { throw NotFoundException.NotFoundInstance; } int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension); int modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7; AlignmentPattern alignmentPattern = null; // Anything above version 1 has an alignment pattern if (provisionalVersion.AlignmentPatternCenters.Length > 0) { // Guess where a "bottom right" finder pattern would have been float bottomRightX = topRight.X - topLeft.X + bottomLeft.X; float bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y; // Estimate that alignment pattern is closer by 3 modules // from "bottom right" to known top left location float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters; int estAlignmentX = (int)(topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X)); int estAlignmentY = (int)(topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y)); // Kind of arbitrary -- expand search radius before giving up for (int i = 4; i <= 16; i <<= 1) { try { alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float) i); break; } catch (NotFoundException re) { // try next round } } // If we didn't find alignment pattern... well try anyway without it } PerspectiveTransform transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); BitMatrix bits = sampleGrid(image, transform, dimension); ResultPoint[] points; if (alignmentPattern == null) { points = new ResultPoint[]{bottomLeft, topLeft, topRight}; } else { points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern}; } return new DetectorResult(bits, points); }
/// <summary> Increments the Integer associated with a key by one.</summary> private static void increment(System.Collections.Hashtable table, ResultPoint key) { //System.Int32 value_Renamed = (System.Int32) table[key]; ////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'" //table[key] = value_Renamed == null?INTEGERS[1]:INTEGERS[value_Renamed + 1]; // Redivivus.in Java to c# Porting update // 30/01/2010 // Added // START System.Int32 value_Renamed = 0; try { if (table.Count > 0) value_Renamed = (System.Int32)table[key]; } catch { value_Renamed = 0; } table[key] = value_Renamed == 0 ? INTEGERS[1] : INTEGERS[value_Renamed + 1]; //END }
/// <summary> We're going to examine rows from the middle outward, searching alternately above and below the /// middle, and farther out each time. rowStep is the number of rows between each successive /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then /// middle + rowStep, then middle - (2 * rowStep), etc. /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the /// image if "trying harder". /// /// </summary> /// <param name="image">The image to decode /// </param> /// <param name="hints">Any hints that were requested /// </param> /// <returns> The contents of the decoded barcode /// </returns> /// <throws> ReaderException Any spontaneous errors which occur </throws> private Result doDecode(BinaryBitmap image, System.Collections.Hashtable hints) { int width = image.Width; int height = image.Height; BitArray row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = System.Math.Max(1, height >> (tryHarder?7:4)); int maxLines; if (tryHarder) { maxLines = height; // Look at the whole image, not just the center } else { maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image } for (int x = 0; x < maxLines; x++) { // Scanning from the middle out. Determine which row we're looking at next: int rowStepsAboveOrBelow = (x + 1) >> 1; bool isAbove = (x & 0x01) == 0; // i.e. is x even? int rowNumber = middle + rowStep * (isAbove?rowStepsAboveOrBelow:- rowStepsAboveOrBelow); if (rowNumber < 0 || rowNumber >= height) { // Oops, if we run off the top or bottom, stop break; } // Estimate black point for this row and load it: try { row = image.getBlackRow(rowNumber, row); } catch (ReaderException) { continue; } // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to // handle decoding upside down barcodes. for (int attempt = 0; attempt < 2; attempt++) { if (attempt == 1) { // trying again? row.reverse(); // reverse the row and continue // This means we will only ever draw result points *once* in the life of this method // since we want to avoid drawing the wrong points after flipping the row, and, // don't want to clutter with noise from every single row scan -- just the scans // that start on the center line. if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { System.Collections.Hashtable newHints = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); // Can't use clone() in J2ME System.Collections.IEnumerator hintEnum = hints.Keys.GetEnumerator(); //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'" while (hintEnum.MoveNext()) { //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'" System.Object key = hintEnum.Current; if (!key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { newHints[key] = hints[key]; } } hints = newHints; } } try { // Look for a barcode Result result = decodeRow(rowNumber, row, hints); // We found our barcode if (attempt == 1) { // But it was upside down, so note that result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.ResultPoints; points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y); points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y); } return result; } catch (ReaderException) { // continue -- just couldn't decode this row } } } throw ReaderException.Instance; }
/// <summary> Locate the vertices and the codewords area of a black blob using the Start /// and Stop patterns as locators. This assumes that the image is rotated 180 /// degrees and if it locates the start and stop patterns at it will re-map /// the vertices for a 0 degree rotation. /// TODO: Change assumption about barcode location. /// TODO: Scanning every row is very expensive. We should only do this for TRY_HARDER. /// /// </summary> /// <param name="matrix">the scanned barcode image. /// </param> /// <returns> an array containing the vertices: /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area /// </returns> private static ResultPoint[] findVertices180(BitMatrix matrix) { int height = matrix.Height; int width = matrix.Width; int halfWidth = width >> 1; ResultPoint[] result = new ResultPoint[8]; bool found = false; // Top Left for (int i = height - 1; i > 0; i--) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); if (loc != null) { result[0] = new ResultPoint(loc[1], i); result[4] = new ResultPoint(loc[0], i); found = true; break; } } // Bottom Left if (found) { // Found the Top Left vertex found = false; for (int i = 0; i < height; i++) { int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); if (loc != null) { result[1] = new ResultPoint(loc[1], i); result[5] = new ResultPoint(loc[0], i); found = true; break; } } } // Top Right if (found) { // Found the Bottom Left vertex found = false; for (int i = height - 1; i > 0; i--) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); if (loc != null) { result[2] = new ResultPoint(loc[0], i); result[6] = new ResultPoint(loc[1], i); found = true; break; } } } // Bottom Right if (found) { // Found the Top Right vertex found = false; for (int i = 0; i < height; i++) { int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); if (loc != null) { result[3] = new ResultPoint(loc[0], i); result[7] = new ResultPoint(loc[1], i); found = true; break; } } } return(found?result:null); }
internal virtual FinderPatternInfo find(System.Collections.Hashtable hints) { bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int maxI = image.Height; int maxJ = image.Width; // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. int iSkip = (3 * maxI) / (4 * MAX_MODULES); if (iSkip < MIN_SKIP || tryHarder) { iSkip = MIN_SKIP; } bool done = false; int[] stateCount = new int[5]; for (int i = iSkip - 1; i < maxI && !done; i += iSkip) { // Get a row of black/white values stateCount[0] = 0; // black stateCount[1] = 0; // white stateCount[2] = 0; // black stateCount[3] = 0; // white stateCount[4] = 0; // black int currentState = 0; for (int j = 0; j < maxJ; j++) { if (image.get_Renamed(j, i)) { // Black pixel if ((currentState % 2) == 1) // if currentState is White (i.e. 1 or 3) { // Counting white pixels currentState++; } stateCount[currentState]++; } else { // White pixel if ((currentState % 2) == 0) { // Counting black pixels if (currentState == 4) { // A winner? if (foundPatternCross(stateCount)) { // Yes bool confirmed = handlePossibleCenter(stateCount, i, j); if (confirmed) { // Start examining every other line. Checking each line turned out to be too // expensive and didn't improve performance. iSkip = 2; if (hasSkipped) { done = haveMultiplyConfirmedCenters(); } else { int rowSkip = findRowSkip(); if (rowSkip > stateCount[2]) { // Skip rows between row of lower confirmed center // and top of presumed third confirmed center // but back up a bit to get a full chance of detecting // it, entire width of center of finder pattern // Skip by rowSkip, but back off by stateCount[2] (size of last center // of pattern we saw) to be conservative, and also back off by iSkip which // is about to be re-added i += rowSkip - stateCount[2] - iSkip; j = maxJ - 1; } } } else { // Advance to next black pixel do { j++; }while (j < maxJ && !image.get_Renamed(j, i)); j--; // back up to that last white pixel } // Clear state to start looking again currentState = 0; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; } else { // No, shift counts back by two stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; } } else { stateCount[++currentState]++; } } else { // Counting white pixels stateCount[currentState]++; } } } if (foundPatternCross(stateCount)) { bool confirmed = handlePossibleCenter(stateCount, i, maxJ); if (confirmed) { iSkip = stateCount[0]; if (hasSkipped) { // Found a third one done = haveMultiplyConfirmedCenters(); } } } } FinderPattern[] patternInfo = selectBestPatterns(); if (patternInfo.IsBlank()) { return(null); } ResultPoint.orderBestPatterns(patternInfo); return(new FinderPatternInfo(patternInfo)); }
/// <summary> /// We're going to examine rows from the middle outward, searching alternately above and below the /// middle, and farther out each time. rowStep is the number of rows between each successive /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then /// middle + rowStep, then middle - (2 * rowStep), etc. /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the /// image if "trying harder". /// </summary> /// <param name="image"> The image to decode </param> /// <param name="hints"> Any hints that were requested </param> /// <returns> The contents of the decoded barcode </returns> /// <exception cref="NotFoundException"> Any spontaneous errors which occur </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private com.google.zxing.Result doDecode(com.google.zxing.BinaryBitmap image, java.util.Map<com.google.zxing.DecodeHintType,?> hints) throws com.google.zxing.NotFoundException private Result doDecode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints) { int width = image.Width; int height = image.Height; BitArray row = new BitArray(width); int middle = height >> 1; bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); int rowStep = Math.Max(1, height >> (tryHarder ? 8 : 5)); int maxLines; if (tryHarder) { maxLines = height; // Look at the whole image, not just the center } else { maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image } for (int x = 0; x < maxLines; x++) { // Scanning from the middle out. Determine which row we're looking at next: int rowStepsAboveOrBelow = (x + 1) >> 1; bool isAbove = (x & 0x01) == 0; // i.e. is x even? int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow); if (rowNumber < 0 || rowNumber >= height) { // Oops, if we run off the top or bottom, stop break; } // Estimate black point for this row and load it: try { row = image.getBlackRow(rowNumber, row); } catch (NotFoundException nfe) { continue; } // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to // handle decoding upside down barcodes. for (int attempt = 0; attempt < 2; attempt++) { if (attempt == 1) // trying again? { row.reverse(); // reverse the row and continue // This means we will only ever draw result points *once* in the life of this method // since we want to avoid drawing the wrong points after flipping the row, and, // don't want to clutter with noise from every single row scan -- just the scans // that start on the center line. if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { //IDictionary<DecodeHintType, object> newHints = new EnumMap<DecodeHintType, object>(typeof(DecodeHintType)); Dictionary<DecodeHintType,object> newHints = new Dictionary<DecodeHintType,object>(); //JAVA TO C# CONVERTER TODO TASK: There is no .NET Dictionary equivalent to the Java 'putAll' method: //newHints.putAll(hints); //foreach (DecodeHintType dht in Enum.GetValues(typeof(DecodeHintType))) //{ // newHints.Add(dht,dht); //} foreach (KeyValuePair<DecodeHintType,object> kvp in hints) { newHints.Add(kvp.Key,kvp.Value); } if (newHints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) { newHints.Remove(DecodeHintType.NEED_RESULT_POINT_CALLBACK); } hints = newHints; } } try { // Look for a barcode Result result = decodeRow(rowNumber, row, hints); // We found our barcode if (attempt == 1) { // But it was upside down, so note that result.putMetadata(ResultMetadataType.ORIENTATION, 180); // And remember to flip the result points horizontally. ResultPoint[] points = result.ResultPoints; if (points != null) { points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y); points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y); } } return result; } catch (ReaderException re) { // continue -- just couldn't decode this row } } } throw NotFoundException.NotFoundInstance; }