GreyscaleRotatedLuminanceSource: implemented getMatrix()
[zxing.git] / cpp / core / src / zxing / common / GlobalHistogramBinarizer.cpp
index 68fbe55..cb7654f 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  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.
@@ -30,31 +31,92 @@ const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
 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];
     }
   }
 
@@ -63,19 +125,22 @@ Ref<BitMatrix> GlobalHistogramBinarizer::estimateBlackMatrix() {
   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;
@@ -110,11 +175,13 @@ int GlobalHistogramBinarizer::estimate(valarray<int> &histogram) {
     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");
   }
@@ -124,9 +191,10 @@ int GlobalHistogramBinarizer::estimate(valarray<int> &histogram) {
   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;
@@ -136,4 +204,8 @@ int GlobalHistogramBinarizer::estimate(valarray<int> &histogram) {
   return bestValley;
 }
 
+Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
+  return Ref<Binarizer> (new GlobalHistogramBinarizer(source));
 }
+
+} // namespace zxing