Issue 497
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Sat, 7 Aug 2010 13:22:47 +0000 (13:22 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Sat, 7 Aug 2010 13:22:47 +0000 (13:22 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@1515 59b500cc-1b3d-0410-9834-0bbf25fbcc57

cpp/core/src/zxing/qrcode/QRCodeReader.cpp
cpp/core/src/zxing/qrcode/detector/Detector.cpp
cpp/core/src/zxing/qrcode/detector/Detector.h
cpp/core/src/zxing/qrcode/detector/FinderPatternFinder.cpp
cpp/core/src/zxing/qrcode/detector/FinderPatternFinder.h

index da11644..cc92e93 100644 (file)
@@ -43,7 +43,7 @@ namespace zxing {
                        cout << "(1) created detector " << &detector << "\n" << flush;
 #endif
                        
-                       Ref<DetectorResult> detectorResult(detector.detect());
+                       Ref<DetectorResult> detectorResult(detector.detect(hints));
 #ifdef DEBUG
                        cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
 #endif
index a50424c..6c32558 100644 (file)
@@ -25,6 +25,7 @@
 #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
@@ -42,9 +43,9 @@ Ref<BitMatrix> Detector::getImage() {
    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
@@ -176,26 +177,32 @@ float Detector::calculateModuleSizeOneWay(Ref<ResultPoint> pattern, Ref<ResultPo
 \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
@@ -254,6 +261,9 @@ Ref<AlignmentPattern> Detector::findAlignmentInRegion(float overallEstModuleSize
   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
index 2bafe80..e63aac1 100644 (file)
@@ -27,6 +27,9 @@
 #include <zxing/common/PerspectiveTransform.h>\r
 \r
 namespace zxing {\r
+\r
+class DecodeHints;\r
+\r
 namespace qrcode {\r
 \r
 class Detector : public Counted {\r
@@ -51,7 +54,7 @@ public:
       ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension);\r
 \r
   Detector(Ref<BitMatrix> image);\r
-  Ref<DetectorResult> detect();\r
+  Ref<DetectorResult> detect(DecodeHints const& hints);\r
 };\r
 }\r
 }\r
index 7dbd334..fa02af3 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <zxing/qrcode/detector/FinderPatternFinder.h>
 #include <zxing/ReaderException.h>
+#include <zxing/DecodeHints.h>
 #include <vector>
 #include <cmath>
 #include <cstdlib>
@@ -49,7 +50,8 @@ public:
 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();
   }
 };
 
@@ -260,7 +262,7 @@ int FinderPatternFinder::findRowSkip() {
         // 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;
       }
     }
   }
@@ -284,51 +286,48 @@ bool FinderPatternFinder::haveMultiplyConfirmedCenters() {
   // 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];
@@ -389,7 +388,9 @@ FinderPatternFinder::FinderPatternFinder(Ref<BitMatrix> image) :
     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();
 
@@ -407,7 +408,10 @@ Ref<FinderPatternInfo> FinderPatternFinder::find() {
   // 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_;
@@ -434,7 +438,9 @@ Ref<FinderPatternInfo> FinderPatternFinder::find() {
             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 {
index afa2d89..ac8489e 100644 (file)
@@ -27,6 +27,9 @@
 #include <vector>
 
 namespace zxing {
+
+class DecodeHints;
+
 namespace qrcode {
 
 class FinderPatternFinder {
@@ -56,7 +59,7 @@ private:
 public:
   static float distance(Ref<ResultPoint> p1, Ref<ResultPoint> p2);
   FinderPatternFinder(Ref<BitMatrix> image);
-  Ref<FinderPatternInfo> find();
+  Ref<FinderPatternInfo> find(DecodeHints const& hints);
 };
 }
 }