Permalink
Browse files

C++ port:

 - 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
  • Loading branch information...
1 parent 4462da5 commit b6dc14f14ea4810c1b5bab2fe274764d772e3d53 ralf.kistner committed Dec 8, 2009
View
@@ -0,0 +1,5 @@
+testout
+build
+report.html
+.sconsign.dblite
+
View
@@ -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
View
@@ -29,7 +29,7 @@ zxing_files = all_files('core/src')
zxing_include = ['core/src']
zxing_libs = env.Library('zxing', source=zxing_files, CPPPATH=zxing_include, **compile_options)
-app_files = all_files('magick/src')
+app_files = ['magick/src/MagickBitmapSource.cpp', 'magick/src/main.cpp']
app_executable = env.Program('zxing', app_files, CPPPATH=magick_include + zxing_include, LIBS=magick_libs + zxing_libs, **compile_options)
test_files = all_files('core/tests/src')
@@ -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
@@ -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_ */
@@ -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);
@@ -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_
+#define ZXING_POINT_H_
+
+namespace zxing {
+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;
+};
+}
+#endif // POINT_H_
@@ -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;
}
@@ -77,7 +77,7 @@ Ref<DetectorResult> Detector::detect() {
}
}
if (alignmentPattern == 0) {
- throw zxing::ReaderException("Could not find alignment pattern");
+ // Try anyway
}
}
@@ -31,7 +31,7 @@ namespace zxing {
namespace qrcode {
class Detector : public Counted {
-private:
+protected:
Ref<BitMatrix> image_;
@@ -46,7 +46,7 @@ class Detector : public Counted {
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);
@@ -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;
}
Oops, something went wrong.

0 comments on commit b6dc14f

Please sign in to comment.