X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=csharp%2Fdatamatrix%2Fdetector%2FDetector.cs;fp=csharp%2Fdatamatrix%2Fdetector%2FDetector.cs;h=31278d0064fdc0732fa74591cb2f5b43a388b835;hb=ec1d7dfa5fb34e69b0f65f936eec23c8a8b88b56;hp=0000000000000000000000000000000000000000;hpb=e8f7ac2a4615b772609002f4f903fda2e9474c5c;p=zxing.git diff --git a/csharp/datamatrix/detector/Detector.cs b/csharp/datamatrix/detector/Detector.cs new file mode 100755 index 00000000..31278d00 --- /dev/null +++ b/csharp/datamatrix/detector/Detector.cs @@ -0,0 +1,325 @@ +/* +* Copyright 2008 ZXing authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using BitMatrix = com.google.zxing.common.BitMatrix; +using Collections = com.google.zxing.common.Collections; +using Comparator = com.google.zxing.common.Comparator; +using DetectorResult = com.google.zxing.common.DetectorResult; +using GridSampler = com.google.zxing.common.GridSampler; +using MonochromeRectangleDetector = com.google.zxing.common.detector.MonochromeRectangleDetector; +namespace com.google.zxing.datamatrix.detector +{ + + ///

Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code + /// is rotated or skewed, or partially obscured.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Detector + { + + //private static final int MAX_MODULES = 32; + + // Trick to avoid creating new Integer objects below -- a sort of crude copy of + // the Integer.valueOf(int) optimization added in Java 5, not in J2ME + //UPGRADE_NOTE: Final was removed from the declaration of 'INTEGERS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly System.Int32[] INTEGERS = new System.Int32[]{0, 1, 2, 3, 4}; + // No, can't use valueOf() + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix image; + //UPGRADE_NOTE: Final was removed from the declaration of 'rectangleDetector '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private MonochromeRectangleDetector rectangleDetector; + + public Detector(BitMatrix image) + { + this.image = image; + rectangleDetector = new MonochromeRectangleDetector(image); + } + + ///

Detects a Data Matrix Code in an image.

+ /// + ///
+ /// {@link DetectorResult} encapsulating results of detecting a QR Code + /// + /// ReaderException if no Data Matrix Code can be found + 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}); + } + + /// Increments the Integer associated with a key by one. + 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 + } + + 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); + } + + /// Counts the number of black/white transitions between two points, using something like Bresenham's algorithm. + 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); + } + + /// Simply encapsulates two points and a number of transitions between them. + private class ResultPointsAndTransitions + { + public ResultPoint From + { + get + { + return from; + } + + } + public ResultPoint To + { + get + { + return to; + } + + } + public int Transitions + { + get + { + return transitions; + } + + } + //UPGRADE_NOTE: Final was removed from the declaration of 'from '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPoint from; + //UPGRADE_NOTE: Final was removed from the declaration of 'to '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPoint to; + //UPGRADE_NOTE: Final was removed from the declaration of 'transitions '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int transitions; + internal ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions) + { + this.from = from; + this.to = to; + this.transitions = transitions; + } + public override System.String ToString() + { + return from + "/" + to + '/' + transitions; + } + } + + /// Orders ResultPointsAndTransitions by number of transitions, ascending. + private class ResultPointsAndTransitionsComparator : Comparator + { + public int compare(System.Object o1, System.Object o2) + { + return ((ResultPointsAndTransitions) o1).Transitions - ((ResultPointsAndTransitions) o2).Transitions; + } + } + } +} \ No newline at end of file