CharacterSetECI* CharacterSetECI::getCharacterSetECIByValue(int value) {
  if (value < 0 || value >= 900) {
    std::ostringstream oss;
    oss << "Bad ECI value: " << value;
    throw IllegalArgumentException(oss.str().c_str());
  }
  return VALUE_TO_ECI[value];
}
示例#2
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;
}
示例#3
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();
}
示例#4
0
vector<Ref<GenericGFPoly> > ReedSolomonDecoder::runEuclideanAlgorithm(Ref<GenericGFPoly> a,
                                                                      Ref<GenericGFPoly> b,
                                                                      int R) {
  // Assume a's degree is >= b's
  if (a->getDegree() < b->getDegree()) {
    Ref<GenericGFPoly> tmp = a;
    a = b;
    b = tmp;
  }

  Ref<GenericGFPoly> rLast(a);
  Ref<GenericGFPoly> r(b);
  Ref<GenericGFPoly> tLast(field->getZero());
  Ref<GenericGFPoly> t(field->getOne());

  // Run Euclidean algorithm until r's degree is less than R/2
  while (r->getDegree() >= R / 2) {
    Ref<GenericGFPoly> rLastLast(rLast);
    Ref<GenericGFPoly> tLastLast(tLast);
    rLast = r;
    tLast = t;

    // Divide rLastLast by rLast, with quotient q and remainder r
    if (rLast->isZero()) {
      // Oops, Euclidean algorithm already terminated?
      throw ReedSolomonException("r_{i-1} was zero");
    }
    r = rLastLast;
    Ref<GenericGFPoly> q = field->getZero();
    int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree());
    int dltInverse = field->inverse(denominatorLeadingTerm);
    while (r->getDegree() >= rLast->getDegree() && !r->isZero()) {
      int degreeDiff = r->getDegree() - rLast->getDegree();
      int scale = field->multiply(r->getCoefficient(r->getDegree()), dltInverse);
      q = q->addOrSubtract(field->buildMonomial(degreeDiff, scale));
      r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale));
    }

    t = q->multiply(tLast)->addOrSubtract(tLastLast);

    if (r->getDegree() >= rLast->getDegree()) {
      throw IllegalStateException("Division algorithm failed to reduce polynomial?");
    }
  }

  int sigmaTildeAtZero = t->getCoefficient(0);
  if (sigmaTildeAtZero == 0) {
    throw ReedSolomonException("sigmaTilde(0) was zero");
  }

  int inverse = field->inverse(sigmaTildeAtZero);
  Ref<GenericGFPoly> sigma(t->multiply(inverse));
  Ref<GenericGFPoly> omega(r->multiply(inverse));
  vector<Ref<GenericGFPoly> > result(2);
  result[0] = sigma;
  result[1] = omega;
  return result;
}
示例#5
0
文件: ECI.cpp 项目: Android9001/zxing
ECI* ECI::getECIByValue(int value) {
  if (value < 0 || value > 999999) {
    throw IllegalArgumentException("Bad ECI value: " + value);
  }
  if (value < 900) { // Character set ECIs use 000000 - 000899
    return CharacterSetECI::getCharacterSetECIByValue(value);
  }
  return 0;
}
示例#6
0
Point LinesSampler::intersection(Line a, Line b) {
  float dxa = a.start.x - a.end.x;
  float dxb = b.start.x - b.end.x;
  float dya = a.start.y - a.end.y;
  float dyb = b.start.y - b.end.y;

  float p = a.start.x * a.end.y - a.start.y * a.end.x;
  float q = b.start.x * b.end.y - b.start.y * b.end.x;
  float denom = dxa * dyb - dya * dxb;
  if(abs(denom) < 1e-12)  // Lines don't intersect (replaces "denom == 0")
    return Point(std::numeric_limits<float>::infinity(),
                 std::numeric_limits<float>::infinity());

  float x = (p * dxb - dxa * q) / denom;
  float y = (p * dyb - dya * q) / denom;

  return Point(x, y);
}
示例#7
0
ECI* ECI::getECIByValue(int value) {
  if (value < 0 || value > 999999) {
    std::ostringstream oss;
    oss << "Bad ECI value: " << value;
    throw IllegalArgumentException(oss.str().c_str());
  }
  if (value < 900) { // Character set ECIs use 000000 - 000899
    return CharacterSetECI::getCharacterSetECIByValue(value);
  }
  return 0;
}
示例#8
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();
  }

  // UPC/EAN should never be less than 8 chars anyway
  if (result.length() < 8) {
    throw FormatException();
  }

  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;
}
示例#9
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();
	}
}
示例#10
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();
}
示例#11
0
void Code93Reader::checkOneChecksum(string const& result,
                                    int checkPosition,
                                    int weightMax) {
  int weight = 1;
  int total = 0;
  for (int i = checkPosition - 1; i >= 0; i--) {
    total += weight * ALPHABET_STRING.find_first_of(result[i]);
    if (++weight > weightMax) {
      weight = 1;
    }
  }
  if (result[checkPosition] != ALPHABET[total % 47]) {
    throw ChecksumException();
  }
}
示例#12
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();
}
示例#13
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();
}
示例#14
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();
}
示例#15
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();
  }
}
示例#16
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();
  }
}
示例#17
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);
}
示例#18
0
Ref<Result> ITFReader::decodeRow(int rowNumber, Ref<BitArray> row) {
	// Find out where the Middle section (payload) starts & ends

	Range startRange = decodeStart(row);
	Range endRange = decodeEnd(row);

	std::string result;
	decodeMiddle(row, startRange[1], endRange[0], result);
	Ref<String> resultString(new String(result));

	ArrayRef<int> allowedLengths;
	// Java hints stuff missing
	if (!allowedLengths) {
		allowedLengths = DEFAULT_ALLOWED_LENGTHS;
	}

	// To avoid false positives with 2D barcodes (and other patterns), make
	// an assumption that the decoded string must be 6, 10 or 14 digits.
	int length = resultString->size();
	bool lengthOK = false;
	for (int i = 0, e = allowedLengths->size(); i < e; i++) {
		if (length == allowedLengths[i]) {
			lengthOK = true;
			break;
		}
	}

	if (!lengthOK) {
		throw FormatException();
	}

	ArrayRef<Ref<ResultPoint> > resultPoints(2);
	resultPoints[0] = Ref<OneDResultPoint>(
			new OneDResultPoint(float(startRange[1]), float(rowNumber)));
	resultPoints[1] = Ref<OneDResultPoint>(
			new OneDResultPoint(float(endRange[0]), float(rowNumber)));
	return Ref<Result>(
			new Result(resultString, ArrayRef<char>(), resultPoints,
					BarcodeFormat::ITF));
}
示例#19
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();
  }
}
示例#20
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();
}
示例#21
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();
}
示例#22
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));
}
示例#23
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));
}
vector<vector<Ref<FinderPattern> > > MultiFinderPatternFinder::selectBestPatterns(){
  vector<Ref<FinderPattern> > possibleCenters = possibleCenters_;
  
  int size = possibleCenters.size();

  if (size < 3) {
    // Couldn't find enough finder patterns
    throw ReaderException("No code detected");
  }
  
  vector<vector<Ref<FinderPattern> > > results;

  /*
   * Begin HE modifications to safely detect multiple codes of equal size
   */
  if (size == 3) {
    results.push_back(possibleCenters_);
    return results;
  }

  // Sort by estimated module size to speed up the upcoming checks
  //TODO do a sort based on module size
  sort(possibleCenters.begin(), possibleCenters.end(), compareModuleSize);

  /*
   * Now lets start: build a list of tuples of three finder locations that
   *  - feature similar module sizes
   *  - are placed in a distance so the estimated module count is within the QR specification
   *  - have similar distance between upper left/right and left top/bottom finder patterns
   *  - form a triangle with 90° angle (checked by comparing top right/bottom left distance
   *    with pythagoras)
   *
   * Note: we allow each point to be used for more than one code region: this might seem
   * counterintuitive at first, but the performance penalty is not that big. At this point,
   * we cannot make a good quality decision whether the three finders actually represent
   * a QR code, or are just by chance layouted so it looks like there might be a QR code there.
   * So, if the layout seems right, lets have the decoder try to decode.
   */

  for (int i1 = 0; i1 < (size - 2); i1++) {
    Ref<FinderPattern> p1 = possibleCenters[i1];
    for (int i2 = i1 + 1; i2 < (size - 1); i2++) {
      Ref<FinderPattern> p2 = possibleCenters[i2];
      // Compare the expected module sizes; if they are really off, skip
      float vModSize12 = (p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize()) / min(p1->getEstimatedModuleSize(), p2->getEstimatedModuleSize());
      float vModSize12A = abs(p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize());
      if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
        // break, since elements are ordered by the module size deviation there cannot be
        // any more interesting elements for the given p1.
        break;
      }
      for (int i3 = i2 + 1; i3 < size; i3++) {
        Ref<FinderPattern> p3 = possibleCenters[i3];
        // Compare the expected module sizes; if they are really off, skip
        float vModSize23 = (p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize()) / min(p2->getEstimatedModuleSize(), p3->getEstimatedModuleSize());
        float vModSize23A = abs(p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize());
        if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
          // break, since elements are ordered by the module size deviation there cannot be
          // any more interesting elements for the given p1.
          break;
        }
        vector<Ref<FinderPattern> > test;
        test.push_back(p1);
        test.push_back(p2);
        test.push_back(p3);
        test = FinderPatternFinder::orderBestPatterns(test);
        // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
        Ref<FinderPatternInfo> info = Ref<FinderPatternInfo>(new FinderPatternInfo(test));
        float dA = FinderPatternFinder::distance(info->getTopLeft(), info->getBottomLeft());
        float dC = FinderPatternFinder::distance(info->getTopRight(), info->getBottomLeft());
        float dB = FinderPatternFinder::distance(info->getTopLeft(), info->getTopRight());
        // Check the sizes
        float estimatedModuleCount = (dA + dB) / (p1->getEstimatedModuleSize() * 2.0f);
        if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {
          continue;
        }
        // Calculate the difference of the edge lengths in percent
        float vABBC = abs((dA - dB) / min(dA, dB));
        if (vABBC >= 0.1f) {
          continue;
        }
        // Calculate the diagonal length by assuming a 90° angle at topleft
        float dCpy = (float) sqrt(dA * dA + dB * dB);
        // Compare to the real distance in %
        float vPyC = abs((dC - dCpy) / min(dC, dCpy));
        if (vPyC >= 0.1f) {
          continue;
        }
        // All tests passed!
        results.push_back(test);
      } // end iterate p3
    } // end iterate p2
  } // end iterate p1
  if (results.empty()){
    // Nothing found!
    throw ReaderException("No code detected");    
  }
  return results;
}
示例#25
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
}
示例#26
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));
}
Ref<DetectorResult> Detector::detect() {
    return detect(DecodeHints());
}
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();
}
示例#29
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;
}