Changed OneDReader::recordPattern to not throw exceptions. For now it just
[zxing.git] / cpp / core / src / zxing / oned / ITFReader.cpp
1 /*
2  *  ITFReader.cpp
3  *  ZXing
4  *
5  *  Copyright 2010 ZXing authors All rights reserved.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include "ITFReader.h"
21 #include <zxing/oned/OneDResultPoint.h>
22 #include <zxing/common/Array.h>
23 #include <zxing/ReaderException.h>
24 #include <math.h>
25
26 namespace zxing {
27   namespace oned {
28
29     static const int W = 3; // Pixel width of a wide line
30     static const int N = 1; // Pixed width of a narrow line
31
32     const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 };
33
34     /**
35      * Start/end guard pattern.
36      *
37      * Note: The end pattern is reversed because the row is reversed before
38      * searching for the END_PATTERN
39      */
40     static const int START_PATTERN_LEN = 4;
41     static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N};
42
43     static const int END_PATTERN_REVERSED_LEN = 3;
44     static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W};
45
46     /**
47      * Patterns of Wide / Narrow lines to indicate each digit
48      */
49     static const int PATTERNS_LEN = 10;
50     static const int PATTERNS[PATTERNS_LEN][5] = {
51       {N, N, W, W, N}, // 0
52       {W, N, N, N, W}, // 1
53       {N, W, N, N, W}, // 2
54       {W, W, N, N, N}, // 3
55       {N, N, W, N, W}, // 4
56       {W, N, W, N, N}, // 5
57       {N, W, W, N, N}, // 6
58       {N, N, N, W, W}, // 7
59       {W, N, N, W, N}, // 8
60       {N, W, N, W, N}  // 9
61     };
62
63
64     ITFReader::ITFReader() : narrowLineWidth(-1) {
65     }
66
67
68     Ref<Result> ITFReader::decodeRow(int rowNumber, Ref<BitArray> row) {
69       int* startRange = 0;
70       int* endRange = 0;
71       try {
72         // Find out where the Middle section (payload) starts & ends
73         startRange = decodeStart(row);
74         endRange = decodeEnd(row);
75
76         std::string tmpResult;
77         decodeMiddle(row, startRange[1], endRange[0], tmpResult);
78
79         // To avoid false positives with 2D barcodes (and other patterns), make
80         // an assumption that the decoded string must be 6, 10 or 14 digits.
81         int length = tmpResult.length();
82         bool lengthOK = false;
83         if (length == 6 || length == 10 || length == 14) {
84           lengthOK = true;
85         }
86         if (!lengthOK) {
87           throw ReaderException("not enough characters count");
88         }
89
90         Ref<String> resultString(new String(tmpResult));
91
92         std::vector< Ref<ResultPoint> > resultPoints(2);
93         Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber));
94         Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber));
95         resultPoints[0] = resultPoint1;
96         resultPoints[1] = resultPoint2;
97
98         delete [] startRange;
99         delete [] endRange;
100         ArrayRef<unsigned char> resultBytes(1);
101         return Ref<Result>(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF));
102       } catch (ReaderException re) {
103         delete [] startRange;
104         delete [] endRange;
105         return Ref<Result>();
106       }
107     }
108
109     /**
110      * @param row          row of black/white values to search
111      * @param payloadStart offset of start pattern
112      * @param resultString {@link StringBuffer} to append decoded chars to
113      * @throws ReaderException if decoding could not complete successfully
114      */
115     void ITFReader::decodeMiddle(Ref<BitArray> row, int payloadStart, int payloadEnd,
116         std::string& resultString) {
117       // Digits are interleaved in pairs - 5 black lines for one digit, and the
118       // 5
119       // interleaved white lines for the second digit.
120       // Therefore, need to scan 10 lines and then
121       // split these into two arrays
122       int counterDigitPairLen = 10;
123       int counterDigitPair[counterDigitPairLen];
124       for (int i=0; i<counterDigitPairLen; i++) {
125         counterDigitPair[i] = 0;
126       }
127
128       int counterBlack[5];
129       int counterWhite[5];
130       for (int i=0; i<5; i++) {
131         counterBlack[i] = 0;
132         counterWhite[i] = 0;
133       }
134
135       while (payloadStart < payloadEnd) {
136         // Get 10 runs of black/white.
137         if (!recordPattern(row, payloadStart, counterDigitPair, counterDigitPairLen)) {
138           throw ReaderException("");
139         }
140         // Split them into each array
141         for (int k = 0; k < 5; k++) {
142           int twoK = k << 1;
143           counterBlack[k] = counterDigitPair[twoK];
144           counterWhite[k] = counterDigitPair[twoK + 1];
145         }
146
147         int bestMatch = decodeDigit(counterBlack, 5);
148         resultString.append(1, (char) ('0' + bestMatch));
149         bestMatch = decodeDigit(counterWhite, 5);
150         resultString.append(1, (char) ('0' + bestMatch));
151
152         for (int i = 0; i < counterDigitPairLen; i++) {
153           payloadStart += counterDigitPair[i];
154         }
155       }
156     }
157
158     /**
159      * Identify where the start of the middle / payload section starts.
160      *
161      * @param row row of black/white values to search
162      * @return Array, containing index of start of 'start block' and end of
163      *         'start block'
164      * @throws ReaderException
165      */
166     int* ITFReader::decodeStart(Ref<BitArray> row) {
167       int endStart = skipWhiteSpace(row);
168       int* startPattern = 0;
169       try {
170           startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN);
171
172           // Determine the width of a narrow line in pixels. We can do this by
173           // getting the width of the start pattern and dividing by 4 because its
174           // made up of 4 narrow lines.
175           narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;
176           validateQuietZone(row, startPattern[0]);
177           return startPattern;
178       } catch (ReaderException re) {
179           delete [] startPattern;
180         throw re;
181       }
182     }
183
184     /**
185      * Identify where the end of the middle / payload section ends.
186      *
187      * @param row row of black/white values to search
188      * @return Array, containing index of start of 'end block' and end of 'end
189      *         block'
190      * @throws ReaderException
191      */
192
193     int* ITFReader::decodeEnd(Ref<BitArray> row) {
194       // For convenience, reverse the row and then
195       // search from 'the start' for the end block
196       row->reverse();
197                         int* endPattern = 0;
198       try {
199         int endStart = skipWhiteSpace(row);
200         endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN);
201
202         // The start & end patterns must be pre/post fixed by a quiet zone. This
203         // zone must be at least 10 times the width of a narrow line.
204         // ref: http://www.barcode-1.net/i25code.html
205         validateQuietZone(row, endPattern[0]);
206
207         // Now recalculate the indices of where the 'endblock' starts & stops to
208         // accommodate
209         // the reversed nature of the search
210         int temp = endPattern[0];
211         endPattern[0] = row->getSize() - endPattern[1];
212         endPattern[1] = row->getSize() - temp;
213
214         row->reverse();
215         return endPattern;
216       } catch (ReaderException re) {
217                                 delete [] endPattern;
218         row->reverse();
219         throw re;
220       }
221     }
222
223     /**
224      * The start & end patterns must be pre/post fixed by a quiet zone. This
225      * zone must be at least 10 times the width of a narrow line.  Scan back until
226      * we either get to the start of the barcode or match the necessary number of
227      * quiet zone pixels.
228      *
229      * Note: Its assumed the row is reversed when using this method to find
230      * quiet zone after the end pattern.
231      *
232      * ref: http://www.barcode-1.net/i25code.html
233      *
234      * @param row bit array representing the scanned barcode.
235      * @param startPattern index into row of the start or end pattern.
236      * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown.
237      */
238     void ITFReader::validateQuietZone(Ref<BitArray> row, int startPattern) {
239 //#pragma mark needs some corrections
240 //      int quietCount = narrowLineWidth * 10;  // expect to find this many pixels of quiet zone
241 //
242 //      for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
243 //        if (row->get(i)) {
244 //          break;
245 //        }
246 //        quietCount--;
247 //      }
248 //      if (quietCount != 0) {
249 //        // Unable to find the necessary number of quiet zone pixels.
250 //        throw ReaderException("Unable to find the necessary number of quiet zone pixels");
251 //      }
252     }
253
254     /**
255      * Skip all whitespace until we get to the first black line.
256      *
257      * @param row row of black/white values to search
258      * @return index of the first black line.
259      * @throws ReaderException Throws exception if no black lines are found in the row
260      */
261     int ITFReader::skipWhiteSpace(Ref<BitArray> row) {
262       int width = row->getSize();
263       int endStart = 0;
264       while (endStart < width) {
265         if (row->get(endStart)) {
266           break;
267         }
268         endStart++;
269       }
270       if (endStart == width) {
271         throw ReaderException("");
272       }
273       return endStart;
274     }
275
276     /**
277      * @param row       row of black/white values to search
278      * @param rowOffset position to start search
279      * @param pattern   pattern of counts of number of black and white pixels that are
280      *                  being searched for as a pattern
281      * @return start/end horizontal offset of guard pattern, as an array of two
282      *         ints
283      * @throws ReaderException if pattern is not found
284      */
285     int* ITFReader::findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[],
286         int patternLen) {
287       // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be
288       // merged to a single method.
289       int patternLength = patternLen;
290       int counters[patternLength];
291       for (int i=0; i<patternLength; i++) {
292         counters[i] = 0;
293       }
294       int width = row->getSize();
295       bool isWhite = false;
296
297       int counterPosition = 0;
298       int patternStart = rowOffset;
299       for (int x = rowOffset; x < width; x++) {
300         bool pixel = row->get(x);
301         if (pixel ^ isWhite) {
302           counters[counterPosition]++;
303         } else {
304           if (counterPosition == patternLength - 1) {
305             if (patternMatchVariance(counters, patternLength, pattern,
306                 MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
307               int* resultValue = new int[2];
308               resultValue[0] = patternStart;
309               resultValue[1] = x;
310               return resultValue;
311             }
312             patternStart += counters[0] + counters[1];
313             for (int y = 2; y < patternLength; y++) {
314               counters[y - 2] = counters[y];
315             }
316             counters[patternLength - 2] = 0;
317             counters[patternLength - 1] = 0;
318             counterPosition--;
319           } else {
320             counterPosition++;
321           }
322           counters[counterPosition] = 1;
323           isWhite = !isWhite;
324         }
325       }
326       throw ReaderException("");
327     }
328
329     /**
330      * Attempts to decode a sequence of ITF black/white lines into single
331      * digit.
332      *
333      * @param counters the counts of runs of observed black/white/black/... values
334      * @return The decoded digit
335      * @throws ReaderException if digit cannot be decoded
336      */
337     int ITFReader::decodeDigit(int counters[], int countersLen){
338       unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
339       int bestMatch = -1;
340       int max = PATTERNS_LEN;
341       for (int i = 0; i < max; i++) {
342         int pattern[countersLen];
343         for(int ind = 0; ind<countersLen; ind++){
344           pattern[ind] = PATTERNS[i][ind];
345         }
346         unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
347             MAX_INDIVIDUAL_VARIANCE);
348         if (variance < bestVariance) {
349           bestVariance = variance;
350           bestMatch = i;
351         }
352       }
353       if (bestMatch >= 0) {
354         return bestMatch;
355       } else {
356         throw ReaderException("digit didint found");
357       }
358     }
359
360     ITFReader::~ITFReader(){
361     }
362   }
363 }