2 * Copyright 2008 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 BinaryBitmap = com.google.zxing.BinaryBitmap;
\r
18 using DecodeHintType = com.google.zxing.DecodeHintType;
\r
19 using Reader = com.google.zxing.Reader;
\r
20 using ReaderException = com.google.zxing.ReaderException;
\r
21 using Result = com.google.zxing.Result;
\r
22 using ResultMetadataType = com.google.zxing.ResultMetadataType;
\r
23 using ResultPoint = com.google.zxing.ResultPoint;
\r
24 using BitArray = com.google.zxing.common.BitArray;
\r
25 namespace com.google.zxing.oned
\r
28 /// <summary> Encapsulates functionality and implementation that is common to all families
\r
29 /// of one-dimensional barcodes.
\r
32 /// <author> dswitkin@google.com (Daniel Switkin)
\r
34 /// <author> Sean Owen
\r
36 /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
\r
38 public abstract class OneDReader : Reader
\r
41 private const int INTEGER_MATH_SHIFT = 8;
\r
42 //UPGRADE_NOTE: Final was removed from the declaration of 'PATTERN_MATCH_RESULT_SCALE_FACTOR '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
\r
43 internal static readonly int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;
\r
45 public virtual Result decode(BinaryBitmap image)
\r
47 return decode(image, null);
\r
50 // Note that we don't try rotation without the try harder flag, even if rotation was supported.
\r
51 public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints)
\r
55 return doDecode(image, hints);
\r
57 catch (ReaderException re)
\r
59 bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);
\r
60 if (tryHarder && image.RotateSupported)
\r
62 BinaryBitmap rotatedImage = image.rotateCounterClockwise();
\r
63 Result result = doDecode(rotatedImage, hints);
\r
64 // Record that we found it rotated 90 degrees CCW / 270 degrees CW
\r
65 System.Collections.Hashtable metadata = result.ResultMetadata;
\r
66 int orientation = 270;
\r
67 if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION))
\r
69 // But if we found it reversed in doDecode(), add in that result here:
\r
70 orientation = (orientation + ((System.Int32) metadata[ResultMetadataType.ORIENTATION])) % 360;
\r
72 result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) orientation);
\r
73 // Update result points
\r
74 ResultPoint[] points = result.ResultPoints;
\r
75 int height = rotatedImage.Height;
\r
76 for (int i = 0; i < points.Length; i++)
\r
78 points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X);
\r
89 /// <summary> We're going to examine rows from the middle outward, searching alternately above and below the
\r
90 /// middle, and farther out each time. rowStep is the number of rows between each successive
\r
91 /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then
\r
92 /// middle + rowStep, then middle - (2 * rowStep), etc.
\r
93 /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily
\r
94 /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the
\r
95 /// image if "trying harder".
\r
98 /// <param name="image">The image to decode
\r
100 /// <param name="hints">Any hints that were requested
\r
102 /// <returns> The contents of the decoded barcode
\r
104 /// <throws> ReaderException Any spontaneous errors which occur </throws>
\r
105 private Result doDecode(BinaryBitmap image, System.Collections.Hashtable hints)
\r
107 int width = image.Width;
\r
108 int height = image.Height;
\r
109 BitArray row = new BitArray(width);
\r
111 int middle = height >> 1;
\r
112 bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);
\r
113 int rowStep = System.Math.Max(1, height >> (tryHarder?7:4));
\r
117 maxLines = height; // Look at the whole image, not just the center
\r
121 maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image
\r
124 for (int x = 0; x < maxLines; x++)
\r
127 // Scanning from the middle out. Determine which row we're looking at next:
\r
128 int rowStepsAboveOrBelow = (x + 1) >> 1;
\r
129 bool isAbove = (x & 0x01) == 0; // i.e. is x even?
\r
130 int rowNumber = middle + rowStep * (isAbove?rowStepsAboveOrBelow:- rowStepsAboveOrBelow);
\r
131 if (rowNumber < 0 || rowNumber >= height)
\r
133 // Oops, if we run off the top or bottom, stop
\r
137 // Estimate black point for this row and load it:
\r
140 row = image.getBlackRow(rowNumber, row);
\r
142 catch (ReaderException re)
\r
147 // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
\r
148 // handle decoding upside down barcodes.
\r
149 for (int attempt = 0; attempt < 2; attempt++)
\r
154 row.reverse(); // reverse the row and continue
\r
155 // This means we will only ever draw result points *once* in the life of this method
\r
156 // since we want to avoid drawing the wrong points after flipping the row, and,
\r
157 // don't want to clutter with noise from every single row scan -- just the scans
\r
158 // that start on the center line.
\r
159 if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK))
\r
161 System.Collections.Hashtable newHints = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); // Can't use clone() in J2ME
\r
162 System.Collections.IEnumerator hintEnum = hints.Keys.GetEnumerator();
\r
163 //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'"
\r
164 while (hintEnum.MoveNext())
\r
166 //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'"
\r
167 System.Object key = hintEnum.Current;
\r
168 if (!key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK))
\r
170 newHints[key] = hints[key];
\r
178 // Look for a barcode
\r
179 Result result = decodeRow(rowNumber, row, hints);
\r
180 // We found our barcode
\r
183 // But it was upside down, so note that
\r
184 result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) 180);
\r
185 // And remember to flip the result points horizontally.
\r
186 ResultPoint[] points = result.ResultPoints;
\r
187 points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y);
\r
188 points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y);
\r
192 catch (ReaderException re)
\r
194 // continue -- just couldn't decode this row
\r
199 throw ReaderException.Instance;
\r
202 /// <summary> Records the size of successive runs of white and black pixels in a row, starting at a given point.
\r
203 /// The values are recorded in the given array, and the number of runs recorded is equal to the size
\r
204 /// of the array. If the row starts on a white pixel at the given start point, then the first count
\r
205 /// recorded is the run of white pixels starting from that point; likewise it is the count of a run
\r
206 /// of black pixels if the row begin on a black pixels at that point.
\r
209 /// <param name="row">row to count from
\r
211 /// <param name="start">offset into row to start at
\r
213 /// <param name="counters">array into which to record counts
\r
215 /// <throws> ReaderException if counters cannot be filled entirely from row before running out </throws>
\r
216 /// <summary> of pixels
\r
218 internal static void recordPattern(BitArray row, int start, int[] counters)
\r
220 int numCounters = counters.Length;
\r
221 for (int i = 0; i < numCounters; i++)
\r
225 int end = row.Size;
\r
228 throw ReaderException.Instance;
\r
230 bool isWhite = !row.get_Renamed(start);
\r
231 int counterPosition = 0;
\r
235 bool pixel = row.get_Renamed(i2);
\r
236 if (pixel ^ isWhite)
\r
238 // that is, exactly one is true
\r
239 counters[counterPosition]++;
\r
244 if (counterPosition == numCounters)
\r
250 counters[counterPosition] = 1;
\r
251 isWhite ^= true; // isWhite = !isWhite;
\r
256 // If we read fully the last section of pixels and filled up our counters -- or filled
\r
257 // the last counter but ran off the side of the image, OK. Otherwise, a problem.
\r
258 if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i2 == end)))
\r
260 throw ReaderException.Instance;
\r
264 /// <summary> Determines how closely a set of observed counts of runs of black/white values matches a given
\r
265 /// target pattern. This is reported as the ratio of the total variance from the expected pattern
\r
266 /// proportions across all pattern elements, to the length of the pattern.
\r
269 /// <param name="counters">observed counters
\r
271 /// <param name="pattern">expected pattern
\r
273 /// <param name="maxIndividualVariance">The most any counter can differ before we give up
\r
275 /// <returns> ratio of total variance between counters and pattern compared to total pattern size,
\r
276 /// where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means
\r
277 /// the total variance between counters and patterns equals the pattern length, higher values mean
\r
278 /// even more variance
\r
280 internal static int patternMatchVariance(int[] counters, int[] pattern, int maxIndividualVariance)
\r
282 int numCounters = counters.Length;
\r
284 int patternLength = 0;
\r
285 for (int i = 0; i < numCounters; i++)
\r
287 total += counters[i];
\r
288 patternLength += pattern[i];
\r
290 if (total < patternLength)
\r
292 // If we don't even have one pixel per unit of bar width, assume this is too small
\r
293 // to reliably match, so fail:
\r
294 return System.Int32.MaxValue;
\r
296 // We're going to fake floating-point math in integers. We just need to use more bits.
\r
297 // Scale up patternLength so that intermediate values below like scaledCounter will have
\r
298 // more "significant digits"
\r
299 int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
\r
300 maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
\r
302 int totalVariance = 0;
\r
303 for (int x = 0; x < numCounters; x++)
\r
305 int counter = counters[x] << INTEGER_MATH_SHIFT;
\r
306 int scaledPattern = pattern[x] * unitBarWidth;
\r
307 int variance = counter > scaledPattern?counter - scaledPattern:scaledPattern - counter;
\r
308 if (variance > maxIndividualVariance)
\r
310 return System.Int32.MaxValue;
\r
312 totalVariance += variance;
\r
314 return totalVariance / total;
\r
317 /// <summary> <p>Attempts to decode a one-dimensional barcode format given a single row of
\r
321 /// <param name="rowNumber">row number from top of the row
\r
323 /// <param name="row">the black/white pixel data of the row
\r
325 /// <param name="hints">decode hints
\r
327 /// <returns> {@link Result} containing encoded string and start/end of barcode
\r
329 /// <throws> ReaderException if an error occurs or barcode cannot be found </throws>
\r
330 public abstract Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints);
\r