2 * Copyright 2009 ZXing authors
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
17 using ReaderException = com.google.zxing.ReaderException;
\r
18 using ResultPoint = com.google.zxing.ResultPoint;
\r
19 using BitMatrix = com.google.zxing.common.BitMatrix;
\r
20 namespace com.google.zxing.common.detector
\r
23 /// <summary> <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
\r
24 /// It looks within a mostly white region of an image for a region of black and white, but mostly
\r
25 /// black. It returns the four corners of the region, as best it can determine.</p>
\r
28 /// <author> Sean Owen
\r
30 /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
\r
32 public sealed class MonochromeRectangleDetector
\r
35 private const int MAX_MODULES = 32;
\r
37 //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
\r
38 private BitMatrix image;
\r
40 public MonochromeRectangleDetector(BitMatrix image)
\r
45 /// <summary> <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
\r
46 /// white, in an image.</p>
\r
49 /// <returns> {@link ResultPoint}[] describing the corners of the rectangular region. The first and
\r
50 /// last points are opposed on the diagonal, as are the second and third. The first point will be
\r
51 /// the topmost point and the last, the bottommost. The second point will be leftmost and the
\r
52 /// third, the rightmost
\r
54 /// <throws> ReaderException if no Data Matrix Code can be found </throws>
\r
55 public ResultPoint[] detect()
\r
57 int height = image.Height;
\r
58 int width = image.Width;
\r
59 int halfHeight = height >> 1;
\r
60 int halfWidth = width >> 1;
\r
61 int deltaY = System.Math.Max(1, height / (MAX_MODULES << 3));
\r
62 int deltaX = System.Math.Max(1, width / (MAX_MODULES << 3));
\r
65 int bottom = height;
\r
68 ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 1);
\r
69 //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
\r
70 top = (int) pointA.Y - 1;
\r
71 ResultPoint pointB = findCornerFromCenter(halfWidth, - deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1);
\r
72 //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
\r
73 left = (int) pointB.X - 1;
\r
74 ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1);
\r
75 //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
\r
76 right = (int) pointC.X + 1;
\r
77 ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, deltaY, top, bottom, halfWidth >> 1);
\r
78 //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
\r
79 bottom = (int) pointD.Y + 1;
\r
81 // Go try to find point A again with better information -- might have been off at first.
\r
82 pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 2);
\r
84 return new ResultPoint[]{pointA, pointB, pointC, pointD};
\r
87 /// <summary> Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
\r
88 /// point which should be within the barcode.
\r
91 /// <param name="centerX">center's x component (horizontal)
\r
93 /// <param name="deltaX">same as deltaY but change in x per step instead
\r
95 /// <param name="left">minimum value of x
\r
97 /// <param name="right">maximum value of x
\r
99 /// <param name="centerY">center's y component (vertical)
\r
101 /// <param name="deltaY">change in y per step. If scanning up this is negative; down, positive;
\r
102 /// left or right, 0
\r
104 /// <param name="top">minimum value of y to search through (meaningless when di == 0)
\r
106 /// <param name="bottom">maximum value of y
\r
108 /// <param name="maxWhiteRun">maximum run of white pixels that can still be considered to be within
\r
111 /// <returns> a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
\r
113 /// <throws> com.google.zxing.ReaderException if such a point cannot be found </throws>
\r
114 private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right, int centerY, int deltaY, int top, int bottom, int maxWhiteRun)
\r
116 int[] lastRange = null;
\r
117 for (int y = centerY, x = centerX; y < bottom && y >= top && x < right && x >= left; y += deltaY, x += deltaX)
\r
122 // horizontal slices, up and down
\r
123 range = blackWhiteRange(y, maxWhiteRun, left, right, true);
\r
127 // vertical slices, left and right
\r
128 range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
\r
132 if (lastRange == null)
\r
134 throw ReaderException.Instance;
\r
136 // lastRange was found
\r
139 int lastY = y - deltaY;
\r
140 if (lastRange[0] < centerX)
\r
142 if (lastRange[1] > centerX)
\r
144 // straddle, choose one or the other based on direction
\r
145 return new ResultPoint(deltaY > 0?lastRange[0]:lastRange[1], lastY);
\r
147 return new ResultPoint(lastRange[0], lastY);
\r
151 return new ResultPoint(lastRange[1], lastY);
\r
156 int lastX = x - deltaX;
\r
157 if (lastRange[0] < centerY)
\r
159 if (lastRange[1] > centerY)
\r
161 return new ResultPoint(lastX, deltaX < 0?lastRange[0]:lastRange[1]);
\r
163 return new ResultPoint(lastX, lastRange[0]);
\r
167 return new ResultPoint(lastX, lastRange[1]);
\r
173 throw ReaderException.Instance;
\r
176 /// <summary> Computes the start and end of a region of pixels, either horizontally or vertically, that could
\r
177 /// be part of a Data Matrix barcode.
\r
180 /// <param name="fixedDimension">if scanning horizontally, this is the row (the fixed vertical location)
\r
181 /// where we are scanning. If scanning vertically it's the column, the fixed horizontal location
\r
183 /// <param name="maxWhiteRun">largest run of white pixels that can still be considered part of the
\r
186 /// <param name="minDim">minimum pixel location, horizontally or vertically, to consider
\r
188 /// <param name="maxDim">maximum pixel location, horizontally or vertically, to consider
\r
190 /// <param name="horizontal">if true, we're scanning left-right, instead of up-down
\r
192 /// <returns> int[] with start and end of found range, or null if no such range is found
\r
193 /// (e.g. only white was found)
\r
195 private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, bool horizontal)
\r
198 int center = (minDim + maxDim) >> 1;
\r
200 // Scan left/up first
\r
201 int start = center;
\r
202 while (start >= minDim)
\r
204 if (horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start))
\r
210 int whiteRunStart = start;
\r
215 while (start >= minDim && !(horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start)));
\r
216 int whiteRunSize = whiteRunStart - start;
\r
217 if (start < minDim || whiteRunSize > maxWhiteRun)
\r
219 start = whiteRunStart;
\r
226 // Then try right/down
\r
228 while (end < maxDim)
\r
230 if (horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end))
\r
236 int whiteRunStart = end;
\r
241 while (end < maxDim && !(horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end)));
\r
242 int whiteRunSize = end - whiteRunStart;
\r
243 if (end >= maxDim || whiteRunSize > maxWhiteRun)
\r
245 end = whiteRunStart;
\r
252 return end > start?new int[]{start, end}:null;
\r