diff --git a/cpp/core/src/zxing/qrcode/detector/Detector.cpp b/cpp/core/src/zxing/qrcode/detector/Detector.cpp index 163333b7e..fa1d8bc43 100644 --- a/cpp/core/src/zxing/qrcode/detector/Detector.cpp +++ b/cpp/core/src/zxing/qrcode/detector/Detector.cpp @@ -33,272 +33,276 @@ namespace math_utils = zxing::common::detector::math_utils; -namespace zxing { - namespace qrcode { +using std::ostringstream; +using std::min; +using std::max; +using std::isnan; +using zxing::qrcode::Detector; +using zxing::Ref; +using zxing::BitMatrix; +using zxing::ResultPointCallback; +using zxing::DetectorResult; +using zxing::PerspectiveTransform; +using zxing::qrcode::AlignmentPattern; - using namespace std; +Detector::Detector(Ref image) : + image_(image) { +} - Detector::Detector(Ref image) : - image_(image) { - } +Ref Detector::getImage() const { + return image_; +} - Ref Detector::getImage() const { - return image_; - } +Ref Detector::getResultPointCallback() const { + return callback_; +} - Ref Detector::getResultPointCallback() const { - return callback_; - } +Ref Detector::detect(DecodeHints const& hints) { + callback_ = hints.getResultPointCallback(); + FinderPatternFinder finder(image_, hints.getResultPointCallback()); + Ref info(finder.find(hints)); + return processFinderPatternInfo(info); +} - Ref Detector::detect(DecodeHints const& hints) { - callback_ = hints.getResultPointCallback(); - FinderPatternFinder finder(image_, hints.getResultPointCallback()); - Ref info(finder.find(hints)); - return processFinderPatternInfo(info); - } +Ref Detector::processFinderPatternInfo(Ref info){ + Ref topLeft(info->getTopLeft()); + Ref topRight(info->getTopRight()); + Ref bottomLeft(info->getBottomLeft()); - Ref Detector::processFinderPatternInfo(Ref info){ - Ref topLeft(info->getTopLeft()); - Ref topRight(info->getTopRight()); - Ref bottomLeft(info->getBottomLeft()); + float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); + if (moduleSize < 1.0f) { + throw zxing::ReaderException("bad module size"); + } + int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); + Version *provisionalVersion = Version::getProvisionalVersionForDimension(dimension); + int modulesBetweenFPCenters = provisionalVersion->getDimensionForVersion() - 7; - float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); - if (moduleSize < 1.0f) { - throw zxing::ReaderException("bad module size"); - } - int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); - Version *provisionalVersion = Version::getProvisionalVersionForDimension(dimension); - int modulesBetweenFPCenters = provisionalVersion->getDimensionForVersion() - 7; - - Ref alignmentPattern; - // Anything above version 1 has an alignment pattern - if (provisionalVersion->getAlignmentPatternCenters().size() > 0) { + Ref alignmentPattern; + // Anything above version 1 has an alignment pattern + if (provisionalVersion->getAlignmentPatternCenters().size() > 0) { - // Guess where a "bottom right" finder pattern would have been - float bottomRightX = topRight->getX() - topLeft->getX() + bottomLeft->getX(); - float bottomRightY = topRight->getY() - topLeft->getY() + bottomLeft->getY(); + // Guess where a "bottom right" finder pattern would have been + float bottomRightX = topRight->getX() - topLeft->getX() + bottomLeft->getX(); + float bottomRightY = topRight->getY() - topLeft->getY() + bottomLeft->getY(); - // Estimate that alignment pattern is closer by 3 modules - // from "bottom right" to known top left location - float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters; - int estAlignmentX = (int)(topLeft->getX() + correctionToTopLeft * (bottomRightX - topLeft->getX())); - int estAlignmentY = (int)(topLeft->getY() + correctionToTopLeft * (bottomRightY - topLeft->getY())); + // Estimate that alignment pattern is closer by 3 modules + // from "bottom right" to known top left location + float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters; + int estAlignmentX = (int)(topLeft->getX() + correctionToTopLeft * (bottomRightX - topLeft->getX())); + int estAlignmentY = (int)(topLeft->getY() + correctionToTopLeft * (bottomRightY - topLeft->getY())); - // Kind of arbitrary -- expand search radius before giving up - for (int i = 4; i <= 16; i <<= 1) { - try { - alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i); - break; - } catch (zxing::ReaderException const& re) { - // try next round - } - } - if (alignmentPattern == 0) { - // Try anyway - } - - } - - Ref transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); - Ref bits(sampleGrid(image_, dimension, transform)); - std::vector > points(alignmentPattern == 0 ? 3 : 4); - points[0].reset(bottomLeft); - points[1].reset(topLeft); - points[2].reset(topRight); - if (alignmentPattern != 0) { - points[3].reset(alignmentPattern); - } - - Ref result(new DetectorResult(bits, points)); - return result; - } - - Ref Detector::createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref alignmentPattern, int dimension) { - - float dimMinusThree = (float)dimension - 3.5f; - float bottomRightX; - float bottomRightY; - float sourceBottomRightX; - float sourceBottomRightY; - if (alignmentPattern != 0) { - bottomRightX = alignmentPattern->getX(); - bottomRightY = alignmentPattern->getY(); - sourceBottomRightX = dimMinusThree - 3.0f; - sourceBottomRightY = sourceBottomRightX; - } else { - // Don't have an alignment pattern, just make up the bottom-right point - bottomRightX = (topRight->getX() - topLeft->getX()) + bottomLeft->getX(); - bottomRightY = (topRight->getY() - topLeft->getY()) + bottomLeft->getY(); - sourceBottomRightX = dimMinusThree; - sourceBottomRightY = dimMinusThree; - } - - Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, - sourceBottomRightY, 3.5f, dimMinusThree, topLeft->getX(), topLeft->getY(), topRight->getX(), - topRight->getY(), bottomRightX, bottomRightY, bottomLeft->getX(), bottomLeft->getY())); - - return transform; - } - - Ref Detector::sampleGrid(Ref image, int dimension, Ref transform) { - GridSampler &sampler = GridSampler::getInstance(); - return sampler.sampleGrid(image, dimension, transform); - } - - int Detector::computeDimension(Ref topLeft, Ref topRight, Ref bottomLeft, - float moduleSize) { - int tltrCentersDimension = - math_utils::round(ResultPoint::distance(topLeft, topRight) / moduleSize); - int tlblCentersDimension = - math_utils::round(ResultPoint::distance(topLeft, bottomLeft) / moduleSize); - int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; - switch (dimension & 0x03) { // mod 4 - case 0: - dimension++; + // Kind of arbitrary -- expand search radius before giving up + for (int i = 4; i <= 16; i <<= 1) { + try { + alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i); break; - // 1? do nothing - case 2: - dimension--; - break; - case 3: - ostringstream s; - s << "Bad dimension: " << dimension; - throw zxing::ReaderException(s.str().c_str()); + } catch (zxing::ReaderException const& re) { + // try next round } - return dimension; } - - float Detector::calculateModuleSize(Ref topLeft, Ref topRight, Ref bottomLeft) { - // Take the average - return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; - } - - float Detector::calculateModuleSizeOneWay(Ref pattern, Ref otherPattern) { - float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern->getX(), (int)pattern->getY(), - (int)otherPattern->getX(), (int)otherPattern->getY()); - float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern->getX(), (int)otherPattern->getY(), - (int)pattern->getX(), (int)pattern->getY()); - if (isnan(moduleSizeEst1)) { - return moduleSizeEst2; - } - if (isnan(moduleSizeEst2)) { - return moduleSizeEst1; - } - // Average them, and divide by 7 since we've counted the width of 3 black modules, - // and 1 white and 1 black module on either side. Ergo, divide sum by 14. - return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; - } - - float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) { - - float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); - - // Now count other way -- don't run off image though of course - float scale = 1.0f; - int otherToX = fromX - (toX - fromX); - if (otherToX < 0) { - scale = (float) fromX / (float) (fromX - otherToX); - otherToX = 0; - } else if (otherToX >= (int)image_->getWidth()) { - scale = (float) (image_->getWidth() - 1 - fromX) / (float) (otherToX - fromX); - otherToX = image_->getWidth() - 1; - } - int otherToY = (int) (fromY - (toY - fromY) * scale); - - scale = 1.0f; - if (otherToY < 0) { - scale = (float) fromY / (float) (fromY - otherToY); - otherToY = 0; - } else if (otherToY >= (int)image_->getHeight()) { - scale = (float) (image_->getHeight() - 1 - fromY) / (float) (otherToY - fromY); - otherToY = image_->getHeight() - 1; - } - otherToX = (int) (fromX + (otherToX - fromX) * scale); - - result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); - - // Middle pixel is double-counted this way; subtract 1 - return result - 1.0f; - } - - float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) { - // Mild variant of Bresenham's algorithm; - // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm - bool steep = abs(toY - fromY) > abs(toX - fromX); - if (steep) { - int temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - - int dx = abs(toX - fromX); - int dy = abs(toY - fromY); - int error = -dx >> 1; - int xstep = fromX < toX ? 1 : -1; - int ystep = fromY < toY ? 1 : -1; - - // In black pixels, looking for white, first or second time. - int state = 0; - // Loop up until x == toX, but not beyond - int xLimit = toX + xstep; - for (int x = fromX, y = fromY; x != xLimit; x += xstep) { - int realX = steep ? y : x; - int realY = steep ? x : y; - - // Does current pixel mean we have moved white to black or vice versa? - if (!((state == 1) ^ image_->get(realX, realY))) { - if (state == 2) { - return math_utils::distance(x, y, fromX, fromY); - } - state++; - } - - error += dy; - if (error > 0) { - if (y == toY) { - break; - } - y += ystep; - error -= dx; - } - } - // Found black-white-black; give the benefit of the doubt that the next pixel outside the image - // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a - // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. - if (state == 2) { - return math_utils::distance(toX + xstep, toY, fromX, fromY); - } - // else we didn't find even black-white-black; no estimate is really possible - return NAN; - } - - Ref Detector::findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, - float allowanceFactor) { - // Look for an alignment pattern (3 modules in size) around where it - // should be - int allowance = (int)(allowanceFactor * overallEstModuleSize); - int alignmentAreaLeftX = max(0, estAlignmentX - allowance); - int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance); - if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) { - throw zxing::ReaderException("region too small to hold alignment pattern"); - } - int alignmentAreaTopY = max(0, estAlignmentY - allowance); - int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance); - if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) { - throw zxing::ReaderException("region too small to hold alignment pattern"); - } - - AlignmentPatternFinder alignmentFinder(image_, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, callback_); - return alignmentFinder.find(); + if (alignmentPattern == 0) { + // Try anyway } } + + Ref transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); + Ref bits(sampleGrid(image_, dimension, transform)); + std::vector > points(alignmentPattern == 0 ? 3 : 4); + points[0].reset(bottomLeft); + points[1].reset(topLeft); + points[2].reset(topRight); + if (alignmentPattern != 0) { + points[3].reset(alignmentPattern); + } + + Ref result(new DetectorResult(bits, points)); + return result; +} + +Ref Detector::createTransform(Ref topLeft, Ref topRight, Ref < + ResultPoint > bottomLeft, Ref alignmentPattern, int dimension) { + + float dimMinusThree = (float)dimension - 3.5f; + float bottomRightX; + float bottomRightY; + float sourceBottomRightX; + float sourceBottomRightY; + if (alignmentPattern != 0) { + bottomRightX = alignmentPattern->getX(); + bottomRightY = alignmentPattern->getY(); + sourceBottomRightX = dimMinusThree - 3.0f; + sourceBottomRightY = sourceBottomRightX; + } else { + // Don't have an alignment pattern, just make up the bottom-right point + bottomRightX = (topRight->getX() - topLeft->getX()) + bottomLeft->getX(); + bottomRightY = (topRight->getY() - topLeft->getY()) + bottomLeft->getY(); + sourceBottomRightX = dimMinusThree; + sourceBottomRightY = dimMinusThree; + } + + Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, + sourceBottomRightY, 3.5f, dimMinusThree, topLeft->getX(), topLeft->getY(), topRight->getX(), + topRight->getY(), bottomRightX, bottomRightY, bottomLeft->getX(), bottomLeft->getY())); + + return transform; +} + +Ref Detector::sampleGrid(Ref image, int dimension, Ref transform) { + GridSampler &sampler = GridSampler::getInstance(); + return sampler.sampleGrid(image, dimension, transform); +} + +int Detector::computeDimension(Ref topLeft, Ref topRight, Ref bottomLeft, + float moduleSize) { + int tltrCentersDimension = + math_utils::round(ResultPoint::distance(topLeft, topRight) / moduleSize); + int tlblCentersDimension = + math_utils::round(ResultPoint::distance(topLeft, bottomLeft) / moduleSize); + int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; + switch (dimension & 0x03) { // mod 4 + case 0: + dimension++; + break; + // 1? do nothing + case 2: + dimension--; + break; + case 3: + ostringstream s; + s << "Bad dimension: " << dimension; + throw zxing::ReaderException(s.str().c_str()); + } + return dimension; +} + +float Detector::calculateModuleSize(Ref topLeft, Ref topRight, Ref bottomLeft) { + // Take the average + return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; +} + +float Detector::calculateModuleSizeOneWay(Ref pattern, Ref otherPattern) { + float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern->getX(), (int)pattern->getY(), + (int)otherPattern->getX(), (int)otherPattern->getY()); + float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern->getX(), (int)otherPattern->getY(), + (int)pattern->getX(), (int)pattern->getY()); + if (isnan(moduleSizeEst1)) { + return moduleSizeEst2; + } + if (isnan(moduleSizeEst2)) { + return moduleSizeEst1; + } + // Average them, and divide by 7 since we've counted the width of 3 black modules, + // and 1 white and 1 black module on either side. Ergo, divide sum by 14. + return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; +} + +float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) { + + float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); + + // Now count other way -- don't run off image though of course + float scale = 1.0f; + int otherToX = fromX - (toX - fromX); + if (otherToX < 0) { + scale = (float) fromX / (float) (fromX - otherToX); + otherToX = 0; + } else if (otherToX >= (int)image_->getWidth()) { + scale = (float) (image_->getWidth() - 1 - fromX) / (float) (otherToX - fromX); + otherToX = image_->getWidth() - 1; + } + int otherToY = (int) (fromY - (toY - fromY) * scale); + + scale = 1.0f; + if (otherToY < 0) { + scale = (float) fromY / (float) (fromY - otherToY); + otherToY = 0; + } else if (otherToY >= (int)image_->getHeight()) { + scale = (float) (image_->getHeight() - 1 - fromY) / (float) (otherToY - fromY); + otherToY = image_->getHeight() - 1; + } + otherToX = (int) (fromX + (otherToX - fromX) * scale); + + result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); + + // Middle pixel is double-counted this way; subtract 1 + return result - 1.0f; +} + +float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) { + // Mild variant of Bresenham's algorithm; + // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm + bool steep = abs(toY - fromY) > abs(toX - fromX); + if (steep) { + int temp = fromX; + fromX = fromY; + fromY = temp; + temp = toX; + toX = toY; + toY = temp; + } + + int dx = abs(toX - fromX); + int dy = abs(toY - fromY); + int error = -dx >> 1; + int xstep = fromX < toX ? 1 : -1; + int ystep = fromY < toY ? 1 : -1; + + // In black pixels, looking for white, first or second time. + int state = 0; + // Loop up until x == toX, but not beyond + int xLimit = toX + xstep; + for (int x = fromX, y = fromY; x != xLimit; x += xstep) { + int realX = steep ? y : x; + int realY = steep ? x : y; + + // Does current pixel mean we have moved white to black or vice versa? + if (!((state == 1) ^ image_->get(realX, realY))) { + if (state == 2) { + return math_utils::distance(x, y, fromX, fromY); + } + state++; + } + + error += dy; + if (error > 0) { + if (y == toY) { + break; + } + y += ystep; + error -= dx; + } + } + // Found black-white-black; give the benefit of the doubt that the next pixel outside the image + // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a + // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. + if (state == 2) { + return math_utils::distance(toX + xstep, toY, fromX, fromY); + } + // else we didn't find even black-white-black; no estimate is really possible + return NAN; +} + +Ref Detector::findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, + float allowanceFactor) { + // Look for an alignment pattern (3 modules in size) around where it + // should be + int allowance = (int)(allowanceFactor * overallEstModuleSize); + int alignmentAreaLeftX = max(0, estAlignmentX - allowance); + int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance); + if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) { + throw zxing::ReaderException("region too small to hold alignment pattern"); + } + int alignmentAreaTopY = max(0, estAlignmentY - allowance); + int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance); + if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) { + throw zxing::ReaderException("region too small to hold alignment pattern"); + } + + AlignmentPatternFinder alignmentFinder(image_, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX + - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, callback_); + return alignmentFinder.find(); }