5 * Created by Lukasz Warchol on 10-01-26.
6 * Copyright 2010 ZXing authors All rights reserved.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "ITFReader.h"
22 #include <zxing/oned/OneDResultPoint.h>
23 #include <zxing/common/Array.h>
24 #include <zxing/ReaderException.h>
30 static const int W = 3; // Pixel width of a wide line
31 static const int N = 1; // Pixed width of a narrow line
33 const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 };
36 * Start/end guard pattern.
38 * Note: The end pattern is reversed because the row is reversed before
39 * searching for the END_PATTERN
41 static const int START_PATTERN_LEN = 4;
42 static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N};
44 static const int END_PATTERN_REVERSED_LEN = 3;
45 static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W};
48 * Patterns of Wide / Narrow lines to indicate each digit
50 static const int PATTERNS_LEN = 10;
51 static const int PATTERNS[PATTERNS_LEN][5] = {
65 ITFReader::ITFReader(){
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);
75 std::string tmpResult;
76 decodeMiddle(row, startRange[1], endRange[0], tmpResult);
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) {
86 throw ReaderException("not enought characters count");
89 Ref<String> resultString(new String(tmpResult));
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;
97 ArrayRef<unsigned char> resultBytes(1);
102 Ref<Result> res(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF));
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
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
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;
124 int* counterBlack = new int[5];
125 int* counterWhite = new int[5];
126 for (int i=0; i<5; i++) {
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++) {
137 counterBlack[k] = counterDigitPair[twoK];
138 counterWhite[k] = counterDigitPair[twoK + 1];
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));
146 for (int i = 0; i < counterDigitPairLen; i++) {
147 payloadStart += counterDigitPair[i];
150 delete [] counterDigitPair;
151 delete [] counterBlack;
152 delete [] counterWhite;
156 * Identify where the start of the middle / payload section starts.
158 * @param row row of black/white values to search
159 * @return Array, containing index of start of 'start block' and end of
161 * @throws ReaderException
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);
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;
173 validateQuietZone(row, startPattern[0]);
179 * Identify where the end of the middle / payload section ends.
181 * @param row row of black/white values to search
182 * @return Array, containing index of start of 'end block' and end of 'end
184 * @throws ReaderException
187 int* ITFReader::decodeEnd(Ref<BitArray> row){
188 // For convenience, reverse the row and then
189 // search from 'the start' for the end block
192 int endStart = skipWhiteSpace(row);
193 int* endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN);
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]);
200 // Now recalculate the indices of where the 'endblock' starts & stops to
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;
208 }catch (Exception e) {
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
220 * Note: Its assumed the row is reversed when using this method to find
221 * quiet zone after the end pattern.
223 * ref: http://www.barcode-1.net/i25code.html
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.
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
233 // for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
234 // if (row->get(i)) {
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");
246 * Skip all whitespace until we get to the first black line.
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
252 int ITFReader::skipWhiteSpace(Ref<BitArray> row){
253 int width = row->getSize();
255 while (endStart < width) {
256 if (row->get(endStart)) {
261 if (endStart == width) {
262 throw ReaderException("");
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
274 * @throws ReaderException if pattern is not found
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++) {
285 int width = row->getSize();
286 bool isWhite = false;
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]++;
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;
302 patternStart += counters[0] + counters[1];
303 for (int y = 2; y < patternLength; y++) {
304 counters[y - 2] = counters[y];
306 counters[patternLength - 2] = 0;
307 counters[patternLength - 1] = 0;
312 counters[counterPosition] = 1;
316 throw ReaderException("");
320 * Attempts to decode a sequence of ITF black/white lines into single
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
327 int ITFReader::decodeDigit(int counters[], int countersLen){
328 int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
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];
337 int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE);
338 if (variance < bestVariance) {
339 bestVariance = variance;
344 if (bestMatch >= 0) {
347 throw ReaderException("digit didint found");
352 ITFReader::~ITFReader(){