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