*
* Created by Ralf Kistner on 16/10/2009.
* Copyright 2008 ZXing authors All rights reserved.
+ * Modified by Lukasz Warchol on 02/02/2010.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
- Binarizer(source) {
+ Binarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) {
}
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
}
-Ref<BitMatrix> GlobalHistogramBinarizer::estimateBlackMatrix() {
+
+Ref<BitArray> GlobalHistogramBinarizer::getBlackRow(int y, Ref<BitArray> row) {
+ if (y == cached_row_num_) {
+ if (cached_row_ != NULL) {
+ return cached_row_;
+ } else {
+ throw IllegalArgumentException("Too little dynamic range in luminance");
+ }
+ }
+
+ vector<int> histogram(LUMINANCE_BUCKETS, 0);
+ LuminanceSource& source = *getLuminanceSource();
+ int width = source.getWidth();
+ if (row == NULL || static_cast<int>(row->getSize()) < width) {
+ row = new BitArray(width);
+ } else {
+ row->clear();
+ }
+
+ //TODO(flyashi): cache this instead of allocating and deleting per row
+ unsigned char* row_pixels = NULL;
+ try {
+ row_pixels = new unsigned char[width];
+ row_pixels = source.getRow(y, row_pixels);
+ for (int x = 0; x < width; x++) {
+ histogram[row_pixels[x] >> LUMINANCE_SHIFT]++;
+ }
+ int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
+
+ BitArray& array = *row;
+ int left = row_pixels[0];
+ int center = row_pixels[1];
+ for (int x = 1; x < width - 1; x++) {
+ int right = row_pixels[x + 1];
+ // A simple -1 4 -1 box filter with a weight of 2.
+ int luminance = ((center << 2) - left - right) >> 1;
+ if (luminance < blackPoint) {
+ array.set(x);
+ }
+ left = center;
+ center = right;
+ }
+
+ cached_row_ = row;
+ cached_row_num_ = y;
+ delete [] row_pixels;
+ return row;
+ } catch (IllegalArgumentException const& iae) {
+ // Cache the fact that this row failed.
+ cached_row_ = NULL;
+ cached_row_num_ = y;
+ delete [] row_pixels;
+ throw iae;
+ }
+}
+
+Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix() {
+ if (cached_matrix_ != NULL) {
+ return cached_matrix_;
+ }
+
// Faster than working with the reference
- LuminanceSource& source = *getSource();
+ LuminanceSource& source = *getLuminanceSource();
int width = source.getWidth();
int height = source.getHeight();
- valarray<int> histogram(0, LUMINANCE_BUCKETS);
-
+ vector<int> histogram(LUMINANCE_BUCKETS, 0);
- // Quickly calculates the histogram by sampling four rows from the image. This proved to be
- // more robust on the blackbox tests than sampling a diagonal as we used to do.
+ // Quickly calculates the histogram by sampling four rows from the image.
+ // This proved to be more robust on the blackbox tests than sampling a
+ // diagonal as we used to do.
+ unsigned char* row = new unsigned char[width];
for (int y = 1; y < 5; y++) {
- int row = height * y / 5;
+ int rownum = height * y / 5;
int right = (width << 2) / 5;
int sdf;
+ row = source.getRow(rownum, row);
for (int x = width / 5; x < right; x++) {
- unsigned char pixel = source.getPixel(x, row);
- histogram[pixel >> LUMINANCE_SHIFT]++;
- sdf = histogram[pixel >> LUMINANCE_SHIFT];
+ histogram[row[x] >> LUMINANCE_SHIFT]++;
+ sdf = histogram[row[x] >> LUMINANCE_SHIFT];
}
}
Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
BitMatrix& matrix = *matrix_ref;
for (int y = 0; y < height; y++) {
+ row = source.getRow(y, row);
for (int x = 0; x < width; x++) {
- if (source.getPixel(x, y) <= blackPoint)
+ if (row[x] <= blackPoint)
matrix.set(x, y);
}
}
+
+ cached_matrix_ = matrix_ref;
+ delete [] row;
return matrix_ref;
}
-int GlobalHistogramBinarizer::estimate(valarray<int> &histogram) {
+int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
int numBuckets = histogram.size();
int maxBucketCount = 0;
-
// Find tallest peak in histogram
int firstPeak = 0;
int firstPeakSize = 0;
secondPeak = temp;
}
- // Kind of arbitrary; if the two peaks are very close, then we figure there is so little
- // dynamic range in the image, that discriminating black and white is too error-prone.
- // Decoding the image/line is either pointless, or may in some cases lead to a false positive
- // for 1D formats, which are relatively lenient.
- // We arbitrarily say "close" is "<= 1/16 of the total histogram buckets apart"
+ // Kind of arbitrary; if the two peaks are very close, then we figure there is
+ // so little dynamic range in the image, that discriminating black and white
+ // is too error-prone.
+ // Decoding the image/line is either pointless, or may in some cases lead to
+ // a false positive for 1D formats, which are relatively lenient.
+ // We arbitrarily say "close" is
+ // "<= 1/16 of the total histogram buckets apart"
if (secondPeak - firstPeak <= numBuckets >> 4) {
throw IllegalArgumentException("Too little dynamic range in luminance");
}
int bestValleyScore = -1;
for (int i = secondPeak - 1; i > firstPeak; i--) {
int fromFirst = i - firstPeak;
- // Favor a "valley" that is not too close to either peak -- especially not the black peak --
- // and that has a low value of course
- int score = fromFirst * fromFirst * (secondPeak - i) * (maxBucketCount - histogram[i]);
+ // Favor a "valley" that is not too close to either peak -- especially not
+ // the black peak -- and that has a low value of course
+ int score = fromFirst * fromFirst * (secondPeak - i) *
+ (maxBucketCount - histogram[i]);
if (score > bestValleyScore) {
bestValley = i;
bestValleyScore = score;
return bestValley;
}
+Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
+ return Ref<Binarizer> (new GlobalHistogramBinarizer(source));
}
+
+} // namespace zxing