5 * Copyright 2010 ZXing authors All rights reserved.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include "ITFReader.h"
21 #include <zxing/oned/OneDResultPoint.h>
22 #include <zxing/common/Array.h>
23 #include <zxing/ReaderException.h>
29 static const int W = 3; // Pixel width of a wide line
30 static const int N = 1; // Pixed width of a narrow line
32 const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 };
35 * Start/end guard pattern.
37 * Note: The end pattern is reversed because the row is reversed before
38 * searching for the END_PATTERN
40 static const int START_PATTERN_LEN = 4;
41 static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N};
43 static const int END_PATTERN_REVERSED_LEN = 3;
44 static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W};
47 * Patterns of Wide / Narrow lines to indicate each digit
49 static const int PATTERNS_LEN = 10;
50 static const int PATTERNS[PATTERNS_LEN][5] = {
64 ITFReader::ITFReader() : narrowLineWidth(-1) {
68 Ref<Result> ITFReader::decodeRow(int rowNumber, Ref<BitArray> row) {
72 // Find out where the Middle section (payload) starts & ends
73 startRange = decodeStart(row);
74 endRange = decodeEnd(row);
76 std::string tmpResult;
77 decodeMiddle(row, startRange[1], endRange[0], tmpResult);
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) {
87 throw ReaderException("not enough characters count");
90 Ref<String> resultString(new String(tmpResult));
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;
100 ArrayRef<unsigned char> resultBytes(1);
101 return Ref<Result>(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF));
102 } catch (ReaderException re) {
103 delete [] startRange;
105 return Ref<Result>();
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
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
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;
130 for (int i=0; i<5; i++) {
135 while (payloadStart < payloadEnd) {
136 // Get 10 runs of black/white.
137 recordPattern(row, payloadStart, counterDigitPair, counterDigitPairLen);
138 // Split them into each array
139 for (int k = 0; k < 5; k++) {
141 counterBlack[k] = counterDigitPair[twoK];
142 counterWhite[k] = counterDigitPair[twoK + 1];
145 int bestMatch = decodeDigit(counterBlack, 5);
146 resultString.append(1, (char) ('0' + bestMatch));
147 bestMatch = decodeDigit(counterWhite, 5);
148 resultString.append(1, (char) ('0' + bestMatch));
150 for (int i = 0; i < counterDigitPairLen; i++) {
151 payloadStart += counterDigitPair[i];
157 * Identify where the start of the middle / payload section starts.
159 * @param row row of black/white values to search
160 * @return Array, containing index of start of 'start block' and end of
162 * @throws ReaderException
164 int* ITFReader::decodeStart(Ref<BitArray> row) {
165 int endStart = skipWhiteSpace(row);
166 int* startPattern = 0;
168 startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN);
170 // Determine the width of a narrow line in pixels. We can do this by
171 // getting the width of the start pattern and dividing by 4 because its
172 // made up of 4 narrow lines.
173 narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;
174 validateQuietZone(row, startPattern[0]);
176 } catch (ReaderException re) {
177 delete [] startPattern;
183 * Identify where the end of the middle / payload section ends.
185 * @param row row of black/white values to search
186 * @return Array, containing index of start of 'end block' and end of 'end
188 * @throws ReaderException
191 int* ITFReader::decodeEnd(Ref<BitArray> row) {
192 // For convenience, reverse the row and then
193 // search from 'the start' for the end block
197 int endStart = skipWhiteSpace(row);
198 endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN);
200 // The start & end patterns must be pre/post fixed by a quiet zone. This
201 // zone must be at least 10 times the width of a narrow line.
202 // ref: http://www.barcode-1.net/i25code.html
203 validateQuietZone(row, endPattern[0]);
205 // Now recalculate the indices of where the 'endblock' starts & stops to
207 // the reversed nature of the search
208 int temp = endPattern[0];
209 endPattern[0] = row->getSize() - endPattern[1];
210 endPattern[1] = row->getSize() - temp;
214 } catch (ReaderException re) {
215 delete [] endPattern;
222 * The start & end patterns must be pre/post fixed by a quiet zone. This
223 * zone must be at least 10 times the width of a narrow line. Scan back until
224 * we either get to the start of the barcode or match the necessary number of
227 * Note: Its assumed the row is reversed when using this method to find
228 * quiet zone after the end pattern.
230 * ref: http://www.barcode-1.net/i25code.html
232 * @param row bit array representing the scanned barcode.
233 * @param startPattern index into row of the start or end pattern.
234 * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown.
236 void ITFReader::validateQuietZone(Ref<BitArray> row, int startPattern) {
237 //#pragma mark needs some corrections
238 // int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone
240 // for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
241 // if (row->get(i)) {
246 // if (quietCount != 0) {
247 // // Unable to find the necessary number of quiet zone pixels.
248 // throw ReaderException("Unable to find the necessary number of quiet zone pixels");
253 * Skip all whitespace until we get to the first black line.
255 * @param row row of black/white values to search
256 * @return index of the first black line.
257 * @throws ReaderException Throws exception if no black lines are found in the row
259 int ITFReader::skipWhiteSpace(Ref<BitArray> row) {
260 int width = row->getSize();
262 while (endStart < width) {
263 if (row->get(endStart)) {
268 if (endStart == width) {
269 throw ReaderException("");
275 * @param row row of black/white values to search
276 * @param rowOffset position to start search
277 * @param pattern pattern of counts of number of black and white pixels that are
278 * being searched for as a pattern
279 * @return start/end horizontal offset of guard pattern, as an array of two
281 * @throws ReaderException if pattern is not found
283 int* ITFReader::findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[],
285 // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be
286 // merged to a single method.
287 int patternLength = patternLen;
288 int counters[patternLength];
289 for (int i=0; i<patternLength; i++) {
292 int width = row->getSize();
293 bool isWhite = false;
295 int counterPosition = 0;
296 int patternStart = rowOffset;
297 for (int x = rowOffset; x < width; x++) {
298 bool pixel = row->get(x);
299 if (pixel ^ isWhite) {
300 counters[counterPosition]++;
302 if (counterPosition == patternLength - 1) {
303 if (patternMatchVariance(counters, patternLength, pattern,
304 MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
305 int* resultValue = new int[2];
306 resultValue[0] = patternStart;
310 patternStart += counters[0] + counters[1];
311 for (int y = 2; y < patternLength; y++) {
312 counters[y - 2] = counters[y];
314 counters[patternLength - 2] = 0;
315 counters[patternLength - 1] = 0;
320 counters[counterPosition] = 1;
324 throw ReaderException("");
328 * Attempts to decode a sequence of ITF black/white lines into single
331 * @param counters the counts of runs of observed black/white/black/... values
332 * @return The decoded digit
333 * @throws ReaderException if digit cannot be decoded
335 int ITFReader::decodeDigit(int counters[], int countersLen){
336 unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
338 int max = PATTERNS_LEN;
339 for (int i = 0; i < max; i++) {
340 int pattern[countersLen];
341 for(int ind = 0; ind<countersLen; ind++){
342 pattern[ind] = PATTERNS[i][ind];
344 unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
345 MAX_INDIVIDUAL_VARIANCE);
346 if (variance < bestVariance) {
347 bestVariance = variance;
351 if (bestMatch >= 0) {
354 throw ReaderException("digit didint found");
358 ITFReader::~ITFReader(){