C++ port:
authorralf.kistner <ralf.kistner@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Tue, 8 Dec 2009 21:16:43 +0000 (21:16 +0000)
committerralf.kistner <ralf.kistner@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Tue, 8 Dec 2009 21:16:43 +0000 (21:16 +0000)
 - Added experimental QR edge detector.
 - Modified pattern finder tolerance levels, as in the Java code.
 - Adjusted the local block binarizer slightly.
 - Added a simple example application.
 - Modified MagickBitmapSource to compute the luminance in the same way as the Java BufferedImageLuminanceSource.

git-svn-id: http://zxing.googlecode.com/svn/trunk@1144 59b500cc-1b3d-0410-9834-0bbf25fbcc57

17 files changed:
cpp/.svnignore [new file with mode: 0644]
cpp/README
cpp/SConscript
cpp/core/src/zxing/common/EdgeDetector.cpp [new file with mode: 0644]
cpp/core/src/zxing/common/EdgeDetector.h [new file with mode: 0644]
cpp/core/src/zxing/common/LocalBlockBinarizer.cpp
cpp/core/src/zxing/common/Point.h [new file with mode: 0644]
cpp/core/src/zxing/qrcode/detector/AlignmentPatternFinder.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/QREdgeDetector.cpp [new file with mode: 0644]
cpp/core/src/zxing/qrcode/detector/QREdgeDetector.h [new file with mode: 0644]
cpp/magick/src/MagickBitmapSource.cpp
cpp/magick/src/MagickBitmapSource.h
cpp/magick/src/example.cpp [new file with mode: 0644]
cpp/magick/src/main.cpp

diff --git a/cpp/.svnignore b/cpp/.svnignore
new file mode 100644 (file)
index 0000000..a2365b1
--- /dev/null
@@ -0,0 +1,5 @@
+testout
+build
+report.html
+.sconsign.dblite
+
index e5a9fe8..514b35f 100644 (file)
@@ -13,6 +13,8 @@ To build the test utility:
 - Install Magick++ (libmagick++-dev on Ubuntu)
 - Run "scons zxing"
 
+An simple example application is now also included, but no compilation instructions yet.
+
 To clean:
 - Run "scons -c all"
 
@@ -28,3 +30,7 @@ To format the code:
  - Install astyle
  - Run ./format
 
+To profile the code (very useful to optimize the code):
+ - Install valgrind
+ - "valgrind --tool=callgrind build/zxing - path/to/test/data/*.jpg > report.html"
+ - kcachegrind is a very nice tool to analize the output
index d324545..8283952 100644 (file)
@@ -29,7 +29,7 @@ zxing_files = all_files('core/src')
 zxing_include = ['core/src']\r
 zxing_libs = env.Library('zxing', source=zxing_files, CPPPATH=zxing_include, **compile_options)\r
 \r
-app_files = all_files('magick/src')\r
+app_files = ['magick/src/MagickBitmapSource.cpp', 'magick/src/main.cpp']\r
 app_executable = env.Program('zxing', app_files, CPPPATH=magick_include + zxing_include, LIBS=magick_libs + zxing_libs, **compile_options)\r
 \r
 test_files = all_files('core/tests/src')\r
diff --git a/cpp/core/src/zxing/common/EdgeDetector.cpp b/cpp/core/src/zxing/common/EdgeDetector.cpp
new file mode 100644 (file)
index 0000000..3f0b403
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ *  EdgeDetector.cpp
+ *  zxing
+ *
+ *  Created by Ralf Kistner on 7/12/2009.
+ *  Copyright 2008 ZXing authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <zxing/common/EdgeDetector.h>
+#include <algorithm>
+
+using namespace std;
+
+namespace zxing {
+namespace EdgeDetector {
+
+void findEdgePoints(std::vector<Point>& points, const BitMatrix& image, Point start, Point end, bool invert, int skip, float deviation) {
+  float xdist = end.x - start.x;
+  float ydist = end.y - start.y;
+  float length = sqrt(xdist * xdist + ydist * ydist);
+
+
+  int var;
+
+  if (abs(xdist) > abs(ydist)) {
+    // Horizontal
+    if (xdist < 0)
+      skip = -skip;
+
+    var = int(abs(deviation * length / xdist));
+
+    float dy = ydist / xdist * skip;
+    bool left = (skip < 0) ^ invert;
+    int x = int(start.x);
+
+    int steps = int(xdist / skip);
+    for (int i = 0; i < steps; i++) {
+      x += skip;
+      if (x < 0 || x >= (int)image.getWidth())
+        continue; // In case we start off the edge
+      int my = int(start.y + dy * i);
+      int ey = min(my + var + 1, (int)image.getHeight() - 1);
+      int sy = max(my - var, 0);
+      for (int y = sy + 1; y < ey; y++) {
+        if (left) {
+          if (image.get(x, y) && !image.get(x, y + 1)) {
+            points.push_back(Point(x, y + 0.5f));
+          }
+        } else {
+          if (!image.get(x, y) && image.get(x, y + 1)) {
+            points.push_back(Point(x, y + 0.5f));
+          }
+        }
+      }
+    }
+  } else {
+    // Vertical
+    if (ydist < 0)
+      skip = -skip;
+
+    var = int(abs(deviation * length / ydist));
+
+    float dx = xdist / ydist * skip;
+    bool down = (skip > 0) ^ invert;
+    int y = int(start.y);
+
+    int steps = int(ydist / skip);
+    for (int i = 0; i < steps; i++) {
+      y += skip;
+      if (y < 0 || y >= (int)image.getHeight())
+        continue; // In case we start off the edge
+      int mx = int(start.x + dx * i);
+      int ex = min(mx + var + 1, (int)image.getWidth() - 1);
+      int sx = max(mx - var, 0);
+      for (int x = sx + 1; x < ex; x++) {
+        if (down) {
+          if (image.get(x, y) && !image.get(x + 1, y)) {
+            points.push_back(Point(x + 0.5f, y));
+          }
+
+        } else {
+          if (!image.get(x, y) && image.get(x + 1, y)) {
+            points.push_back(Point(x + 0.5f, y));
+          }
+        }
+
+      }
+    }
+
+  }
+}
+
+Line findLine(const BitMatrix& image, Line estimate, bool invert, int deviation, float threshold, int skip) {
+  float t = threshold * threshold;
+
+  Point start = estimate.start;
+  Point end = estimate.end;
+
+  vector<Point> edges;
+  edges.clear();
+  findEdgePoints(edges, image, start, end, invert, skip, deviation);
+
+  int n = edges.size();
+
+  float xdist = end.x - start.x;
+  float ydist = end.y - start.y;
+
+  bool horizontal = abs(xdist) > abs(ydist);
+
+  float max = 0;
+  Line bestLine(start, end);  // prepopulate with the given line, in case we can't find any line for some reason
+
+  for (int i = -deviation; i < deviation; i++) {
+    float x1, y1;
+    if (horizontal) {
+      y1 = start.y + i;
+      x1 = start.x - i * ydist / xdist;
+    } else {
+      y1 = start.y - i * xdist / ydist;
+      x1 = start.x + i;
+    }
+
+    for (int j = -deviation; j < deviation; j++) {
+      float x2, y2;
+      if (horizontal) {
+        y2 = end.y + j;
+        x2 = end.x - j * ydist / xdist;
+      } else {
+        y2 = end.y - j * xdist / ydist;
+        x2 = end.x + j;
+      }
+
+      float dx = x1 - x2;
+      float dy = y1 - y2;
+      float length = sqrt(dx * dx + dy * dy);
+
+      float score = 0;
+
+      for(int k = 0; k < n; k++) {
+        const Point& edge = edges[k];
+        float dist = ((x1 - edge.x) * dy - (y1 - edge.y) * dx) / length;
+        // Similar to least squares method
+        float s = t - dist * dist;
+        if (s > 0)
+          score += s;
+      }
+
+      if (score > max) {
+        max = score;
+        bestLine.start = Point(x1, y1);
+        bestLine.end = Point(x2, y2);
+      }
+    }
+  }
+
+  return bestLine;
+}
+
+Point 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(denom == 0)  // Lines don't intersect
+    return Point(INFINITY, INFINITY);
+
+  float x = (p * dxb - dxa * q) / denom;
+  float y = (p * dyb - dya * q) / denom;
+
+  return Point(x, y);
+}
+
+} // namespace EdgeDetector
+} // namespace zxing
diff --git a/cpp/core/src/zxing/common/EdgeDetector.h b/cpp/core/src/zxing/common/EdgeDetector.h
new file mode 100644 (file)
index 0000000..3698c1c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  EdgeDetector.h
+ *  zxing
+ *
+ *  Created by Ralf Kistner on 7/12/2009.
+ *  Copyright 2008 ZXing authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EDGEDETECTOR_H_
+#define EDGEDETECTOR_H_
+
+#include <vector>
+#include <zxing/common/BitMatrix.h>
+#include <zxing/common/Point.h>
+
+namespace zxing {
+namespace EdgeDetector {
+
+void findEdgePoints(std::vector<Point>& points, const BitMatrix& image, Point start, Point end, bool invert, int skip, float deviation);
+Line findLine(const BitMatrix& image, Line estimate, bool invert, int deviation, float threshold, int skip);
+
+Point intersection(Line a, Line b);
+
+}
+}
+#endif /* EDGEDETECTOR_H_ */
index c8058d3..693f133 100644 (file)
@@ -86,7 +86,7 @@ void LocalBlockBinarizer::calculateThresholdForBlock(const unsigned char* lumina
       int top = (y > 0) ? y : 1;
       top = (top < subHeight - 1) ? top : subHeight - 2;
       int sum = 0;
-      int type = 0;
+      int contrast = 0;
       for (int z = -1; z <= 1; z++) {
 //                             sum += averages[(top + z) * subWidth + left - 2];
         sum += averages[(top + z) * subWidth + left - 1];
@@ -95,15 +95,15 @@ void LocalBlockBinarizer::calculateThresholdForBlock(const unsigned char* lumina
 //                             sum += averages[(top + z) * subWidth + left + 2];
 
 //                             type += types[(top + z) * subWidth + left - 2];
-        type += types[(top + z) * subWidth + left - 1];
-        type += types[(top + z) * subWidth + left];
-        type += types[(top + z) * subWidth + left + 1];
+        contrast += types[(top + z) * subWidth + left - 1];
+        contrast += types[(top + z) * subWidth + left];
+        contrast += types[(top + z) * subWidth + left + 1];
 //                             type += types[(top + z) * subWidth + left + 2];
       }
       int average = sum / 9;
 
 
-      if (type > 3)
+      if (contrast > 2)
         threshold8x8Block(luminances, x << 3, y << 3, average, stride, matrix);
 //                     else if(average < global)       // Black
 //                             matrix.setRegion(x << 3, y << 3, 8, 8);
diff --git a/cpp/core/src/zxing/common/Point.h b/cpp/core/src/zxing/common/Point.h
new file mode 100644 (file)
index 0000000..a391042
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  Point.h
+ *  zxing
+ *
+ *  Created by Ralf Kistner on 7/12/2009.
+ *  Copyright 2008 ZXing authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ZXING_POINT_H_\r
+#define ZXING_POINT_H_\r
+
+namespace zxing {\r
+class PointI {
+public:
+  int x;
+  int y;
+};
+
+class Point {
+public:
+  Point(float x_, float y_) : x(x_), y(y_) {};
+
+  float x;
+  float y;
+};
+
+class Line {
+public:
+  Line(Point start_, Point end_) : start(start_), end(end_) {};
+
+  Point start;
+  Point end;
+};\r
+}\r
+#endif // POINT_H_\r
index 029f5b8..b3d92d4 100644 (file)
@@ -85,7 +85,7 @@ float AlignmentPatternFinder::crossCheckVertical(size_t startI, size_t centerJ,
   }
 
   int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
-  if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
+  if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
     return NAN;
   }
 
index 6e35180..1fa16f6 100644 (file)
@@ -77,7 +77,7 @@ Ref<DetectorResult> Detector::detect() {
       }
     }
     if (alignmentPattern == 0) {
-      throw zxing::ReaderException("Could not find alignment pattern");
+      // Try anyway
     }
 
   }
index a95a5fc..aa3703e 100644 (file)
@@ -31,7 +31,7 @@ namespace zxing {
 namespace qrcode {
 
 class Detector : public Counted {
-private:
+protected:
   Ref<BitMatrix> image_;
 
 
@@ -46,7 +46,7 @@ private:
       float allowanceFactor);
 public:
 
-  static Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
+  virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
       ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension);
 
   Detector(Ref<BitMatrix> image);
index af6609d..666c4f2 100644 (file)
@@ -133,10 +133,10 @@ float FinderPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int
     return NAN;
   }
 
-  // If we found a finder-pattern-like section, but its size is more than 20% different than
+  // If we found a finder-pattern-like section, but its size is more than 40% different than
   // the original, assume it's a false positive
   int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
-  if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
+  if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
     return NAN;
   }
 
diff --git a/cpp/core/src/zxing/qrcode/detector/QREdgeDetector.cpp b/cpp/core/src/zxing/qrcode/detector/QREdgeDetector.cpp
new file mode 100644 (file)
index 0000000..739a739
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *  QREdgeDetector.cpp
+ *  zxing
+ *
+ *  Created by Ralf Kistner on 7/12/2009.
+ *  Copyright 2008 ZXing authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <zxing/qrcode/detector/QREdgeDetector.h>
+#include <zxing/common/EdgeDetector.h>
+
+using namespace std;
+
+namespace zxing {
+namespace qrcode {
+
+static const float patternEdgeThreshold = 2;
+static const int patternEdgeWidth = 3;
+static const float patternEdgeSearchRatio = 1.1;
+static const int patternEdgeSkip = 2;
+
+static const float accurateEdgeThreshold = 3.3;
+static const int accurateEdgeWidth = 7;
+static const int accurateEdgeSkip = 2;
+
+static Point guessLastPattern(Point topLeft, Point topRight, Point bottomLeft) {
+  return Point(topRight.x - topLeft.x + bottomLeft.x, topRight.y - topLeft.y + bottomLeft.y);
+}
+
+static Point rp(Ref<ResultPoint> rp) {
+  return Point(rp->getX(), rp->getY());
+}
+
+QREdgeDetector::QREdgeDetector(Ref<BitMatrix> image) : Detector(image) { }
+
+Ref<PerspectiveTransform> QREdgeDetector::createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
+      ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension) {
+
+  if(alignmentPattern == NULL) {
+    Point corner = findCorner(*image_.object_, rp(topLeft), rp(topRight), rp(bottomLeft), dimension);
+    return get1CornerTransform(rp(topLeft), rp(topRight), rp(bottomLeft), corner, dimension);
+  } else {
+    return Detector::createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
+  }
+}
+
+
+
+
+Point QREdgeDetector::findCorner(const BitMatrix& image, Point topLeft, Point topRight, Point bottomLeft, int dimension) {
+  Point bottomRight = guessLastPattern(topLeft, topRight, bottomLeft);
+
+  Line bottomEst = findPatternEdge(image, bottomLeft, topLeft, bottomRight, false);
+  Line rightEst = findPatternEdge(image, topRight, topLeft, bottomRight, true);
+
+  //return EdgeDetector::intersection(bottomEst, rightEst);
+
+  Line bottom = EdgeDetector::findLine(image, bottomEst, false, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip);
+  Line right = EdgeDetector::findLine(image, rightEst, true, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip);
+
+
+  return EdgeDetector::intersection(bottom, right);
+}
+
+Line QREdgeDetector::findPatternEdge(const BitMatrix& image, Point pattern, Point opposite, Point direction, bool invert) {
+  Point start = endOfReverseBlackWhiteBlackRun(image, pattern, opposite);
+
+  float dx = pattern.x - start.x;
+  float dy = pattern.y - start.y;
+  float dist = sqrt(dx*dx + dy*dy);
+
+  float dirX = direction.x - pattern.x;
+  float dirY = direction.y - pattern.y;
+  float dirSize = sqrt(dirX*dirX + dirY*dirY);
+
+  float nx = dirX/dirSize;
+  float ny = dirY/dirSize;
+
+  float search = dist * patternEdgeSearchRatio;
+  Point a(start.x + nx*search, start.y + ny*search);
+  Point b(start.x - nx*search, start.y - ny*search);
+
+  return EdgeDetector::findLine(image, Line(a, b), invert, patternEdgeWidth, patternEdgeThreshold, patternEdgeSkip);
+}
+
+
+Ref<PerspectiveTransform> QREdgeDetector::get1CornerTransform(Point topLeft, Point topRight, Point bottomLeft, Point corner, int dimension) {
+  float dimMinusThree = (float) dimension - 3.5f;
+
+  Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, dimension,
+                                      dimension, 3.5f, dimMinusThree, topLeft.x, topLeft.y, topRight.x,
+                                      topRight.y, corner.x, corner.y, bottomLeft.x, bottomLeft.y));
+
+  return transform;
+}
+
+// Adapted from "sizeOfBlackWhiteBlackRun" in zxing::qrcode::Detector
+Point QREdgeDetector::endOfReverseBlackWhiteBlackRun(const BitMatrix& image, Point from, Point to) {
+  float fromX = from.x;
+  float fromY = from.y;
+  float toX = to.x;
+  float toY = to.y;
+
+  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 ystep = fromY < toY ? -1 : 1;
+  int xstep = fromX < toX ? -1 : 1;
+  int state = 0; // In black pixels, looking for white, first or second time
+
+  // In case there are no points, prepopulate to from
+  int realX = from.x;
+  int realY = from.y;
+  for (int x = fromX, y = fromY; x != toX; x += xstep) {
+    realX = steep ? y : x;
+    realY = steep ? x : y;
+
+    if(realX < 0 || realY < 0 || realX >= (int)image.getWidth() || realY >= (int)image.getHeight())
+      break;
+
+    if (state == 1) { // In white pixels, looking for black
+      if (image.get(realX, realY)) {
+        state++;
+      }
+    } else {
+      if (!image.get(realX, realY)) {
+        state++;
+      }
+    }
+
+    if (state == 3) { // Found black, white, black, and stumbled back onto white; done
+      return Point(realX, realY);
+    }
+    error += dy;
+    if (error > 0) {
+      y += ystep;
+      error -= dx;
+    }
+  }
+
+  // B-W-B run not found, return the last point visited.
+  return Point(realX, realY);
+}
+
+} // namespace qrcode
+} // namespace zxing
diff --git a/cpp/core/src/zxing/qrcode/detector/QREdgeDetector.h b/cpp/core/src/zxing/qrcode/detector/QREdgeDetector.h
new file mode 100644 (file)
index 0000000..f5cdc89
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  QREdgeDetector.h
+ *  zxing
+ *
+ *  Created by Ralf Kistner on 7/12/2009.
+ *  Copyright 2008 ZXing authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef QREDGEDETECTOR_H_\r
+#define QREDGEDETECTOR_H_\r
+
+#include <zxing/qrcode/detector/Detector.h>
+#include <zxing/common/Point.h>
+
+namespace zxing {
+namespace qrcode {
+
+class QREdgeDetector : public Detector {
+public:
+  QREdgeDetector(Ref<BitMatrix> image);
+
+  virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
+      ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension);
+
+private:
+  Point findCorner(const BitMatrix& image, Point topLeft, Point topRight, Point bottomLeft, int dimension);
+  Line findPatternEdge(const BitMatrix& image, Point pattern, Point opposite, Point direction, bool invert);
+
+  Point endOfReverseBlackWhiteBlackRun(const BitMatrix& image, Point from, Point to);
+
+  Ref<PerspectiveTransform> get1CornerTransform(Point topLeft, Point topRight, Point bottomLeft, Point corner, int dimension);
+};
+
+}
+}\r
+#endif // QREDGEDETECTOR_H_\r
index 0da0dbc..2008599 100644 (file)
@@ -46,5 +46,26 @@ int MagickBitmapSource::getHeight() {
 unsigned char MagickBitmapSource::getPixel(int x, int y) {
   const PixelPacket* p = pixel_cache + y * width + x;
   // We assume 16 bit values here
-  return (unsigned char)((((int)p->red + (int)p->green + (int)p->blue) >> 8) / 3);
+
+  //return (unsigned char)((((int)p->red + (int)p->green + (int)p->blue) >> 8) / 3);
+
+  return (unsigned char)((306 * ((int)p->red >> 8) + 601 * ((int)p->green >> 8) + 117 * ((int)p->blue >> 8)) >> 10);
 }
+
+/** This is a more efficient implementation. */
+unsigned char* MagickBitmapSource::copyMatrix() {
+  int width = getWidth();
+  int height =  getHeight();
+  unsigned char* matrix = new unsigned char[width*height];
+  unsigned char* m = matrix;
+  const Magick::PixelPacket* p = pixel_cache;
+  for (int y = 0; y < height; y++) {
+    for (int x = 0; x < width; x++) {
+      *m = (unsigned char)((306 * ((int)p->red >> 8) + 601 * ((int)p->green >> 8) + 117 * ((int)p->blue >> 8)) >> 10);
+      m++;
+      p++;
+    }
+  }
+  return matrix;
+}
+
index acaf138..dd21084 100644 (file)
@@ -39,6 +39,7 @@ public:
   int getWidth();
   int getHeight();
   unsigned char getPixel(int x, int y);
+  unsigned char* copyMatrix();
 };
 
 #endif /* MAGICKMONOCHROMEBITMAPSOURCE_H_ */
diff --git a/cpp/magick/src/example.cpp b/cpp/magick/src/example.cpp
new file mode 100644 (file)
index 0000000..cb119ad
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  example.cpp
+ *  zxing
+ *
+ *  Created by Ralf Kistner on 16/10/2009.
+ *  Copyright 2008 ZXing authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <Magick++.h>
+#include "MagickBitmapSource.h"
+#include <zxing/qrcode/QRCodeReader.h>
+#include <zxing/common/GlobalHistogramBinarizer.h>
+#include <zxing/common/LocalBlockBinarizer.h>
+#include <zxing/Exception.h>
+
+using namespace Magick;
+using namespace std;
+using namespace zxing;
+using namespace zxing::qrcode;
+
+
+void decode_image(Image& image, bool localized) {
+  try {
+    Ref<MagickBitmapSource> source(new MagickBitmapSource(image));
+    
+    Ref<Binarizer> binarizer(NULL);
+    if (localized) {
+      binarizer = new LocalBlockBinarizer(source);
+    } else {
+      binarizer = new GlobalHistogramBinarizer(source);
+    }
+    
+
+    Ref<BinaryBitmap> image(new BinaryBitmap(binarizer));
+    QRCodeReader reader;
+    Ref<Result> result(reader.decode(image));
+    
+    cout << result->getText()->getText() << endl;
+  } catch (zxing::Exception& e) {
+    cerr << "Error: " << e.what() << endl;
+  }
+}
+
+
+int main(int argc, char** argv) {
+  if (argc < 2) {
+    cout << "Usage: " << argv[0] << "<filename1> [<filename2> ...]" << endl;
+    return 1;
+  }
+  for (int i = 1; i < argc; i++) {
+    string infilename = argv[i];
+    cout << "Processing: " << infilename << endl;
+    Image image;
+    try {
+      image.read(infilename);
+    } catch (...) {
+      cerr << "Unable to open image, ignoring" << endl;
+      continue;
+    }
+    
+    bool local = true; // Use local thresholding
+    
+    test_image(image, local);
+  }
+  return 0;
+}
+
+
index ce5f877..ff4e64b 100644 (file)
@@ -35,6 +35,7 @@
 #include <zxing/BinaryBitmap.h>
 
 #include <zxing/qrcode/detector/Detector.h>
+#include <zxing/qrcode/detector/QREdgeDetector.h>
 #include <zxing/qrcode/decoder/Decoder.h>
 
 using namespace Magick;
@@ -108,7 +109,7 @@ void save_grid(Ref<BitMatrix> matrix, string filename, Ref<PerspectiveTransform>
 Ref<Result> decode(string out_prefix, Ref<BinaryBitmap> image, string& cell_grid, string& cell_transformed) {
   Decoder decoder;
 
-  Detector detector(image->getBlackMatrix());
+  QREdgeDetector detector = QREdgeDetector(image->getBlackMatrix());
 
   Ref<DetectorResult> detectorResult(detector.detect());
 
@@ -147,6 +148,7 @@ int test_image(Image& image, string out_prefix, bool localized) {
   string cell_transformed;
   string cell_result;
   string cell_grid;
+  string result_color = "red";
   int res = -1;
 
   Ref<BitMatrix> matrix(NULL);
@@ -171,7 +173,8 @@ int test_image(Image& image, string out_prefix, bool localized) {
 
     Ref<BinaryBitmap> binary(new BinaryBitmap(binarizer));
     Ref<Result> result(decode(out_prefix, binary, cell_grid, cell_transformed));
-    cell_result = "<font color=\"blue\"><b>" + result->getText()->getText() + "</b></font>";
+    cell_result = result->getText()->getText();
+    result_color = "green";
     res = 0;
   } catch (ReaderException e) {
     cell_result = "zxing::ReaderException: " + string(e.what());
@@ -190,7 +193,7 @@ int test_image(Image& image, string out_prefix, bool localized) {
   cout << "<td>" << cell_mono << "</td>" << endl;
   cout << "<td>" << cell_grid << "</td>" << endl;
   cout << "<td>" << cell_transformed << "</td>" << endl;
-  cout << "<td>" << cell_result << "</td>" << endl;
+  cout << "<td bgcolor=\"" << result_color << "\">" << cell_result << "</td>" << endl;
   return res;
 }
 
@@ -216,7 +219,7 @@ int main(int argc, char** argv) {
   int both = 0;
   int neither = 0;
 
-  cout << "<html><body><table>" << endl;
+  cout << "<html><body><table border=\"1\">" << endl;
   for (int i = 2; i < argc; i++) {
     string infilename = argv[i];
     cerr << "Processing: " << infilename << endl;
@@ -227,7 +230,7 @@ int main(int argc, char** argv) {
       cerr << "Unable to open image, ignoring" << endl;
       continue;
     }
-    cout << "<tr><td colspan=\"3\">" << infilename << "</td></tr>" << endl;
+    cout << "<tr><td colspan=\"5\">" << infilename << "</td></tr>" << endl;
     cout << "<tr>" << endl;
 
     cout << "<td><img src=\"" << infilename << "\" /></td>" << endl;
@@ -237,12 +240,12 @@ int main(int argc, char** argv) {
     int lresult = 1;
 
     if (outfolder == string("-")) {
-      gresult = test_image_global(image, "");
+      //gresult = test_image_global(image, "");
       lresult = test_image_local(image, "");
     } else {
       replace(infilename.begin(), infilename.end(), '/', '_');
       string prefix = string(outfolder) + string("/") + infilename;
-      gresult = test_image_global(image, prefix + ".g");
+      //gresult = test_image_global(image, prefix + ".g");
       lresult = test_image_local(image, prefix + ".l");
     }