void OneDReader::recordPattern(Ref<BitArray> row,
                               int start,
                               vector<int>& counters) {
  int numCounters = counters.size();
  for (int i = 0; i < numCounters; i++) {
    counters[i] = 0;
  }
  int end = row->getSize();
  if (start >= end) {
    throw NotFoundException();
  }
  bool isWhite = !row->get(start);
  int counterPosition = 0;
  int i = start;
  while (i < end) {
    if (row->get(i) ^ isWhite) { // that is, exactly one is true
      counters[counterPosition]++;
    } else {
      counterPosition++;
      if (counterPosition == numCounters) {
        break;
      } else {
        counters[counterPosition] = 1;
        isWhite = !isWhite;
      }
    }
    i++;
  }
  // If we read fully the last section of pixels and filled up our counters -- or filled
  // the last counter but ran off the side of the image, OK. Otherwise, a problem.
  if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
    throw NotFoundException();
  }
}
Пример #2
0
void CodaBarReader::validatePattern(int start)  {
  // First, sum up the total size of our four categories of stripe sizes;
  vector<int> sizes (4, 0);
  vector<int> counts (4, 0);
  int end = decodeRowResult.length() - 1;

  // We break out of this loop in the middle, in order to handle
  // inter-character spaces properly.
  int pos = start;
  for (int i = 0; true; i++) {
    int pattern = CHARACTER_ENCODINGS[(int)decodeRowResult[i]];
    for (int j = 6; j >= 0; j--) {
      // Even j = bars, while odd j = spaces. Categories 2 and 3 are for
      // long stripes, while 0 and 1 are for short stripes.
      int category = (j & 1) + (pattern & 1) * 2;
      sizes[category] += counters[pos + j];
      counts[category]++;
      pattern >>= 1;
    }
    if (i >= end) {
      break;
    }
    // We ignore the inter-character space - it could be of any size.
    pos += 8;
  }

  // Calculate our allowable size thresholds using fixed-point math.
  vector<int> maxes (4, 0);
  vector<int> mins (4, 0);
  // Define the threshold of acceptability to be the midpoint between the
  // average small stripe and the average large stripe. No stripe lengths
  // should be on the "wrong" side of that line.
  for (int i = 0; i < 2; i++) {
    mins[i] = 0;  // Accept arbitrarily small "short" stripes.
    mins[i + 2] = ((sizes[i] << INTEGER_MATH_SHIFT) / counts[i] +
                   (sizes[i + 2] << INTEGER_MATH_SHIFT) / counts[i + 2]) >> 1;
    maxes[i] = mins[i + 2];
    maxes[i + 2] = (sizes[i + 2] * MAX_ACCEPTABLE + PADDING) / counts[i + 2];
  }

  // Now verify that all of the stripes are within the thresholds.
  pos = start;
  for (int i = 0; true; i++) {
    int pattern = CHARACTER_ENCODINGS[(int)decodeRowResult[i]];
    for (int j = 6; j >= 0; j--) {
      // Even j = bars, while odd j = spaces. Categories 2 and 3 are for
      // long stripes, while 0 and 1 are for short stripes.
      int category = (j & 1) + (pattern & 1) * 2;
      int size = counters[pos + j] << INTEGER_MATH_SHIFT;
      if (size < mins[category] || size > maxes[category]) {
        throw NotFoundException();
      }
      pattern >>= 1;
    }
    if (i >= end) {
      break;
    }
    pos += 8;
  }
}
Пример #3
0
Ref<Result> UPCEANReader::decodeRow(int rowNumber,
                                    Ref<BitArray> row,
                                    Range const& startGuardRange) {
  string& result = decodeRowStringBuffer;
  result.clear();
  int endStart = decodeMiddle(row, startGuardRange, result);

  Range endRange = decodeEnd(row, endStart);

  // Make sure there is a quiet zone at least as big as the end pattern after the barcode.
  // The spec might want more whitespace, but in practice this is the maximum we can count on.

  int end = endRange[1];
  int quietEnd = end + (end - endRange[0]);
  if (quietEnd >= row->getSize() || !row->isRange(end, quietEnd, false)) {
    throw NotFoundException();
  }

  Ref<String> resultString (new String(result));
  if (!checkChecksum(resultString)) {
    throw ChecksumException();
  }
  
  float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f;
  float right = (float) (endRange[1] + endRange[0]) / 2.0f;
  BarcodeFormat format = getBarcodeFormat();
  ArrayRef< Ref<ResultPoint> > resultPoints(2);
  resultPoints[0] = Ref<ResultPoint>(new OneDResultPoint(left, (float) rowNumber));
  resultPoints[1] = Ref<ResultPoint>(new OneDResultPoint(right, (float) rowNumber));
  Ref<Result> decodeResult (new Result(resultString, ArrayRef<char>(), resultPoints, format));
  // Java extension and man stuff
  return decodeResult;
}
Пример #4
0
char Code93Reader::patternToChar(int pattern)  {
  for (int i = 0; i < CHARACTER_ENCODINGS_LENGTH; i++) {
    if (CHARACTER_ENCODINGS[i] == pattern) {
      return ALPHABET[i];
    }
  }
  throw NotFoundException();
}
Пример #5
0
/**
 * Skip all whitespace until we get to the first black line.
 *
 * @param row row of black/white values to search
 * @return index of the first black line.
 * @throws ReaderException Throws exception if no black lines are found in the row
 */
int ITFReader::skipWhiteSpace(Ref<BitArray> row) {
	int width = row->getSize();
	int endStart = row->getNextSet(0);
	if (endStart == width) {
		throw NotFoundException();
	}
	return endStart;
}
Пример #6
0
/**
 * The start & end patterns must be pre/post fixed by a quiet zone. This
 * zone must be at least 10 times the width of a narrow line.  Scan back until
 * we either get to the start of the barcode or match the necessary number of
 * quiet zone pixels.
 *
 * Note: Its assumed the row is reversed when using this method to find
 * quiet zone after the end pattern.
 *
 * ref: http://www.barcode-1.net/i25code.html
 *
 * @param row bit array representing the scanned barcode.
 * @param startPattern index into row of the start or end pattern.
 * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown.
 */
void ITFReader::validateQuietZone(Ref<BitArray> row, int startPattern) {
	int quietCount = this->narrowLineWidth * 10; // expect to find this many pixels of quiet zone

	for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
		if (row->get(i)) {
			break;
		}
		quietCount--;
	}
	if (quietCount != 0) {
		// Unable to find the necessary number of quiet zone pixels.
		throw NotFoundException();
	}
}
Пример #7
0
vector<int> Code128Reader::findStartPattern(Ref<BitArray> row){
  int width = row->getSize();
  int rowOffset = row->getNextSet(0);

  int counterPosition = 0;
  vector<int> counters (6, 0);
  int patternStart = rowOffset;
  bool isWhite = false;
  int patternLength =  counters.size();

  for (int i = rowOffset; i < width; i++) {
    if (row->get(i) ^ isWhite) {
      counters[counterPosition]++;
    } else {
      if (counterPosition == patternLength - 1) {
        int bestVariance = MAX_AVG_VARIANCE;
        int bestMatch = -1;
        for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
          int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);
          if (variance < bestVariance) {
            bestVariance = variance;
            bestMatch = startCode;
          }
        }
        // Look for whitespace before start pattern, >= 50% of width of start pattern
        if (bestMatch >= 0 &&
            row->isRange(std::max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
          vector<int> resultValue (3, 0);
          resultValue[0] = patternStart;
          resultValue[1] = i;
          resultValue[2] = bestMatch;
          return resultValue;
        }
        patternStart += counters[0] + counters[1];
        for (int y = 2; y < patternLength; y++) {
          counters[y - 2] = counters[y];
        }
        counters[patternLength - 2] = 0;
        counters[patternLength - 1] = 0;
        counterPosition--;
      } else {
        counterPosition++;
      }
      counters[counterPosition] = 1;
      isWhite = !isWhite;
    }
  }
  throw NotFoundException();
}
Пример #8
0
int CodaBarReader::findStartPattern() {
  for (int i = 1; i < counterLength; i += 2) {
    int charOffset = toNarrowWidePattern(i);
    if (charOffset != -1 && arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
      // Look for whitespace before start pattern, >= 50% of width of start pattern
      // We make an exception if the whitespace is the first element.
      int patternSize = 0;
      for (int j = i; j < i + 7; j++) {
        patternSize += counters[j];
      }
      if (i == 1 || counters[i-1] >= patternSize / 2) {
        return i;
      }
    }
  }
  throw NotFoundException();
}
Пример #9
0
Ref<Result> MultiFormatUPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row) {
  // Compute this location once and reuse it on multiple implementations
  UPCEANReader::Range startGuardPattern = UPCEANReader::findStartGuardPattern(row);
  for (int i = 0, e = readers.size(); i < e; i++) {
    Ref<UPCEANReader> reader = readers[i];
    Ref<Result> result;
    try {
      result = reader->decodeRow(rowNumber, row, startGuardPattern);
    } catch (ReaderException const& ignored) {
      continue;
    }

    // Special case: a 12-digit code encoded in UPC-A is identical
    // to a "0" followed by those 12 digits encoded as EAN-13. Each
    // will recognize such a code, UPC-A as a 12-digit string and
    // EAN-13 as a 13-digit string starting with "0".  Individually
    // these are correct and their readers will both read such a
    // code and correctly call it EAN-13, or UPC-A, respectively.
    //
    // In this case, if we've been looking for both types, we'd like
    // to call it a UPC-A code. But for efficiency we only run the
    // EAN-13 decoder to also read UPC-A. So we special case it
    // here, and convert an EAN-13 result to a UPC-A result if
    // appropriate.
    bool ean13MayBeUPCA =
      result->getBarcodeFormat() == BarcodeFormat::EAN_13 &&
      result->getText()->charAt(0) == '0';

    // Note: doesn't match Java which uses hints

    bool canReturnUPCA = true;

    if (ean13MayBeUPCA && canReturnUPCA) {
      // Transfer the metdata across
      Ref<Result> resultUPCA (new Result(result->getText()->substring(1),
                                         result->getRawBytes(),
                                         result->getResultPoints(),
                                         BarcodeFormat::UPC_A));
      // needs java metadata stuff
      return resultUPCA;
    }
    return result;
  }

  throw NotFoundException();
}
Пример #10
0
UPCEANReader::Range UPCEANReader::findGuardPattern(Ref<BitArray> row,
                                                   int rowOffset,
                                                   bool whiteFirst,
                                                   vector<int> const& pattern,
                                                   vector<int>& counters) {
  // cerr << "fGP " << rowOffset  << " " << whiteFirst << endl;
  if (false) {
    for(int i=0; i < (int)pattern.size(); ++i) {
      std::cerr << pattern[i];
    }
    std::cerr << std::endl;
  }
  int patternLength = (int)pattern.size();
  int width = row->getSize();
  bool isWhite = whiteFirst;
  rowOffset = whiteFirst ? row->getNextUnset(rowOffset) : row->getNextSet(rowOffset);
  int counterPosition = 0;
  int patternStart = rowOffset;
  for (int x = rowOffset; x < width; x++) {
    // std::cerr << "rg " << x << " " << row->get(x) << std::endl;
    if (row->get(x) ^ isWhite) {
      counters[counterPosition]++;
    } else {
      if (counterPosition == patternLength - 1) {
        if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
          return Range(patternStart, x);
        }
        patternStart += counters[0] + counters[1];
        for (int y = 2; y < patternLength; y++) {
          counters[y - 2] = counters[y];
        }
        counters[patternLength - 2] = 0;
        counters[patternLength - 1] = 0;
        counterPosition--;
      } else {
        counterPosition++;
      }
      counters[counterPosition] = 1;
      isWhite = !isWhite;
    }
  }
  throw NotFoundException();
}
Пример #11
0
/**
 * Attempts to decode a sequence of ITF black/white lines into single
 * digit.
 *
 * @param counters the counts of runs of observed black/white/black/... values
 * @return The decoded digit
 * @throws ReaderException if digit cannot be decoded
 */
int ITFReader::decodeDigit(vector<int>& counters){

  int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
  int bestMatch = -1;
  int max = sizeof(PATTERNS)/sizeof(PATTERNS[0]);
  for (int i = 0; i < max; i++) {
    int const* pattern = PATTERNS[i];
    int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
    if (variance < bestVariance) {
      bestVariance = variance;
      bestMatch = i;
    }
  }
  if (bestMatch >= 0) {
    return bestMatch;
  } else {
    throw NotFoundException();
  }
}
Пример #12
0
int Code128Reader::decodeCode(Ref<BitArray> row, vector<int>& counters, int rowOffset) {
  recordPattern(row, rowOffset, counters);
  int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
  int bestMatch = -1;
  for (int d = 0; d < CODE_PATTERNS_LENGTH; d++) {
    int const* const pattern = CODE_PATTERNS[d];
    int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
    if (variance < bestVariance) {
      bestVariance = variance;
      bestMatch = d;
    }
  }
  // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.
  if (bestMatch >= 0) {
    return bestMatch;
  } else {
    throw NotFoundException();
  }
}
Пример #13
0
/**
 * Records the size of all runs of white and black pixels, starting with white.
 * This is just like recordPattern, except it records all the counters, and
 * uses our builtin "counters" member for storage.
 * @param row row to count from
 */
void CodaBarReader::setCounters(Ref<BitArray> row)  {
  counterLength = 0;
  // Start from the first white bit.
  int i = row->getNextUnset(0);
  int end = row->getSize();
  if (i >= end) {
    throw NotFoundException();
  }
  bool isWhite = true;
  int count = 0;
  for (; i < end; i++) {
    if (row->get(i) ^ isWhite) { // that is, exactly one is true
      count++;
    } else {
      counterAppend(count);
      count = 1;
      isWhite = !isWhite;
    }
  }
  counterAppend(count);
}
Пример #14
0
Code93Reader::Range Code93Reader::findAsteriskPattern(Ref<BitArray> row)  {
  int width = row->getSize();
  int rowOffset = row->getNextSet(0);

  { // Arrays.fill(counters, 0);
    int size = counters.size();
    counters.resize(0);
    counters.resize(size); }
  vector<int>& theCounters (counters);

  int patternStart = rowOffset;
  bool isWhite = false;
  int patternLength = theCounters.size();

  int counterPosition = 0;
  for (int i = rowOffset; i < width; i++) {
    if (row->get(i) ^ isWhite) {
      theCounters[counterPosition]++;
    } else {
      if (counterPosition == patternLength - 1) {
        if (toPattern(theCounters) == ASTERISK_ENCODING) {
          return Range(patternStart, i);
        }
        patternStart += theCounters[0] + theCounters[1];
        for (int y = 2; y < patternLength; y++) {
          theCounters[y - 2] = theCounters[y];
        }
        theCounters[patternLength - 2] = 0;
        theCounters[patternLength - 1] = 0;
        counterPosition--;
      } else {
        counterPosition++;
      }
      theCounters[counterPosition] = 1;
      isWhite = !isWhite;
    }
  }
  throw NotFoundException();
}
Пример #15
0
int UPCEANReader::decodeDigit(Ref<BitArray> row,
                              vector<int> & counters,
                              int rowOffset,
                              vector<int const*> const& patterns) {
  recordPattern(row, rowOffset, counters);
  int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
  int bestMatch = -1;
  int max = (int)patterns.size();
  for (int i = 0; i < max; i++) {
    int const* pattern (patterns[i]);
    int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
    if (variance < bestVariance) {
      bestVariance = variance;
      bestMatch = i;
    }
  }
  if (bestMatch >= 0) {
    return bestMatch;
  } else {
    throw NotFoundException();
  }
}
Пример #16
0
/**
 * @param row       row of black/white values to search
 * @param rowOffset position to start search
 * @param pattern   pattern of counts of number of black and white pixels that are
 *                  being searched for as a pattern
 * @return start/end horizontal offset of guard pattern, as an array of two
 *         ints
 * @throws ReaderException if pattern is not found
 */
ITFReader::Range ITFReader::findGuardPattern(Ref<BitArray> row, int rowOffset,
		vector<int> const& pattern) {
	// TODO: This is very similar to implementation in UPCEANReader. Consider if they can be
	// merged to a single method.
	int patternLength = pattern.size();
	vector<int> counters(patternLength);
	int width = row->getSize();
	bool isWhite = false;

	int counterPosition = 0;
	int patternStart = rowOffset;
	for (int x = rowOffset; x < width; x++) {
		if (row->get(x) ^ isWhite) {
			counters[counterPosition]++;
		} else {
			if (counterPosition == patternLength - 1) {
				if (patternMatchVariance(counters, &pattern[0],
						MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
					return Range(patternStart, x);
				}
				patternStart += counters[0] + counters[1];
				for (int y = 2; y < patternLength; y++) {
					counters[y - 2] = counters[y];
				}
				counters[patternLength - 2] = 0;
				counters[patternLength - 1] = 0;
				counterPosition--;
			} else {
				counterPosition++;
			}
			counters[counterPosition] = 1;
			isWhite = !isWhite;
		}
	}
	throw NotFoundException();
}
Ref<Result> OneDReader::doDecode(Ref<BinaryBitmap> image, DecodeHints hints) {
  int width = image->getWidth();
  int height = image->getHeight();
  Ref<BitArray> row(new BitArray(width));

  int middle = height >> 1;
  bool tryHarder = hints.getTryHarder();
  int rowStep = std::max(1, height >> (tryHarder ? 8 : 5));
  using namespace std;
  // cerr << "rS " << rowStep << " " << height << " " << tryHarder << endl;
  int maxLines;
  if (tryHarder) {
    maxLines = height; // Look at the whole image, not just the center
  } else {
    maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image
  }

  for (int x = 0; x < maxLines; x++) {

    // Scanning from the middle out. Determine which row we're looking at next:
    int rowStepsAboveOrBelow = (x + 1) >> 1;
    bool isAbove = (x & 0x01) == 0; // i.e. is x even?
    int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
    if (false) {
      std::cerr << "rN "
                << rowNumber << " "
                << height << " "
                << middle << " "
                << rowStep << " "
                << isAbove << " "
                << rowStepsAboveOrBelow
                << std::endl;
    }
    if (rowNumber < 0 || rowNumber >= height) {
      // Oops, if we run off the top or bottom, stop
      break;
    }

    // Estimate black point for this row and load it:
    try {
      row = image->getBlackRow(rowNumber, row);
    } catch (NotFoundException const& ignored) {
      (void)ignored;
      continue;
    }

    // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
    // handle decoding upside down barcodes.
    for (int attempt = 0; attempt < 2; attempt++) {
      if (attempt == 1) {
        row->reverse(); // reverse the row and continue
      }

      // Java hints stuff missing

      try {
        // Look for a barcode
        // std::cerr << "rn " << rowNumber << " " << typeid(*this).name() << std::endl;
        Ref<Result> result = decodeRow(rowNumber, row);
        // We found our barcode
        if (attempt == 1) {
          // But it was upside down, so note that
          // result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
          // And remember to flip the result points horizontally.
          ArrayRef< Ref<ResultPoint> > points(result->getResultPoints());
          if (points) {
            points[0] = Ref<ResultPoint>(new OneDResultPoint(width - points[0]->getX() - 1,
                                                             points[0]->getY()));
            points[1] = Ref<ResultPoint>(new OneDResultPoint(width - points[1]->getX() - 1,
                                                             points[1]->getY()));
            
          }
        }
        return result;
      } catch (ReaderException const& re) {
        (void)re;
        continue;
      }
    }
  }
  throw NotFoundException();
}
Пример #18
0
Ref<Result> CodaBarReader::decodeRow(int rowNumber, Ref<BitArray> row) {

  { // Arrays.fill(counters, 0);
    int size = counters.size();
    counters.resize(0);
    counters.resize(size); }

  setCounters(row);
  int startOffset = findStartPattern();
  int nextStart = startOffset;

  decodeRowResult.clear();
  do {
    int charOffset = toNarrowWidePattern(nextStart);
    if (charOffset == -1) {
      throw NotFoundException();
    }
    // Hack: We store the position in the alphabet table into a
    // StringBuilder, so that we can access the decoded patterns in
    // validatePattern. We'll translate to the actual characters later.
    decodeRowResult.append(1, (char)charOffset);
    nextStart += 8;
    // Stop as soon as we see the end character.
    if (decodeRowResult.length() > 1 &&
        arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
      break;
    }
  } while (nextStart < counterLength); // no fixed end pattern so keep on reading while data is available

  // Look for whitespace after pattern:
  int trailingWhitespace = counters[nextStart - 1];
  int lastPatternSize = 0;
  for (int i = -8; i < -1; i++) {
    lastPatternSize += counters[nextStart + i];
  }

  // We need to see whitespace equal to 50% of the last pattern size,
  // otherwise this is probably a false positive. The exception is if we are
  // at the end of the row. (I.e. the barcode barely fits.)
  if (nextStart < counterLength && trailingWhitespace < lastPatternSize / 2) {
    throw NotFoundException();
  }

  validatePattern(startOffset);

  // Translate character table offsets to actual characters.
  for (int i = 0; i < (int)decodeRowResult.length(); i++) {
    decodeRowResult[i] = ALPHABET[(int)decodeRowResult[i]];
  }
  // Ensure a valid start and end character
  char startchar = decodeRowResult[0];
  if (!arrayContains(STARTEND_ENCODING, startchar)) {
    throw NotFoundException();
  }
  char endchar = decodeRowResult[decodeRowResult.length() - 1];
  if (!arrayContains(STARTEND_ENCODING, endchar)) {
    throw NotFoundException();
  }

  // remove stop/start characters character and check if a long enough string is contained
  if ((int)decodeRowResult.length() <= MIN_CHARACTER_LENGTH) {
    // Almost surely a false positive ( start + stop + at least 1 character)
    throw NotFoundException();
  }

  decodeRowResult.erase(decodeRowResult.length() - 1, 1);
  decodeRowResult.erase(0, 1);

  int runningCount = 0;
  for (int i = 0; i < startOffset; i++) {
    runningCount += counters[i];
  }
  float left = (float) runningCount;
  for (int i = startOffset; i < nextStart - 1; i++) {
    runningCount += counters[i];
  }
  float right = (float) runningCount;

  ArrayRef< Ref<ResultPoint> > resultPoints(2);
  resultPoints[0] =
    Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
  resultPoints[1] =
    Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));

  return Ref<Result>(new Result(Ref<String>(new String(decodeRowResult)),
                                ArrayRef<char>(),
                                resultPoints,
                                BarcodeFormat::CODABAR));
}
Пример #19
0
Ref<Result> Code93Reader::decodeRow(int rowNumber, Ref<BitArray> row, zxing::DecodeHints /*hints*/) {
  Range start (findAsteriskPattern(row));
  // Read off white space    
  int nextStart = row->getNextSet(start[1]);
  int end = row->getSize();

  vector<int>& theCounters (counters);
  { // Arrays.fill(counters, 0);
    int size = theCounters.size();
    theCounters.resize(0);
    theCounters.resize(size); }
  string& result (decodeRowResult);
  result.clear();

  char decodedChar;
  int lastStart;
  do {
    recordPattern(row, nextStart, theCounters);
    int pattern = toPattern(theCounters);
    if (pattern < 0) {
      throw NotFoundException();
    }
    decodedChar = patternToChar(pattern);
    result.append(1, decodedChar);
    lastStart = nextStart;
    for(int i=0, e=theCounters.size(); i < e; ++i) {
      nextStart += theCounters[i];
    }
    // Read off white space
    nextStart = row->getNextSet(nextStart);
  } while (decodedChar != '*');
  result.resize(result.length() - 1); // remove asterisk

  // Look for whitespace after pattern:
  int lastPatternSize = 0;
  for (int i = 0, e = theCounters.size(); i < e; i++) {
    lastPatternSize += theCounters[i];
  }
  
  // Should be at least one more black module
  if (nextStart == end || !row->get(nextStart)) {
    throw NotFoundException();
  }

  if (result.length() < 2) {
    // false positive -- need at least 2 checksum digits
    throw NotFoundException();
  }

  checkChecksums(result);
  // Remove checksum digits
  result.resize(result.length() - 2);

  Ref<String> resultString = decodeExtended(result);

  float left = (float) (start[1] + start[0]) / 2.0f;
  float right = lastStart + lastPatternSize / 2.0f;

  ArrayRef< Ref<ResultPoint> > resultPoints (2);
  resultPoints[0] = 
    Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
  resultPoints[1] =
    Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));
  
  return Ref<Result>(new Result(
                       resultString,
                       ArrayRef<byte>(),
                       resultPoints,
                       BarcodeFormat::CODE_93));
}
Пример #20
0
Ref<Result> Code128Reader::decodeRow(int rowNumber, Ref<BitArray> row) {
  // boolean convertFNC1 = hints != null && hints.containsKey(DecodeHintType.ASSUME_GS1);
  boolean convertFNC1 = false;
  vector<int> startPatternInfo (findStartPattern(row));
  int startCode = startPatternInfo[2];
  int codeSet;
  switch (startCode) {
    case CODE_START_A:
      codeSet = CODE_CODE_A;
      break;
    case CODE_START_B:
      codeSet = CODE_CODE_B;
      break;
    case CODE_START_C:
      codeSet = CODE_CODE_C;
      break;
    default:
      throw FormatException();
  }

  bool done = false;
  bool isNextShifted = false;

  string result;
  vector<char> rawCodes(20, 0);

  int lastStart = startPatternInfo[0];
  int nextStart = startPatternInfo[1];
  vector<int> counters (6, 0);

  int lastCode = 0;
  int code = 0;
  int checksumTotal = startCode;
  int multiplier = 0;
  bool lastCharacterWasPrintable = true;

  std::ostringstream oss;

  while (!done) {

    bool unshift = isNextShifted;
    isNextShifted = false;

    // Save off last code
    lastCode = code;

    code = decodeCode(row, counters, nextStart);

    // Remember whether the last code was printable or not (excluding CODE_STOP)
    if (code != CODE_STOP) {
      lastCharacterWasPrintable = true;
    }

    // Add to checksum computation (if not CODE_STOP of course)
    if (code != CODE_STOP) {
      multiplier++;
      checksumTotal += multiplier * code;
    }

    // Advance to where the next code will to start
    lastStart = nextStart;
    for (int i = 0, e = counters.size(); i < e; i++) {
      nextStart += counters[i];
    }

    // Take care of illegal start codes
    switch (code) {
      case CODE_START_A:
      case CODE_START_B:
      case CODE_START_C:
        throw FormatException();
    }

    switch (codeSet) {

      case CODE_CODE_A:
        if (code < 64) {
          result.append(1, (char) (' ' + code));
        } else if (code < 96) {
          result.append(1, (char) (code - 64));
        } else {
          // Don't let CODE_STOP, which always appears, affect whether whether we think the
          // last code was printable or not.
          if (code != CODE_STOP) {
            lastCharacterWasPrintable = false;
          }
          switch (code) {
            case CODE_FNC_1:
              if (convertFNC1) {
                if (result.length() == 0){
                  // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code
                  // is FNC1 then this is GS1-128. We add the symbology identifier.
                  result.append("]C1");
                } else {
                  // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)
                  result.append(1, (char) 29);
                }
              }
              break;
            case CODE_FNC_2:
            case CODE_FNC_3:
            case CODE_FNC_4_A:
              // do nothing?
              break;
            case CODE_SHIFT:
              isNextShifted = true;
              codeSet = CODE_CODE_B;
              break;
            case CODE_CODE_B:
              codeSet = CODE_CODE_B;
              break;
            case CODE_CODE_C:
              codeSet = CODE_CODE_C;
              break;
            case CODE_STOP:
              done = true;
              break;
          }
        }
        break;
      case CODE_CODE_B:
        if (code < 96) {
          result.append(1, (char) (' ' + code));
        } else {
          if (code != CODE_STOP) {
            lastCharacterWasPrintable = false;
          }
          switch (code) {
            case CODE_FNC_1:
            case CODE_FNC_2:
            case CODE_FNC_3:
            case CODE_FNC_4_B:
              // do nothing?
              break;
            case CODE_SHIFT:
              isNextShifted = true;
              codeSet = CODE_CODE_A;
              break;
            case CODE_CODE_A:
              codeSet = CODE_CODE_A;
              break;
            case CODE_CODE_C:
              codeSet = CODE_CODE_C;
              break;
            case CODE_STOP:
              done = true;
              break;
          }
        }
        break;
      case CODE_CODE_C:
        if (code < 100) {
          if (code < 10) {
            result.append(1, '0');
          }
          oss.clear();
          oss.str("");
          oss << code;
          result.append(oss.str());
        } else {
          if (code != CODE_STOP) {
            lastCharacterWasPrintable = false;
          }
          switch (code) {
            case CODE_FNC_1:
              // do nothing?
              break;
            case CODE_CODE_A:
              codeSet = CODE_CODE_A;
              break;
            case CODE_CODE_B:
              codeSet = CODE_CODE_B;
              break;
            case CODE_STOP:
              done = true;
              break;
          }
        }
        break;
    }

    // Unshift back to another code set if we were shifted
    if (unshift) {
      codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A;
    }
    
  }

  // Check for ample whitespace following pattern, but, to do this we first need to remember that
  // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left
  // to read off. Would be slightly better to properly read. Here we just skip it:
  nextStart = row->getNextUnset(nextStart);
  if (!row->isRange(nextStart,
                    std::min(row->getSize(), nextStart + (nextStart - lastStart) / 2),
                    false)) {
    throw NotFoundException();
  }

  // Pull out from sum the value of the penultimate check code
  checksumTotal -= multiplier * lastCode;
  // lastCode is the checksum then:
  if (checksumTotal % 103 != lastCode) {
    throw ChecksumException();
  }

  // Need to pull out the check digits from string
  int resultLength = result.length();
  if (resultLength == 0) {
    // false positive
    throw NotFoundException();
  }

  // Only bother if the result had at least one character, and if the checksum digit happened to
  // be a printable character. If it was just interpreted as a control code, nothing to remove.
  if (resultLength > 0 && lastCharacterWasPrintable) {
    if (codeSet == CODE_CODE_C) {
      result.erase(resultLength - 2, resultLength);
    } else {
      result.erase(resultLength - 1, resultLength);
    }
  }

  float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;
  float right = (float) (nextStart + lastStart) / 2.0f;

  int rawCodesSize = rawCodes.size();
  ArrayRef<char> rawBytes (rawCodesSize);
  for (int i = 0; i < rawCodesSize; i++) {
    rawBytes[i] = rawCodes[i];
  }

  ArrayRef< Ref<ResultPoint> > resultPoints(2);
  resultPoints[0] =
      Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
  resultPoints[1] =
      Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));

  return Ref<Result>(new Result(Ref<String>(new String(result)), rawBytes, resultPoints,
                                BarcodeFormat::CODE_128));
}
Пример #21
0
Ref<DetectorResult> Detector::detect() {
    Ref<WhiteRectangleDetector> rectangleDetector_(new WhiteRectangleDetector(image_));
    std::vector<Ref<ResultPoint> > ResultPoints = rectangleDetector_->detect();
    Ref<ResultPoint> pointA = ResultPoints[0];
    Ref<ResultPoint> pointB = ResultPoints[1];
    Ref<ResultPoint> pointC = ResultPoints[2];
    Ref<ResultPoint> pointD = ResultPoints[3];

    // Point A and D are across the diagonal from one another,
    // as are B and C. Figure out which are the solid black lines
    // by counting transitions
    std::vector<Ref<ResultPointsAndTransitions> > transitions(4);
    transitions[0].reset(transitionsBetween(pointA, pointB));
    transitions[1].reset(transitionsBetween(pointA, pointC));
    transitions[2].reset(transitionsBetween(pointB, pointD));
    transitions[3].reset(transitionsBetween(pointC, pointD));
    insertionSort(transitions);

    // Sort by number of transitions. First two will be the two solid sides; last two
    // will be the two alternating black/white sides
    Ref<ResultPointsAndTransitions> lSideOne(transitions[0]);
    Ref<ResultPointsAndTransitions> lSideTwo(transitions[1]);

    // Figure out which point is their intersection by tallying up the number of times we see the
    // endpoints in the four endpoints. One will show up twice.
    typedef std::map<Ref<ResultPoint>, int> PointMap;
    PointMap pointCount;
    increment(pointCount, lSideOne->getFrom());
    increment(pointCount, lSideOne->getTo());
    increment(pointCount, lSideTwo->getFrom());
    increment(pointCount, lSideTwo->getTo());

    // Figure out which point is their intersection by tallying up the number of times we see the
    // endpoints in the four endpoints. One will show up twice.
    Ref<ResultPoint> maybeTopLeft;
    Ref<ResultPoint> bottomLeft;
    Ref<ResultPoint> maybeBottomRight;
    for (PointMap::const_iterator entry = pointCount.begin(), end = pointCount.end(); entry != end; ++entry) {
        Ref<ResultPoint> const& point = entry->first;
        int value = entry->second;
        if (value == 2) {
            bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides
        } else {
            // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now
            if (maybeTopLeft == 0) {
                maybeTopLeft = point;
            } else {
                maybeBottomRight = point;
            }
        }
    }

    if (maybeTopLeft == 0 || bottomLeft == 0 || maybeBottomRight == 0) {
        throw NotFoundException();
    }

    // Bottom left is correct but top left and bottom right might be switched
    std::vector<Ref<ResultPoint> > corners(3);
    corners[0].reset(maybeTopLeft);
    corners[1].reset(bottomLeft);
    corners[2].reset(maybeBottomRight);

    // Use the dot product trick to sort them out
    ResultPoint::orderBestPatterns(corners);

    // Now we know which is which:
    Ref<ResultPoint> bottomRight(corners[0]);
    bottomLeft = corners[1];
    Ref<ResultPoint> topLeft(corners[2]);

    // Which point didn't we find in relation to the "L" sides? that's the top right corner
    Ref<ResultPoint> topRight;
    if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) {
        topRight = pointA;
    } else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft)
                 || pointB->equals(topLeft))) {
        topRight = pointB;
    } else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft)
                 || pointC->equals(topLeft))) {
        topRight = pointC;
    } else {
        topRight = pointD;
    }

    // Next determine the dimension by tracing along the top or right side and counting black/white
    // transitions. Since we start inside a black module, we should see a number of transitions
    // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
    // end on a black module:

    // The top right point is actually the corner of a module, which is one of the two black modules
    // adjacent to the white module at the top right. Tracing to that corner from either the top left
    // or bottom right should work here.

    int dimensionTop = transitionsBetween(topLeft, topRight)->getTransitions();
    int dimensionRight = transitionsBetween(bottomRight, topRight)->getTransitions();

    //dimensionTop++;
    if ((dimensionTop & 0x01) == 1) {
        // it can't be odd, so, round... up?
        dimensionTop++;
    }
    dimensionTop += 2;

    //dimensionRight++;
    if ((dimensionRight & 0x01) == 1) {
        // it can't be odd, so, round... up?
        dimensionRight++;
    }
    dimensionRight += 2;

    Ref<BitMatrix> bits;
    Ref<PerspectiveTransform> transform;
    Ref<ResultPoint> correctedTopRight;


    // Rectanguar symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more
    // than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as
    // rectangular if the bigger side is at least 7/4 times the other:
    if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
        // The matrix is rectangular
        correctedTopRight = correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight,
                            dimensionTop, dimensionRight);
        if (correctedTopRight == NULL) {
            correctedTopRight = topRight;
        }

        dimensionTop = transitionsBetween(topLeft, correctedTopRight)->getTransitions();
        dimensionRight = transitionsBetween(bottomRight, correctedTopRight)->getTransitions();

        if ((dimensionTop & 0x01) == 1) {
            // it can't be odd, so, round... up?
            dimensionTop++;
        }

        if ((dimensionRight & 0x01) == 1) {
            // it can't be odd, so, round... up?
            dimensionRight++;
        }

        transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight, dimensionTop,
                                    dimensionRight);
        bits = sampleGrid(image_, dimensionTop, dimensionRight, transform);

    } else {
        // The matrix is square
        int dimension = min(dimensionRight, dimensionTop);

        // correct top right point to match the white module
        correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
        if (correctedTopRight == NULL) {
            correctedTopRight = topRight;
        }

        // Redetermine the dimension using the corrected top right point
        int dimensionCorrected = std::max(transitionsBetween(topLeft, correctedTopRight)->getTransitions(),
                                          transitionsBetween(bottomRight, correctedTopRight)->getTransitions());
        dimensionCorrected++;
        if ((dimensionCorrected & 0x01) == 1) {
            dimensionCorrected++;
        }

        transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight,
                                    dimensionCorrected, dimensionCorrected);
        bits = sampleGrid(image_, dimensionCorrected, dimensionCorrected, transform);
    }

    ArrayRef< Ref<ResultPoint> > points (new Array< Ref<ResultPoint> >(4));
    points[0].reset(topLeft);
    points[1].reset(bottomLeft);
    points[2].reset(correctedTopRight);
    points[3].reset(bottomRight);
    Ref<DetectorResult> detectorResult(new DetectorResult(bits, points));
    return detectorResult;
}
Пример #22
0
void LinesSampler::linesMatrixToCodewords(vector<vector<int> >& clusterNumbers,
                                          const int symbolsPerLine,
                                          const vector<float>& symbolWidths,
                                          Ref<BitMatrix> linesMatrix,
                                          vector<vector<int> >& codewords)
{
  for (int y = 0; y < linesMatrix->getHeight(); y++) {
    // Not sure if this is the right way to handle this but avoids an error:
    if (symbolsPerLine > (int)symbolWidths.size()) {
      throw NotFoundException("Inconsistent number of symbols in this line.");
    }

    // TODO: use symbolWidths.size() instead of symbolsPerLine to at least decode some codewords

    codewords[y].resize(symbolsPerLine, 0);
    clusterNumbers[y].resize(symbolsPerLine, -1);
    int line = y;
    vector<int> barWidths(1, 0);
    int barCount = 0;
    // Runlength encode the bars in the scanned linesMatrix.
    // We assume that the first bar is black, as determined by the PDF417 standard.
    bool isSetBar = true;
    // Filter small white bars at the beginning of the barcode.
    // Small white bars may occur due to small deviations in scan line sampling.
    barWidths[0] += BARCODE_START_OFFSET;
    for (int x = BARCODE_START_OFFSET; x < linesMatrix->getWidth(); x++) {
      if (linesMatrix->get(x, line)) {
        if (!isSetBar) {
          isSetBar = true;
          barCount++;
          barWidths.resize(barWidths.size() + 1);
        }
      } else {
        if (isSetBar) {
          isSetBar = false;
          barCount++;
          barWidths.resize(barWidths.size() + 1);
        }

      }
      barWidths[barCount]++;
    }
    // Don't forget the last bar.
    barCount++;
    barWidths.resize(barWidths.size() + 1);

#if PDF417_DIAG && OUTPUT_BAR_WIDTH
    {
      for (int i = 0; i < barWidths.size(); i++) {
        cout << barWidths[i] << ", ";
      }
      cout << endl;
    }
#endif

    //////////////////////////////////////////////////

    // Find the symbols in the line by counting bar lengths until we reach symbolWidth.
    // We make sure, that the last bar of a symbol is always white, as determined by the PDF417 standard.
    // This helps to reduce the amount of errors done during the symbol recognition.
    // The symbolWidth usually is not constant over the width of the barcode.
    int cwWidth = 0;
    int cwCount = 0;
    vector<int> cwStarts(symbolsPerLine, 0);
    cwStarts[0] = 0;
    cwCount++;
    for (int i = 0; i < barCount && cwCount < symbolsPerLine; i++) {
      cwWidth += barWidths[i];
      if ((float)cwWidth > symbolWidths[cwCount - 1]) {
        if ((i % 2) == 1) { // check if bar is white
          i++;
        }
        cwWidth = barWidths[i];
        cwStarts[cwCount] = i;
        cwCount++;
      }
    }

#if PDF417_DIAG && OUTPUT_CW_STARTS
    {
      for (int i = 0; i < cwStarts.size(); i++) {
        cout << cwStarts[i] << ", ";
      }
      cout << endl;
    }
#endif

    ///////////////////////////////////////////

    vector<vector<float> > cwRatios(symbolsPerLine);
    // Distribute bar widths to modules of a codeword.
    for (int i = 0; i < symbolsPerLine; i++) {
      cwRatios[i].resize(BARS_IN_SYMBOL, 0.0f);
      const int cwStart = cwStarts[i];
      const int cwEnd = (i == symbolsPerLine - 1) ? barCount : cwStarts[i + 1];
      const int cwLength = cwEnd - cwStart;

      if (cwLength < 7 || cwLength > 9) {
        // We try to recover smybols with 7 or 9 bars and spaces with heuristics, but everything else is beyond repair.
        continue;
      }

      float cwWidth = 0;

      // For symbols with 9 bar length simply ignore the last bar.
      for (int j = 0; j < min(BARS_IN_SYMBOL, cwLength); ++j) {
        cwWidth += (float)barWidths[cwStart + j];
      }

      // If there were only 7 bars and spaces detected use the following heuristic:
      // Assume the length of the symbol is symbolWidth and the last (unrecognized) bar uses all remaining space.
      if (cwLength == 7) {
        for (int j = 0; j < cwLength; ++j) {
          cwRatios[i][j] = (float)barWidths[cwStart + j] / symbolWidths[i];
        }
        cwRatios[i][7] = (symbolWidths[i] - cwWidth) / symbolWidths[i];
      } else {
        for (int j = 0; j < (int)cwRatios[i].size(); ++j) {
          cwRatios[i][j] = (float)barWidths[cwStart + j] / cwWidth;
        }
      }

      float bestMatchError = std::numeric_limits<float>::max();
      int bestMatch = 0;

      // Search for the most possible codeword by comparing the ratios of bar size to symbol width.
      // The sum of the squared differences is used as similarity metric.
      // (Picture it as the square euclidian distance in the space of eight tuples where a tuple represents the bar ratios.)
      for (int j = 0; j < POSSIBLE_SYMBOLS; j++) {
        float error = 0.0f;
        for (int k = 0; k < BARS_IN_SYMBOL; k++) {
          float diff = RATIOS_TABLE[j * BARS_IN_SYMBOL + k] - cwRatios[i][k];
          error += diff * diff;
          if (error >= bestMatchError) {
            break;
          }
        }
        if (error < bestMatchError) {
          bestMatchError = error;
          bestMatch = BitMatrixParser::SYMBOL_TABLE[j];
        }
      }
      codewords[y][i] = bestMatch;
      clusterNumbers[y][i] = calculateClusterNumber(bestMatch);
    }
  }


#if PDF417_DIAG && OUTPUT_CLUSTER_NUMBERS
  {
    for (int i = 0; i < clusterNumbers.size(); i++) {
      for (int j = 0; j < clusterNumbers[i].size(); j++) {
        cout << clusterNumbers[i][j] << ", ";
      }
      cout << endl;
    }
  }
#endif


#if PDF417_DIAG
  {
    Ref<BitMatrix> bits(new BitMatrix(symbolsPerLine * MODULES_IN_SYMBOL, codewords.size()));
    codewordsToBitMatrix(codewords, bits);
    static int __cnt__ = 0;
    stringstream ss;
    ss << "pdf417-detectedRaw" << __cnt__++ << ".png";
    bits->writePng(ss.str().c_str(), 8, 16);
  }
#endif
}