blackboxpath="../core/test/data/blackbox"
-formats="ean13 ean8 upce upca qrcode"
+if [ "$*" != "" ]; then
+ formats="$*"
+else
+ formats="ean13 ean8 upce upca qrcode"
+fi
passed=0;
failed=0;
oldcat="";
for format in $formats; do
- for pic in `ls ${blackboxpath}/${format}-*/*.{jpg,JPG} 2>/dev/null`; do
+ for pic in `ls ${blackboxpath}/${format}-*/*.{jpg,JPG,gif,GIF,png,PNG} 2>/dev/null | sort -n`; do
category=${pic%/*};
category=${category##*/};
if [ "$oldcat" != "$category" ]; then
failed=0;
fi
echo -n "Processing: $pic ... "
- tmp="${pic%JPG}";
- txt="${tmp%jpg}txt";
+ tmp="${pic}"
+ tmp="${tmp%JPG}";
+ tmp="${tmp%jpg}";
+ tmp="${tmp%gif}";
+ tmp="${tmp%GIF}";
+ tmp="${tmp%png}";
+ tmp="${tmp%PNG}";
+ txt="${tmp}txt";
expected=`cat "$txt"`;
actual=`build/zxing $pic`;
if [ "$expected" == "$actual" ]; then
* BinaryBitmap.cpp
* zxing
*
- * Created by Ralf Kistner on 19/10/2009.
- * Copyright 2008 ZXing authors All rights reserved.
- * Modified by Lukasz Warchol on 02/02/2010.
+ * Copyright 2010 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.
return binarizer_->getLuminanceSource();
}
+
+ bool BinaryBitmap::isCropSupported() const {
+ return getLuminanceSource()->isCropSupported();
+ }
+
+ Ref<BinaryBitmap> BinaryBitmap::crop(int left, int top, int width, int height) {
+ return Ref<BinaryBitmap> (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->crop(left, top, width, height))));
+ }
+
+ bool BinaryBitmap::isRotateSupported() const {
+ return getLuminanceSource()->isRotateSupported();
+ }
+
+ Ref<BinaryBitmap> BinaryBitmap::rotateCounterClockwise() {
+ return Ref<BinaryBitmap> (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->rotateCounterClockwise())));
+ }
}
--- /dev/null
+/*
+ * HybridBinarizer.cpp
+ * zxing
+ *
+ * Copyright 2010 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/HybridBinarizer.h>
+
+#include <zxing/common/IllegalArgumentException.h>
+
+namespace zxing {
+using namespace std;
+
+static const int MINIMUM_DIMENSION = 40;
+
+static const int LUMINANCE_BITS = 5;
+static const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
+static const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
+
+HybridBinarizer::HybridBinarizer(Ref<LuminanceSource> source) :
+ GlobalHistogramBinarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) {
+
+}
+
+HybridBinarizer::~HybridBinarizer() {
+}
+
+
+Ref<BitMatrix> HybridBinarizer::getBlackMatrix() {
+ binarizeEntireImage();
+ return cached_matrix_;
+}
+
+Ref<Binarizer> HybridBinarizer::createBinarizer(Ref<LuminanceSource> source) {
+ return Ref<Binarizer> (new HybridBinarizer(source));
+}
+
+void HybridBinarizer::binarizeEntireImage() {
+ if (cached_matrix_ == NULL) {
+ Ref<LuminanceSource> source = getLuminanceSource();
+ if (source->getWidth() >= MINIMUM_DIMENSION && source->getHeight() >= MINIMUM_DIMENSION) {
+ unsigned char* luminances = source->getMatrix();
+ int width = source->getWidth();
+ int height = source->getHeight();
+ int subWidth = width >> 3;
+ int subHeight = height >> 3;
+ int *blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width);
+ cached_matrix_.reset(new BitMatrix(width,height));
+ calculateThresholdForBlock(luminances, subWidth, subHeight, width, blackPoints, cached_matrix_);
+ delete [] blackPoints;
+ } else {
+ // If the image is too small, fall back to the global histogram approach.
+ cached_matrix_.reset(GlobalHistogramBinarizer::getBlackMatrix());
+ }
+ }
+}
+
+void HybridBinarizer::calculateThresholdForBlock(unsigned char* luminances, int subWidth, int subHeight,
+ int stride, int blackPoints[], Ref<BitMatrix> matrix) {
+ for (int y = 0; y < subHeight; y++) {
+ for (int x = 0; x < subWidth; x++) {
+ int left = (x > 1) ? x : 2;
+ left = (left < subWidth - 2) ? left : subWidth - 3;
+ int top = (y > 1) ? y : 2;
+ top = (top < subHeight - 2) ? top : subHeight - 3;
+ int sum = 0;
+ for (int z = -2; z <= 2; z++) {
+ int *blackRow = &blackPoints[(top + z) * subWidth];
+ sum += blackRow[left - 2];
+ sum += blackRow[left - 1];
+ sum += blackRow[left];
+ sum += blackRow[left + 1];
+ sum += blackRow[left + 2];
+ }
+ int average = sum / 25;
+ threshold8x8Block(luminances, x << 3, y << 3, average, stride, matrix);
+ }
+ }
+}
+
+void HybridBinarizer::threshold8x8Block(unsigned char* luminances, int xoffset, int yoffset, int threshold,
+ int stride, Ref<BitMatrix> matrix) {
+ for (int y = 0; y < 8; y++) {
+ int offset = (yoffset + y) * stride + xoffset;
+ for (int x = 0; x < 8; x++) {
+ int pixel = luminances[offset + x] & 0xff;
+ if (pixel < threshold) {
+ matrix->set(xoffset + x, yoffset + y);
+ }
+ }
+ }
+}
+
+int* HybridBinarizer::calculateBlackPoints(unsigned char* luminances, int subWidth, int subHeight,
+ int stride) {
+ int *blackPoints = new int[subHeight * subWidth];
+ for (int y = 0; y < subHeight; y++) {
+ for (int x = 0; x < subWidth; x++) {
+ int sum = 0;
+ int min = 255;
+ int max = 0;
+ for (int yy = 0; yy < 8; yy++) {
+ int offset = ((y << 3) + yy) * stride + (x << 3);
+ for (int xx = 0; xx < 8; xx++) {
+ int pixel = luminances[offset + xx] & 0xff;
+ sum += pixel;
+ if (pixel < min) {
+ min = pixel;
+ }
+ if (pixel > max) {
+ max = pixel;
+ }
+ }
+ }
+
+ // If the contrast is inadequate, use half the minimum, so that this block will be
+ // treated as part of the white background, but won't drag down neighboring blocks
+ // too much.
+ int average = (max - min > 24) ? (sum >> 6) : (min >> 1);
+ blackPoints[y * subWidth + x] = average;
+ }
+ }
+ return blackPoints;
+}
+
+} // namespace zxing
+
--- /dev/null
+/*
+ * HybridBinarizer.h
+ * zxing
+ *
+ * Copyright 2010 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 HYBRIDBINARIZER_H_
+#define HYBRIDBINARIZER_H_
+
+#include <vector>
+#include <zxing/Binarizer.h>
+#include <zxing/common/GlobalHistogramBinarizer.h>
+#include <zxing/common/BitArray.h>
+#include <zxing/common/BitMatrix.h>
+
+namespace zxing {
+
+ class HybridBinarizer : public GlobalHistogramBinarizer {
+ private:
+ Ref<BitMatrix> cached_matrix_;
+ Ref<BitArray> cached_row_;
+ int cached_row_num_;
+
+ public:
+ HybridBinarizer(Ref<LuminanceSource> source);
+ virtual ~HybridBinarizer();
+
+ virtual Ref<BitMatrix> getBlackMatrix();
+ Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source);
+ private:
+ void binarizeEntireImage();
+ // We'll be using one-D arrays because C++ can't dynamically allocate 2D arrays
+ int* calculateBlackPoints(unsigned char* luminances, int subWidth, int subHeight,
+ int stride);
+ void calculateThresholdForBlock(unsigned char* luminances, int subWidth, int subHeight,
+ int stride, int blackPoints[], Ref<BitMatrix> matrix);
+ void threshold8x8Block(unsigned char* luminances, int xoffset, int yoffset, int threshold,
+ int stride, Ref<BitMatrix> matrix);
+ };
+
+}
+
+#endif /* GLOBALHISTOGRAMBINARIZER_H_ */
* MagickBitmapSource.cpp
* zxing
*
- * Created by Ralf Kistner on 16/10/2009.
- * Copyright 2008 ZXing authors All rights reserved.
+ * Copyright 2010 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.
}
return matrix;
}
+
+bool MagickBitmapSource::isRotateSupported() const {
+ return false;
+}
+
+Ref<LuminanceSource> MagickBitmapSource::rotateCounterClockwise() {
+ //TODO(flyashi): add rotated image support.
+ /* this segfaults. I tried a few things, none seemed to work. Perhaps the problem is elsewhere? */
+ /*
+ Magick::Image rotated(image_);
+ rotated.modifyImage();
+ rotated.rotate(90); // Image::rotate takes CCW degrees as an argument
+ rotated.syncPixels();
+ return Ref<MagickBitmapSource> (new MagickBitmapSource(rotated));
+ */
+ return Ref<MagickBitmapSource> (NULL);
+}
+
}
* MagickBitmapSource.h
* zxing
*
- * Created by Ralf Kistner on 16/10/2009.
- * Copyright 2008 ZXing authors All rights reserved.
+ * Copyright 2010 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.
int getHeight() const;
unsigned char* getRow(int y, unsigned char* row);
unsigned char* getMatrix();
+ bool isRotateSupported() const;
+ Ref<LuminanceSource> rotateCounterClockwise();
};
}
* main.cpp
* zxing
*
- * Created by Ralf Kistner on 16/10/2009.
- * Copyright 2008 ZXing authors All rights reserved.
- * Modified by Yakov Okshtein (flyashi@gmail.com) to add 1D barcode support.
+ * Copyright 2010 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.
#include <zxing/Result.h>
#include <zxing/ReaderException.h>
#include <zxing/common/GlobalHistogramBinarizer.h>
-//#include <zxing/common/LocalBlockBinarizer.h>
+#include <zxing/common/HybridBinarizer.h>
#include <exception>
#include <zxing/Exception.h>
#include <zxing/common/IllegalArgumentException.h>
using namespace zxing;
//using namespace zxing::qrcode;
+static bool raw_dump = false;
+
+static const int MAX_EXPECTED = 1024;
+
Ref<Result> decode(Ref<BinaryBitmap> image) {
Ref<Reader> reader(new MultiFormatReader);
return Ref<Result> (new Result(*reader->decode(image)));
}
-int test_image(Image& image, bool localized) {
+int test_image(Image& image, bool hybrid, string expected = "") {
string cell_result;
int res = -1;
try {
Ref<MagickBitmapSource> source(new MagickBitmapSource(image));
- if (localized) {
- //binarizer = new LocalBlockBinarizer(source);
+ if (hybrid) {
+ binarizer = new HybridBinarizer(source);
} else {
binarizer = new GlobalHistogramBinarizer(source);
}
res = -5;
}
- cout << cell_result;
+ if (cell_result.compare(expected)) {
+ res = -6;
+ if (!raw_dump) {
+ cout << (hybrid ? "Hybrid" : "Global") << " binarizer failed:\n";
+ if (expected.length() >= 0) {
+ cout << " Expected: " << expected << "\n";
+ }
+ cout << " Detected: " << cell_result << endl;
+ }
+ }
+
+
+ if (raw_dump && !hybrid) /* don't print twice, and global is a bit better */
+ cout << cell_result << endl;
+
return res;
}
-int test_image_local(Image& image) {
- return test_image(image, true);
+int test_image_hybrid(Image& image, string expected = "") {
+ return test_image(image, true, expected);
}
-int test_image_global(Image& image) {
- return test_image(image, false);
+int test_image_global(Image& image, string expected = "") {
+ return test_image(image, false, expected);
+}
+
+string get_expected(string imagefilename) {
+ string textfilename = imagefilename;
+ int dotpos = textfilename.rfind(".");
+ textfilename.replace(dotpos+1, textfilename.length() - dotpos - 1, "txt");
+ char data[MAX_EXPECTED];
+ FILE *fp = fopen(textfilename.data(), "rb");
+
+ // get file size
+ fseek(fp, 0, SEEK_END);
+ int toread = ftell(fp);
+ rewind(fp);
+
+ if (toread > MAX_EXPECTED) {
+ cerr << "MAX_EXPECTED = " << MAX_EXPECTED << " but file '" << textfilename << "' has " << toread
+ << " bytes! Skipping..." << endl;
+ fclose(fp);
+ return "";
+ }
+
+ int nread = fread(data, sizeof(char), toread, fp);
+ if (nread != toread) {
+ cerr << "Could not read entire contents of file '" << textfilename << "'! Skipping..." << endl;
+ fclose(fp);
+ return "";
+ }
+ fclose(fp);
+ data[nread] = '\0';
+ string expected(data);
+ return expected;
}
int main(int argc, char** argv) {
if (argc <= 1) {
- cout << "Usage: " << argv[0] << " <filename1> [<filename2> ...]" << endl;
+ cout << "Usage: " << argv[0] << " [--dump-raw] <filename1> [<filename2> ...]" << endl;
return 1;
}
- // int total = argc - 2;
+ int total = 0;
int gonly = 0;
- int lonly = 0;
+ int honly = 0;
int both = 0;
int neither = 0;
+ if (argc == 2) raw_dump = true;
+
for (int i = 1; i < argc; i++) {
string infilename = argv[i];
-// cerr << "Processing: " << infilename << endl;
+ if (infilename.substr(infilename.length()-3,3).compare("txt") == 0) {
+ continue;
+ }
+ if (infilename.compare("--dump-raw") == 0) {
+ raw_dump = true;
+ continue;
+ }
+ if (!raw_dump)
+ cerr << "Processing: " << infilename << endl;
Image image;
try {
image.read(infilename);
continue;
}
+ string expected;
+ expected = get_expected(infilename);
+
int gresult = 1;
- int lresult = 1;
+ int hresult = 1;
- gresult = test_image_global(image);
-// lresult = test_image_local(image);
+ hresult = test_image_hybrid(image, expected);
+ gresult = test_image_global(image, expected);
gresult = gresult == 0;
- // lresult = lresult == 0;
-
- gonly += gresult && !lresult;
- lonly += lresult && !gresult;
- both += gresult && lresult;
- neither += !gresult && !lresult;
+ hresult = hresult == 0;
+ gonly += gresult && !hresult;
+ honly += hresult && !gresult;
+ both += gresult && hresult;
+ neither += !gresult && !hresult;
+ total = total + 1;
}
+
+ if (!raw_dump)
+ cout << (honly+both) << " passed hybrid, " << (gonly+both) << " passed global, "
+ << both << " pass both, " << neither << " pass neither, " << honly
+ << " passed only hybrid, " << gonly << " passed only global, of " << total
+ << " total." << endl;
+
return 0;
}