Updates to C++ port:
[zxing.git] / cpp / core / src / zxing / common / GlobalHistogramBinarizer.cpp
index 61615d3..d30f1a1 100644 (file)
 #include <zxing/common/IllegalArgumentException.h>
 
 namespace zxing {
-       using namespace std;
-       
-       const int LUMINANCE_BITS = 5;
-       const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
-       const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
-       
-       GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
-    Binarizer(source) {
-               
-       }
-       
-       GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
-       }
-       
-       
-       Ref<BitArray> GlobalHistogramBinarizer::estimateBlackRow(int y, Ref<BitArray> row){
-               vector<int> histogram(LUMINANCE_BUCKETS, 0);
-               LuminanceSource& source = *getSource();
-               int width = source.getWidth();
-               if (row == NULL || row->getSize() < width) {
-                       row = new BitArray(width);
-               } else {
-                       row->clear();
-               }
-               
-               for (int x = 0; x < width; x++) {
-                       unsigned char pixel = source.getPixel(x, y);
-                       histogram[pixel >> LUMINANCE_SHIFT]++;
-               }
-               int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
-               
-               
-               Ref<BitArray> array_ref(new BitArray(width));
-               BitArray& array = *array_ref;
-               
-               int left = source.getPixel(0, y);
-               int center = source.getPixel(1, y);
-               for (int x = 1; x < width - 1; x++) {
-                       int right = source.getPixel(x+1, y);
-                       // 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;
-               }
-               
-               return array_ref;
-       }
-       
-       Ref<BitMatrix> GlobalHistogramBinarizer::estimateBlackMatrix() {
-               // Faster than working with the reference
-               LuminanceSource& source = *getSource();
-               int width = source.getWidth();
-               int height = source.getHeight();
-               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.
-               for (int y = 1; y < 5; y++) {
-                       int row = height * y / 5;
-                       int right = (width << 2) / 5;
-                       int sdf;
-                       for (int x = width / 5; x < right; x++) {
-                               unsigned char pixel = source.getPixel(x, row);
-                               histogram[pixel >> LUMINANCE_SHIFT]++;
-                               sdf = histogram[pixel >> LUMINANCE_SHIFT];
-                       }
-               }
-               
-               int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
-               
-               Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
-               BitMatrix& matrix = *matrix_ref;
-               for (int y = 0; y < height; y++) {
-                       for (int x = 0; x < width; x++) {
-                               if (source.getPixel(x, y) <= blackPoint)
-                                       matrix.set(x, y);
-                       }
-               }
-               return matrix_ref;
-       }
-       
-       int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
-               int numBuckets = histogram.size();
-               int maxBucketCount = 0;
-               
-               
-               // Find tallest peak in histogram
-               int firstPeak = 0;
-               int firstPeakSize = 0;
-               for (int i = 0; i < numBuckets; i++) {
-                       if (histogram[i] > firstPeakSize) {
-                               firstPeak = i;
-                               firstPeakSize = histogram[i];
-                       }
-                       if (histogram[i] > maxBucketCount) {
-                               maxBucketCount = histogram[i];
-                       }
-               }
-               
-               // Find second-tallest peak -- well, another peak that is tall and not
-               // so close to the first one
-               int secondPeak = 0;
-               int secondPeakScore = 0;
-               for (int i = 0; i < numBuckets; i++) {
-                       int distanceToBiggest = i - firstPeak;
-                       // Encourage more distant second peaks by multiplying by square of distance
-                       int score = histogram[i] * distanceToBiggest * distanceToBiggest;
-                       if (score > secondPeakScore) {
-                               secondPeak = i;
-                               secondPeakScore = score;
-                       }
-               }
-               
-               // Put firstPeak first
-               if (firstPeak > secondPeak) {
-                       int temp = firstPeak;
-                       firstPeak = secondPeak;
-                       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"
-               if (secondPeak - firstPeak <= numBuckets >> 4) {
-                       throw IllegalArgumentException("Too little dynamic range in luminance");
-               }
-               
-               // Find a valley between them that is low and closer to the white peak
-               int bestValley = secondPeak - 1;
-               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]);
-                       if (score > bestValleyScore) {
-                               bestValley = i;
-                               bestValleyScore = score;
-                       }
-               }
-               
-               return bestValley;
-       }
+using namespace std;
+
+const int LUMINANCE_BITS = 5;
+const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
+const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
+
+GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
+  Binarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) {
+
+}
+
+GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
+}
+
+
+Ref<BitArray> GlobalHistogramBinarizer::getBlackRow(int y, Ref<BitArray> row) {
+
+  if (row == cached_row_num_) {
+    return cached_row_;
+  }
+
+  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 = new unsigned char[width];
+  getLuminanceSource()->getRow(y,row_pixels);
+  for (int x = 0; x < width; x++) {
+    histogram[row_pixels[x] >> LUMINANCE_SHIFT]++;
+  }
+  int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
+
+
+  Ref<BitArray> array_ref(new BitArray(width));
+  BitArray& array = *array_ref;
+
+  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_ = array_ref;
+  cached_row_num_ = y;
        
+  delete [] row_pixels;
+  return array_ref;
+}
+
+Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix() {
+
+  if (cached_matrix_ != NULL) {
+    return cached_matrix_;
+  }
+
+  // Faster than working with the reference
+  LuminanceSource& source = *getLuminanceSource();
+  int width = source.getWidth();
+  int height = source.getHeight();
+  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.
+  unsigned char* row = new unsigned char[width];
+  for (int y = 1; y < 5; y++) {
+    int rownum = height * y / 5;
+    int right = (width << 2) / 5;
+    int sdf;
+    getLuminanceSource()->getRow(rownum,row);
+    for (int x = width / 5; x < right; x++) {
+      histogram[row[x] >> LUMINANCE_SHIFT]++;
+      sdf = histogram[row[x] >> LUMINANCE_SHIFT];
+    }
+  }
+
+  int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
+
+  Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
+  BitMatrix& matrix = *matrix_ref;
+  for (int y = 0; y < height; y++) {
+    getLuminanceSource()->getRow(y,row);
+    for (int x = 0; x < width; x++) {
+      if (row[x] <= blackPoint)
+        matrix.set(x, y);
+    }
+  }
+
+  cached_matrix_ = matrix_ref;
+
+  delete [] row;
+  return matrix_ref;
+}
+
+int GlobalHistogramBinarizer::estimate(vector<int> &histogram) {
+  int numBuckets = histogram.size();
+  int maxBucketCount = 0;
+
+
+  // Find tallest peak in histogram
+  int firstPeak = 0;
+  int firstPeakSize = 0;
+  for (int i = 0; i < numBuckets; i++) {
+    if (histogram[i] > firstPeakSize) {
+      firstPeak = i;
+      firstPeakSize = histogram[i];
+    }
+    if (histogram[i] > maxBucketCount) {
+      maxBucketCount = histogram[i];
+    }
+  }
+
+  // Find second-tallest peak -- well, another peak that is tall and not
+  // so close to the first one
+  int secondPeak = 0;
+  int secondPeakScore = 0;
+  for (int i = 0; i < numBuckets; i++) {
+    int distanceToBiggest = i - firstPeak;
+    // Encourage more distant second peaks by multiplying by square of distance
+    int score = histogram[i] * distanceToBiggest * distanceToBiggest;
+    if (score > secondPeakScore) {
+      secondPeak = i;
+      secondPeakScore = score;
+    }
+  }
+
+  // Put firstPeak first
+  if (firstPeak > secondPeak) {
+    int temp = firstPeak;
+    firstPeak = secondPeak;
+    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"
+  if (secondPeak - firstPeak <= numBuckets >> 4) {
+    throw IllegalArgumentException("Too little dynamic range in luminance");
+  }
+
+  // Find a valley between them that is low and closer to the white peak
+  int bestValley = secondPeak - 1;
+  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]);
+    if (score > bestValleyScore) {
+      bestValley = i;
+      bestValleyScore = score;
+    }
+  }
+
+  return bestValley;
+}
+
+Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
+  return Ref<Binarizer> (new GlobalHistogramBinarizer(source));
 }
+
+} // namespace zxing
+