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 if (!recordPattern(row, payloadStart, counterDigitPair, counterDigitPairLen)) {
138 throw ReaderException("");
140 // Split them into each array
141 for (int k = 0; k < 5; k++) {
143 counterBlack[k] = counterDigitPair[twoK];
144 counterWhite[k] = counterDigitPair[twoK + 1];
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));
152 for (int i = 0; i < counterDigitPairLen; i++) {
153 payloadStart += counterDigitPair[i];
159 * Identify where the start of the middle / payload section starts.
161 * @param row row of black/white values to search
162 * @return Array, containing index of start of 'start block' and end of
164 * @throws ReaderException
166 int* ITFReader::decodeStart(Ref<BitArray> row) {
167 int endStart = skipWhiteSpace(row);
168 int* startPattern = 0;
170 startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN);
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]);
178 } catch (ReaderException re) {
179 delete [] startPattern;
185 * Identify where the end of the middle / payload section ends.
187 * @param row row of black/white values to search
188 * @return Array, containing index of start of 'end block' and end of 'end
190 * @throws ReaderException
193 int* ITFReader::decodeEnd(Ref<BitArray> row) {
194 // For convenience, reverse the row and then
195 // search from 'the start' for the end block
199 int endStart = skipWhiteSpace(row);
200 endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN);
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]);
207 // Now recalculate the indices of where the 'endblock' starts & stops to
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;
216 } catch (ReaderException re) {
217 delete [] endPattern;
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
229 * Note: Its assumed the row is reversed when using this method to find
230 * quiet zone after the end pattern.
232 * ref: http://www.barcode-1.net/i25code.html
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.
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
242 // for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
243 // if (row->get(i)) {
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");
255 * Skip all whitespace until we get to the first black line.
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
261 int ITFReader::skipWhiteSpace(Ref<BitArray> row) {
262 int width = row->getSize();
264 while (endStart < width) {
265 if (row->get(endStart)) {
270 if (endStart == width) {
271 throw ReaderException("");
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
283 * @throws ReaderException if pattern is not found
285 int* ITFReader::findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[],
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++) {
294 int width = row->getSize();
295 bool isWhite = false;
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]++;
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;
312 patternStart += counters[0] + counters[1];
313 for (int y = 2; y < patternLength; y++) {
314 counters[y - 2] = counters[y];
316 counters[patternLength - 2] = 0;
317 counters[patternLength - 1] = 0;
322 counters[counterPosition] = 1;
326 throw ReaderException("");
330 * Attempts to decode a sequence of ITF black/white lines into single
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
337 int ITFReader::decodeDigit(int counters[], int countersLen){
338 unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
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];
346 unsigned int variance = patternMatchVariance(counters, countersLen, pattern,
347 MAX_INDIVIDUAL_VARIANCE);
348 if (variance < bestVariance) {
349 bestVariance = variance;
353 if (bestMatch >= 0) {
356 throw ReaderException("digit didint found");
360 ITFReader::~ITFReader(){