Fixed bug in rotation code for BufferedImageMonochromeBitmapSource; fixed "SKIP_N_BAR...
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Wed, 19 Mar 2008 17:09:27 +0000 (17:09 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Wed, 19 Mar 2008 17:09:27 +0000 (17:09 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@297 59b500cc-1b3d-0410-9834-0bbf25fbcc57

android/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
android/src/com/google/zxing/client/android/YUVMonochromeBitmapSource.java
core/src/com/google/zxing/common/BlackPointEstimator.java
core/src/com/google/zxing/oned/AbstractOneDReader.java
javame/src/com/google/zxing/client/j2me/LCDUIImageMonochromeBitmapSource.java
javase/src/com/google/zxing/client/j2se/BufferedImageMonochromeBitmapSource.java

index 3b9cac9..962c905 100755 (executable)
@@ -19,6 +19,7 @@ package com.google.zxing.client.android;
 import android.graphics.Bitmap;
 import com.google.zxing.BlackPointEstimationMethod;
 import com.google.zxing.MonochromeBitmapSource;
+import com.google.zxing.ReaderException;
 import com.google.zxing.common.BitArray;
 import com.google.zxing.common.BlackPointEstimator;
 
@@ -77,7 +78,7 @@ final class RGBMonochromeBitmapSource implements MonochromeBitmapSource {
     return image.width();
   }
 
-  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) {
+  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
     if (!method.equals(lastMethod) || argument != lastArgument) {
       int width = image.width();
       int height = image.height();
index 4c173d6..b50e16c 100755 (executable)
@@ -19,6 +19,7 @@ package com.google.zxing.client.android;
 import android.graphics.Bitmap;
 import com.google.zxing.BlackPointEstimationMethod;
 import com.google.zxing.MonochromeBitmapSource;
+import com.google.zxing.ReaderException;
 import com.google.zxing.common.BitArray;
 import com.google.zxing.common.BlackPointEstimator;
 
@@ -79,7 +80,7 @@ final class YUVMonochromeBitmapSource implements MonochromeBitmapSource {
     return image.width();
   }
 
-  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) {
+  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
     if (!method.equals(lastMethod) || argument != lastArgument) {
       int width = image.width();
       int height = image.height();
index c5bc0ed..a9fc793 100644 (file)
@@ -16,6 +16,8 @@
 \r
 package com.google.zxing.common;\r
 \r
+import com.google.zxing.ReaderException;\r
+\r
 /**\r
  * <p>Encapsulates logic that estimates the optimal "black point", the luminance value\r
  * which is the best line between "white" and "black" in a grayscale image.</p>\r
@@ -43,8 +45,9 @@ public final class BlackPointEstimator {
    * than 0.0; 1.0 is a good "default"\r
    * @return index within argument of bucket corresponding to brightest values which should be\r
    *         considered "black"\r
+   * @throws ReaderException if "black" and "white" appear to be very close in luminance in the image\r
    */\r
-  public static int estimate(int[] histogram, float biasTowardsWhite) {\r
+  public static int estimate(int[] histogram, float biasTowardsWhite) throws ReaderException{\r
 \r
     if (Float.isNaN(biasTowardsWhite) || biasTowardsWhite <= 0.0f) {\r
       throw new IllegalArgumentException("Illegal biasTowardsWhite: " + biasTowardsWhite);\r
@@ -83,6 +86,15 @@ public final class BlackPointEstimator {
       secondPeak = temp;\r
     }\r
 \r
+    // Kind of aribtrary; if the two peaks are very close, then we figure there is so little\r
+    // dynamic range in the image, that discriminating black and white is too error-prone.\r
+    // Decoding the image/line is either pointless, or may in some cases lead to a false positive\r
+    // for 1D formats, which are relatively lenient.\r
+    // We arbitrarily say "close" is "fewer than 1/8 of the total histogram buckets apart"\r
+    if (secondPeak - firstPeak < histogram.length >> 3) {\r
+      throw new ReaderException("Too little dynamic range in luminance");\r
+    }\r
+\r
     // Find a valley between them that is low and closer to the white peak\r
     int bestValley = secondPeak - 1;\r
     int bestValleyScore = -1;\r
index e5effa6..53a04a3 100644 (file)
@@ -82,20 +82,19 @@ public abstract class AbstractOneDReader implements OneDReader {
     // the middle. So we'd scan row middle, then middle - rowStep, then middle + rowStep,
     // then middle - 2*rowStep, etc.
     // rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily decided
-    // that moving up and down by about 1/16 of the image is pretty good.
+    // that moving up and down by about 1/16 of the image is pretty good; we try more of the image if
+    // "trying harder"
     int middle = height >> 1;
-    int rowStep;
-    if (tryHarder) {
-      rowStep = 2; // Look at every other line if "trying harder"
-    } else {
-      rowStep = Math.max(1, height >> 4);
-    }
+    int rowStep = Math.max(1, height >> (tryHarder ? 7 : 4));
     int maxLines;
     if (tryHarder || barcodesToSkip > 0) {
       maxLines = height; // Look at the whole image; looking for more than one barcode
     } else {
       maxLines = 7;
     }
+
+    Result lastResult = null;
+
     for (int x = 0; x < maxLines; x++) {
 
       int rowStepsAboveOrBelow = (x + 1) >> 1;
@@ -105,34 +104,40 @@ public abstract class AbstractOneDReader implements OneDReader {
         break;
       }
 
-      image.estimateBlackPoint(BlackPointEstimationMethod.ROW_SAMPLING, rowNumber);
+      try {
+        image.estimateBlackPoint(BlackPointEstimationMethod.ROW_SAMPLING, rowNumber);
+      } catch (ReaderException re) {
+        continue;
+      }
       image.getBlackRow(rowNumber, row, 0, width);
 
-      try {
-        Result result = decodeRow(rowNumber, row, hints);
-        if (barcodesToSkip > 0) { // See if we should skip and keep looking
-          barcodesToSkip--;
-        } else {
-          return result;
+      for (int attempt = 0; attempt < 2; attempt++) {
+        if (attempt == 1) { // trying again?
+          if (tryHarder) { // only if "trying harder"
+            row.reverse(); // reverse the row and continue
+          } else {
+            break;
+          }
         }
-      } catch (ReaderException re) {
-        if (tryHarder) {
-          row.reverse(); // try scanning the row backwards
-          try {
-            Result result = decodeRow(rowNumber, row, hints);
+        try {
+          Result result = decodeRow(rowNumber, row, hints);
+          if (lastResult == null || !lastResult.getText().equals(result.getText())) {
+            // Found new barcode, not just the last one again
             if (barcodesToSkip > 0) { // See if we should skip and keep looking
               barcodesToSkip--;
+              lastResult = result; // Remember what we just saw
             } else {
-              // Found it, but upside-down:
-              result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
+              if (attempt == 1) {
+                // Found it, but upside-down:
+                result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
+              }
               return result;
             }
-          } catch (ReaderException re2) {
-            // continue
           }
+        } catch (ReaderException re) {
+          // continue
         }
       }
-
     }
 
     throw new ReaderException("No barcode found");
index 5be7c58..e3f0548 100644 (file)
@@ -18,6 +18,7 @@ package com.google.zxing.client.j2me;
 
 import com.google.zxing.BlackPointEstimationMethod;
 import com.google.zxing.MonochromeBitmapSource;
+import com.google.zxing.ReaderException;
 import com.google.zxing.common.BitArray;
 import com.google.zxing.common.BlackPointEstimator;
 
@@ -77,7 +78,7 @@ public final class LCDUIImageMonochromeBitmapSource implements MonochromeBitmapS
     return width;
   }
 
-  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) {
+  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
     if (!method.equals(lastMethod) || argument != lastArgument) {
       int[] histogram = new int[LUMINANCE_BUCKETS];
       float biasTowardsWhite = 1.0f;
index 5030d7b..a4a0e64 100644 (file)
@@ -18,6 +18,7 @@ package com.google.zxing.client.j2se;
 
 import com.google.zxing.BlackPointEstimationMethod;
 import com.google.zxing.MonochromeBitmapSource;
+import com.google.zxing.ReaderException;
 import com.google.zxing.common.BitArray;
 import com.google.zxing.common.BlackPointEstimator;
 
@@ -51,6 +52,10 @@ public final class BufferedImageMonochromeBitmapSource implements MonochromeBitm
     lastArgument = 0;
   }
 
+  public BufferedImage getImage() {
+    return image;
+  }
+
   public boolean isBlack(int x, int y) {
     return computeRGBLuminance(image.getRGB(x, y)) < blackPoint;
   }
@@ -78,7 +83,7 @@ public final class BufferedImageMonochromeBitmapSource implements MonochromeBitm
     return image.getWidth();
   }
 
-  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) {
+  public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
     if (!method.equals(lastMethod) || argument != lastArgument) {
       int width = image.getWidth();
       int height = image.getHeight();
@@ -120,8 +125,9 @@ public final class BufferedImageMonochromeBitmapSource implements MonochromeBitm
       throw new IllegalStateException("Rotate not supported");
     }
     // 90 degrees counterclockwise:
-    AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, image.getHeight());
+    AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, image.getWidth());
     BufferedImageOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+    // Note width/height are flipped since we are rotating 90 degrees:
     BufferedImage rotatedImage = new BufferedImage(image.getHeight(), image.getWidth(), image.getType());
     op.filter(image, rotatedImage);
     return new BufferedImageMonochromeBitmapSource(rotatedImage);