#include <zxing/qrcode/detector/AlignmentPatternFinder.h>\r
#include <zxing/qrcode/Version.h>\r
#include <zxing/common/GridSampler.h>\r
+#include <zxing/DecodeHints.h>\r
#include <cmath>\r
#include <sstream>\r
#include <cstdlib>\r
return image_;\r
}\r
\r
-Ref<DetectorResult> Detector::detect() {\r
+Ref<DetectorResult> Detector::detect(DecodeHints const& hints) {\r
FinderPatternFinder finder(image_);\r
- Ref<FinderPatternInfo> info(finder.find());\r
+ Ref<FinderPatternInfo> info(finder.find(hints));\r
\r
Ref<FinderPattern> topLeft(info->getTopLeft());\r
Ref<FinderPattern> topRight(info->getTopRight());\r
\r
float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {\r
\r
- float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);\r
-\r
-\r
- // Now count other way -- don't run off image though of course\r
- int otherToX = fromX - (toX - fromX);\r
- if (otherToX < 0) {\r
- // "to" should the be the first value not included, so, the first value off\r
- // the edge is -1\r
- otherToX = -1;\r
- } else if (otherToX >= (int)image_->getWidth()) {\r
- otherToX = image_->getWidth();\r
- }\r
- int otherToY = fromY - (toY - fromY);\r
- if (otherToY < 0) {\r
- otherToY = -1;\r
- } else if (otherToY >= (int)image_->getHeight()) {\r
- otherToY = image_->getHeight();\r
- }\r
- result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);\r
- return result - 1.0f; // -1 because we counted the middle pixel twice\r
+ float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);\r
+\r
+ // Now count other way -- don't run off image though of course\r
+ float scale = 1.0f;\r
+ int otherToX = fromX - (toX - fromX);\r
+ if (otherToX < 0) {\r
+ scale = (float) fromX / (float) (fromX - otherToX);\r
+ otherToX = 0;\r
+ } else if (otherToX >= (int)image_->getWidth()) {\r
+ scale = (float) (image_->getWidth() - 1 - fromX) / (float) (otherToX - fromX);\r
+ otherToX = image_->getWidth() - 1;\r
+ }\r
+ int otherToY = (int) (fromY - (toY - fromY) * scale);\r
+\r
+ scale = 1.0f;\r
+ if (otherToY < 0) {\r
+ scale = (float) fromY / (float) (fromY - otherToY);\r
+ otherToY = 0;\r
+ } else if (otherToY >= (int)image_->getHeight()) {\r
+ scale = (float) (image_->getHeight() - 1 - fromY) / (float) (otherToY - fromY);\r
+ otherToY = image_->getHeight() - 1;\r
+ }\r
+ otherToX = (int) (fromX + (otherToX - fromX) * scale);\r
+\r
+ result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);\r
+ return result - 1.0f; // -1 because we counted the middle pixel twice\r
}\r
\r
float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {\r
int allowance = (int)(allowanceFactor * overallEstModuleSize);\r
int alignmentAreaLeftX = max(0, estAlignmentX - allowance);\r
int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance);\r
+ if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {\r
+ throw zxing::ReaderException("region too small to hold alignment pattern");\r
+ }\r
int alignmentAreaTopY = max(0, estAlignmentY - allowance);\r
int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance);\r
\r
#include <zxing/qrcode/detector/FinderPatternFinder.h>
#include <zxing/ReaderException.h>
+#include <zxing/DecodeHints.h>
#include <vector>
#include <cmath>
#include <cstdlib>
class CenterComparator {
public:
bool operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
- return a->getCount() < b->getCount();
+ // N.B.: we want the result in descending order ...
+ return a->getCount() > b->getCount();
}
};
// This is the case where you find top left first. Draw it out.
hasSkipped_ = true;
return (int)(abs(firstConfirmedCenter->getX() - center->getX()) - abs(firstConfirmedCenter->getY()
- - center->getY()));
+ - center->getY()))/2;
}
}
}
// OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
// and that we need to keep looking. We detect this by asking if the estimated module sizes
// vary too much. We arbitrarily say that when the total deviation from average exceeds
- // 15% of the total module size estimates, it's too much.
+ // 5% of the total module size estimates, it's too much.
float average = totalModuleSize / max;
float totalDeviation = 0.0f;
for (size_t i = 0; i < max; i++) {
Ref<FinderPattern> pattern = possibleCenters_[i];
totalDeviation += abs(pattern->getEstimatedModuleSize() - average);
}
- return totalDeviation <= 0.15f * totalModuleSize;
+ return totalDeviation <= 0.05f * totalModuleSize;
}
vector<Ref<FinderPattern> > FinderPatternFinder::selectBestPatterns() {
- sort(possibleCenters_.begin(), possibleCenters_.end(), CenterComparator());
- size_t size = 0;
- size_t max = possibleCenters_.size();
- while (size < max) {
- if (possibleCenters_[size]->getCount() < CENTER_QUORUM) {
- break;
- }
- size++;
- }
+ size_t startSize = possibleCenters_.size();
- if (size < 3) {
+ if (startSize < 3) {
// Couldn't find enough finder patterns
throw zxing::ReaderException("Could not find three finder patterns");
}
- if (size == 3) {
- // Found just enough -- hope these are good!
- vector<Ref<FinderPattern> > result(3);
- result[0] = possibleCenters_[0];
- result[1] = possibleCenters_[1];
- result[2] = possibleCenters_[2];
- return result;
+ // Filter outlier possibilities whose module size is too different
+ if (startSize > 3) {
+ // But we can only afford to do so if we have at least 4 possibilities to choose from
+ float totalModuleSize = 0.0f;
+ for (size_t i = 0; i < startSize; i++) {
+ totalModuleSize += possibleCenters_[i]->getEstimatedModuleSize();
+ }
+ float average = totalModuleSize / (float) startSize;
+ for (size_t i = 0; i < possibleCenters_.size() && possibleCenters_.size() > 3; i++) {
+ if (abs(possibleCenters_[i]->getEstimatedModuleSize() - average) > 0.2f * average) {
+ possibleCenters_.erase(possibleCenters_.begin()+i);
+ i--;
+ }
+ }
}
- // Hmm, multiple found. We need to pick the best three. Find the most
- // popular ones whose module size is nearest the average
- // This does not work for multiple qr codes in the same image
- float averageModuleSize = 0.0f;
- for (size_t i = 0; i < size; i++) {
- averageModuleSize += possibleCenters_[i]->getEstimatedModuleSize();
+ if (possibleCenters_.size() > 3) {
+ // Throw away all but those first size candidate points we found.
+ sort(possibleCenters_.begin(), possibleCenters_.end(), CenterComparator());
}
- averageModuleSize /= (float)size;
- sort(possibleCenters_.begin(), possibleCenters_.end(), ClosestToAverageComparator(averageModuleSize));
+ if (possibleCenters_.size() > 3) {
+ possibleCenters_.erase(possibleCenters_.begin()+3,possibleCenters_.end());
+ }
vector<Ref<FinderPattern> > result(3);
result[0] = possibleCenters_[0];
image_(image), possibleCenters_(), hasSkipped_(false) {
}
-Ref<FinderPatternInfo> FinderPatternFinder::find() {
+Ref<FinderPatternInfo> FinderPatternFinder::find(DecodeHints const& hints) {
+ bool tryHarder = hints.getTryHarder();
+
size_t maxI = image_->getHeight();
size_t maxJ = image_->getWidth();
// 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.
- size_t iSkip = MIN_SKIP;
+ 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_;
if (foundPatternCross(stateCount)) { // Yes
bool confirmed = handlePossibleCenter(stateCount, i, j);
if (confirmed) {
- iSkip = 1; // Go back to examining each line
+ // 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 {