Ref<Result> OneDReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) { try { return doDecode(image, hints); } catch (NotFoundException const& nfe) { // std::cerr << "trying harder" << std::endl; bool tryHarder = hints.getTryHarder(); if (tryHarder && image->isRotateSupported()) { // std::cerr << "v rotate" << std::endl; Ref<BinaryBitmap> rotatedImage(image->rotateCounterClockwise()); // std::cerr << "^ rotate" << std::endl; Ref<Result> result = doDecode(rotatedImage, hints); // Doesn't have java metadata stuff ArrayRef< Ref<ResultPoint> >& points (result->getResultPoints()); if (points && !points->empty()) { int height = rotatedImage->getHeight(); for (int i = 0; i < points->size(); i++) { points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX())); } } // std::cerr << "tried harder" << std::endl; return result; } else { // std::cerr << "tried harder nfe" << std::endl; throw nfe; } } }
void MultiFormatReader::setHints(DecodeHints hints) { hints_ = hints; readers_.clear(); bool tryHarder = hints.getTryHarder(); bool addOneDReader = hints.containsFormat(BarcodeFormat::UPC_E) || hints.containsFormat(BarcodeFormat::UPC_A) || hints.containsFormat(BarcodeFormat::UPC_E) || hints.containsFormat(BarcodeFormat::EAN_13) || hints.containsFormat(BarcodeFormat::EAN_8) || hints.containsFormat(BarcodeFormat::CODABAR) || hints.containsFormat(BarcodeFormat::CODE_39) || hints.containsFormat(BarcodeFormat::CODE_93) || hints.containsFormat(BarcodeFormat::CODE_128) || hints.containsFormat(BarcodeFormat::ITF) || hints.containsFormat(BarcodeFormat::RSS_14) || hints.containsFormat(BarcodeFormat::RSS_EXPANDED); if (addOneDReader && !tryHarder) { readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints))); } if (hints.containsFormat(BarcodeFormat::QR_CODE)) { readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader())); } if (hints.containsFormat(BarcodeFormat::DATA_MATRIX)) { readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader())); } if (hints.containsFormat(BarcodeFormat::AZTEC)) { readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader())); } if (hints.containsFormat(BarcodeFormat::PDF_417)) { readers_.push_back(Ref<Reader>(new zxing::pdf417::PDF417Reader())); } /* if (formats.contains(BarcodeFormat.PDF_417)) { readers.add(new PDF417Reader()); } if (formats.contains(BarcodeFormat.MAXICODE)) { readers.add(new MaxiCodeReader()); } */ if (addOneDReader && tryHarder) { readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints))); } if (readers_.size() == 0) { if (!tryHarder) { readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints))); } readers_.push_back(Ref<Reader>(new zxing::qrcode::QRCodeReader())); readers_.push_back(Ref<Reader>(new zxing::datamatrix::DataMatrixReader())); readers_.push_back(Ref<Reader>(new zxing::aztec::AztecReader())); readers_.push_back(Ref<Reader>(new zxing::pdf417::PDF417Reader())); // readers.add(new PDF417Reader()); // readers.add(new MaxiCodeReader()); if (tryHarder) { readers_.push_back(Ref<Reader>(new zxing::oned::MultiFormatOneDReader(hints))); } } }
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(); }
Ref<FinderPatternInfo> FinderPatternFinder::find(DecodeHints const &hints) { bool tryHarder = hints.getTryHarder(); size_t maxI = image_->getHeight(); size_t maxJ = image_->getWidth(); // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // As this is used often, we use an integer array instead of vector int stateCount[5]; bool done = false; // Let's assume that the maximum version QR Code we support takes up 1/4 // the height of the image, and then account for the center being 3 // modules in size. This gives the smallest number of pixels the center // could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. int iSkip = (3 * maxI) / (4 * MAX_MODULES); if (iSkip < MIN_SKIP || tryHarder) { iSkip = MIN_SKIP; } // This is slightly faster than using the Ref. Efficiency is important here BitMatrix &matrix = *image_; for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) { // Get a row of black/white values stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; int currentState = 0; for (size_t j = 0; j < maxJ; j++) { if (matrix.get(j, i)) { // Black pixel if ((currentState & 1) == 1) { // Counting white pixels currentState++; } stateCount[currentState]++; } else { // White pixel if ((currentState & 1) == 0) { // Counting black pixels if (currentState == 4) { // A winner? if (foundPatternCross(stateCount)) { // Yes bool confirmed = handlePossibleCenter(stateCount, i, j); if (confirmed) { // Start examining every other line. Checking each line turned out to be too // expensive and didn't improve performance. iSkip = 2; if (hasSkipped_) { done = haveMultiplyConfirmedCenters(); } else { int rowSkip = findRowSkip(); if (rowSkip > stateCount[2]) { // Skip rows between row of lower confirmed center // and top of presumed third confirmed center // but back up a bit to get a full chance of detecting // it, entire width of center of finder pattern // Skip by rowSkip, but back off by stateCount[2] (size // of last center of pattern we saw) to be conservative, // and also back off by iSkip which is about to be // re-added i += rowSkip - stateCount[2] - iSkip; j = maxJ - 1; } } } else { stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; continue; } // Clear state to start looking again currentState = 0; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; } else { // No, shift counts back by two stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; } } else { stateCount[++currentState]++; } } else { // Counting white pixels stateCount[currentState]++; } } } if (foundPatternCross(stateCount)) { bool confirmed = handlePossibleCenter(stateCount, i, maxJ); if (confirmed) { iSkip = stateCount[0]; if (hasSkipped_) { // Found a third one done = haveMultiplyConfirmedCenters(); } } } } vector<Ref<FinderPattern> > patternInfo = selectBestPatterns(); patternInfo = orderBestPatterns(patternInfo); Ref<FinderPatternInfo> result(new FinderPatternInfo(patternInfo)); return result; }
vector<Ref<FinderPatternInfo> > MultiFinderPatternFinder::findMulti(DecodeHints const& hints){ bool tryHarder = hints.getTryHarder(); Ref<BitMatrix> image = image_; // Protected member int maxI = image->getHeight(); int maxJ = image->getWidth(); // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3); if (iSkip < MIN_SKIP || tryHarder) { iSkip = MIN_SKIP; } int stateCount[5]; for (int i = iSkip - 1; i < maxI; i += iSkip) { // Get a row of black/white values stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; int currentState = 0; for (int j = 0; j < maxJ; j++) { if (image->get(j, i)) { // Black pixel if ((currentState & 1) == 1) { // Counting white pixels currentState++; } stateCount[currentState]++; } else { // White pixel if ((currentState & 1) == 0) { // Counting black pixels if (currentState == 4) { // A winner? if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j)) { // Yes // Clear state to start looking again currentState = 0; stateCount[0] = 0; stateCount[1] = 0; stateCount[2] = 0; stateCount[3] = 0; stateCount[4] = 0; } else { // No, shift counts back by two stateCount[0] = stateCount[2]; stateCount[1] = stateCount[3]; stateCount[2] = stateCount[4]; stateCount[3] = 1; stateCount[4] = 0; currentState = 3; } } else { stateCount[++currentState]++; } } else { // Counting white pixels stateCount[currentState]++; } } } // for j=... if (foundPatternCross(stateCount)) { handlePossibleCenter(stateCount, i, maxJ); } // end if foundPatternCross } // for i=iSkip-1 ... vector<vector<Ref<FinderPattern> > > patternInfo = selectBestPatterns(); vector<Ref<FinderPatternInfo> > result; for (unsigned int i = 0; i < patternInfo.size(); i++) { vector<Ref<FinderPattern> > pattern = patternInfo[i]; pattern = FinderPatternFinder::orderBestPatterns(pattern); result.push_back(Ref<FinderPatternInfo>(new FinderPatternInfo(pattern))); } return result; }