* @return The luminance as an int, from 0-255
*/
@Override
- protected int getLuminance(int x, int y) {
+ public int getLuminance(int x, int y) {
return mYUVData[(y + mCropTop) * mDataWidth + x + mCropLeft] & 0xff;
}
@Override
- protected int[] getLuminanceRow(int y, int[] row) {
+ public int[] getLuminanceRow(int y, int[] row) {
int width = getWidth();
if (row == null || row.length < width) {
row = new int[width];
}
@Override
- protected int[] getLuminanceColumn(int x, int[] column) {
+ public int[] getLuminanceColumn(int x, int[] column) {
int height = getHeight();
if (column == null || column.length < height) {
column = new int[height];
}
@Override
- protected int getLuminance(int x, int y) {
+ public int getLuminance(int x, int y) {
return mLuminances[y * getWidth() + x] & 0xff;
}
@Override
- protected int[] getLuminanceRow(int y, int[] row) {
+ public int[] getLuminanceRow(int y, int[] row) {
int width = getWidth();
if (row == null || row.length < width) {
row = new int[width];
}
@Override
- protected int[] getLuminanceColumn(int x, int[] column) {
+ public int[] getLuminanceColumn(int x, int[] column) {
int width = getWidth();
int height = getHeight();
if (column == null || column.length < height) {
*/
boolean isRotateSupported();
+ /**
+ * Retrieves the luminance at the pixel x,y in the bitmap. This method is only used for estimating
+ * the black point and implementing getBlackRow() - it is not meant for decoding, hence it is not
+ * part of MonochromeBitmapSource itself, and is protected.
+ *
+ * @param x The x coordinate in the image.
+ * @param y The y coordinate in the image.
+ * @return The luminance value between 0 and 255.
+ */
+ public abstract int getLuminance(int x, int y);
+
+ /**
+ * This is the main mechanism for retrieving luminance data. It is dramatically more efficient
+ * than repeatedly calling getLuminance(). As above, this is not meant for decoders.
+ *
+ * @param y The row to fetch
+ * @param row The array to write luminance values into. It is <b>strongly</b> suggested that you
+ * allocate this yourself, making sure row.length >= getWidth(), and reuse the same
+ * array on subsequent calls for performance. If you pass null, you will be flogged,
+ * but then I will take pity on you and allocate a sufficient array internally.
+ * @return The array containing the luminance data. This is the same as row if it was usable.
+ */
+ public abstract int[] getLuminanceRow(int y, int[] row);
+
+ /**
+ * The same as getLuminanceRow(), but for columns.
+ *
+ * @param x The column to fetch
+ * @param column The array to write luminance values into. See above.
+ * @return The array containing the luminance data.
+ */
+ public abstract int[] getLuminanceColumn(int x, int[] column);
+
}
*/
public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSource {
- private static final int LUMINANCE_BITS = 5;
- private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
- private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
-
private final int height;
private final int width;
private int blackPoint;
private BlackPointEstimationMethod lastMethod;
private int lastArgument;
- private int[] luminances;
+ private int[] luminances = null;
protected BaseMonochromeBitmapSource(int width, int height) {
this.height = height;
public void estimateBlackPoint(BlackPointEstimationMethod method, int argument)
throws ReaderException {
if (!method.equals(lastMethod) || argument != lastArgument) {
- int width = getWidth();
- int height = getHeight();
- int[] histogram = new int[LUMINANCE_BUCKETS];
- if (method.equals(BlackPointEstimationMethod.TWO_D_SAMPLING)) {
- int minDimension = width < height ? width : height;
- int startX = (width - minDimension) >> 1;
- int startY = (height - minDimension) >> 1;
- for (int n = 0; n < minDimension; n++) {
- int luminance = getLuminance(startX + n, startY + n);
- histogram[luminance >> LUMINANCE_SHIFT]++;
- }
- } else if (method.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {
- if (argument < 0 || argument >= height) {
- throw new IllegalArgumentException("Row is not within the image: " + argument);
- }
- initLuminances();
- luminances = getLuminanceRow(argument, luminances);
- for (int x = 0; x < width; x++) {
- histogram[luminances[x] >> LUMINANCE_SHIFT]++;
- }
- } else {
- throw new IllegalArgumentException("Unknown method");
- }
- blackPoint = BlackPointEstimator.estimate(histogram) << LUMINANCE_SHIFT;
+ blackPoint = BlackPointEstimator.estimate(this, method, argument);
lastMethod = method;
lastArgument = argument;
}
* @param y The y coordinate in the image.
* @return The luminance value between 0 and 255.
*/
- protected abstract int getLuminance(int x, int y);
+ public abstract int getLuminance(int x, int y);
/**
* This is the main mechanism for retrieving luminance data. It is dramatically more efficient
* but then I will take pity on you and allocate a sufficient array internally.
* @return The array containing the luminance data. This is the same as row if it was usable.
*/
- protected abstract int[] getLuminanceRow(int y, int[] row);
+ public abstract int[] getLuminanceRow(int y, int[] row);
/**
* The same as getLuminanceRow(), but for columns.
* @param column The array to write luminance values into. See above.
* @return The array containing the luminance data.
*/
- protected abstract int[] getLuminanceColumn(int x, int[] column);
+ public abstract int[] getLuminanceColumn(int x, int[] column);
}
\r
package com.google.zxing.common;\r
\r
+import com.google.zxing.BlackPointEstimationMethod;\r
+import com.google.zxing.MonochromeBitmapSource;\r
import com.google.zxing.ReaderException;\r
\r
/**\r
* <a href="http://webdiis.unizar.es/~neira/12082/thresholding.pdf">http://webdiis.unizar.es/~neira/12082/thresholding.pdf</a>.\r
* </p>\r
*\r
+ * NOTE: This class is not threadsafe.\r
+ *\r
* @author Sean Owen\r
* @author dswitkin@google.com (Daniel Switkin)\r
*/\r
public final class BlackPointEstimator {\r
\r
+ private static final int LUMINANCE_BITS = 5;\r
+ private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;\r
+ private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;\r
+\r
+ private static int[] luminances = null;\r
+ private static int[] histogram = null;\r
+\r
private BlackPointEstimator() {\r
}\r
\r
+ private static void initArrays(int luminanceSize) {\r
+ if (luminances == null || luminances.length < luminanceSize) {\r
+ luminances = new int[luminanceSize];\r
+ }\r
+ if (histogram == null) {\r
+ histogram = new int[LUMINANCE_BUCKETS];\r
+ } else {\r
+ for (int x = 0; x < LUMINANCE_BUCKETS; x++) {\r
+ histogram[x] = 0;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Calculates the black point for the supplied bitmap.\r
+ *\r
+ * @param source The bitmap to analyze.\r
+ * @param method The pixel sampling technique to use.\r
+ * @param argument The row index in the case of ROW_SAMPLING, otherwise ignored.\r
+ * @return The black point as an integer 0-255.\r
+ * @throws ReaderException An exception thrown if the blackpoint cannot be determined.\r
+ */\r
+ public static int estimate(MonochromeBitmapSource source, BlackPointEstimationMethod method,\r
+ int argument) throws ReaderException {\r
+ int width = source.getWidth();\r
+ int height = source.getHeight();\r
+ initArrays(width);\r
+\r
+ if (method.equals(BlackPointEstimationMethod.TWO_D_SAMPLING)) {\r
+ int minDimension = width < height ? width : height;\r
+ int startX = (width - minDimension) >> 1;\r
+ int startY = (height - minDimension) >> 1;\r
+ for (int n = 0; n < minDimension; n++) {\r
+ int luminance = source.getLuminance(startX + n, startY + n);\r
+ histogram[luminance >> LUMINANCE_SHIFT]++;\r
+ }\r
+ } else if (method.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {\r
+ if (argument < 0 || argument >= height) {\r
+ throw new IllegalArgumentException("Row is not within the image: " + argument);\r
+ }\r
+\r
+ luminances = source.getLuminanceRow(argument, luminances);\r
+ for (int x = 0; x < width; x++) {\r
+ histogram[luminances[x] >> LUMINANCE_SHIFT]++;\r
+ }\r
+ } else {\r
+ throw new IllegalArgumentException("Unknown method");\r
+ }\r
+ return findBestValley(histogram) << LUMINANCE_SHIFT;\r
+ }\r
+\r
/**\r
* <p>Given an array of <em>counts</em> of luminance values (i.e. a histogram), this method\r
* decides which bucket of values corresponds to the black point -- which bucket contains the\r
* count of the brightest luminance values that should be considered "black".</p>\r
*\r
- * @param histogram an array of <em>counts</em> of luminance values\r
+ * @param buckets an array of <em>counts</em> of luminance values\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\r
*/\r
- public static int estimate(int[] histogram) throws ReaderException{\r
- int numBuckets = histogram.length;\r
+ public static int findBestValley(int[] buckets) throws ReaderException {\r
+ int numBuckets = buckets.length;\r
int maxBucketCount = 0;\r
// Find tallest peak in histogram\r
int firstPeak = 0;\r
int firstPeakSize = 0;\r
for (int i = 0; i < numBuckets; i++) {\r
- if (histogram[i] > firstPeakSize) {\r
+ if (buckets[i] > firstPeakSize) {\r
firstPeak = i;\r
- firstPeakSize = histogram[i];\r
+ firstPeakSize = buckets[i];\r
}\r
- if (histogram[i] > maxBucketCount) {\r
- maxBucketCount = histogram[i];\r
+ if (buckets[i] > maxBucketCount) {\r
+ maxBucketCount = buckets[i];\r
}\r
}\r
\r
for (int i = 0; i < numBuckets; i++) {\r
int distanceToBiggest = i - firstPeak;\r
// Encourage more distant second peaks by multiplying by square of distance\r
- int score = histogram[i] * distanceToBiggest * distanceToBiggest;\r
+ int score = buckets[i] * distanceToBiggest * distanceToBiggest;\r
if (score > secondPeakScore) {\r
secondPeak = i;\r
secondPeakScore = score;\r
secondPeak = temp;\r
}\r
\r
- // Kind of aribtrary; if the two peaks are very close, then we figure there is so little\r
+ // Kind of arbitrary; 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
int fromFirst = i - firstPeak;\r
// Favor a "valley" that is not too close to either peak -- especially not the black peak --\r
// and that has a low value of course\r
- int score = fromFirst * fromFirst * (secondPeak - i) * (maxBucketCount - histogram[i]);\r
+ int score = fromFirst * fromFirst * (secondPeak - i) * (maxBucketCount - buckets[i]);\r
if (score > bestValleyScore) {\r
bestValley = i;\r
bestValleyScore = score;\r
return delegate.isRotateSupported();
}
+ public int getLuminance(int x, int y) {
+ return delegate.getLuminance(x, y);
+ }
+
+ public int[] getLuminanceRow(int y, int[] row) {
+ return delegate.getLuminanceRow(y, row);
+ }
+
+ public int[] getLuminanceColumn(int x, int[] column) {
+ return delegate.getLuminanceColumn(x, column);
+ }
+
}
+++ /dev/null
-/*
- * Copyright 2008 ZXing authors
- *
- * 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.
- */
-
-package com.google.zxing.common;
-
-import com.google.zxing.ReaderException;
-import junit.framework.TestCase;
-
-/**
- * @author Sean Owen
- */
-public final class BlackPointEstimationMethodTestCase extends TestCase {
-
- public void testBasic() throws ReaderException {
- int[] histogram = { 0, 0, 11, 43, 37, 18, 3, 1, 0, 0, 13, 36, 24, 0, 11, 2 };
- int point = BlackPointEstimator.estimate(histogram);
- assertEquals(8, point);
- }
-
- public void testTooLittleRange() {
- try {
- int[] histogram = { 0, 0, 0, 0, 0, 0, 1, 43, 48, 18, 3, 1, 0, 0, 0, 0 };
- BlackPointEstimator.estimate(histogram);
- fail("Should have thrown an exception");
- } catch (ReaderException re) {
- // good
- }
- }
-
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * 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.
+ */
+
+package com.google.zxing.common;
+
+import com.google.zxing.ReaderException;
+import junit.framework.TestCase;
+
+/**
+ * @author Sean Owen
+ */
+public final class BlackPointEstimatorTestCase extends TestCase {
+
+ public void testBasic() throws ReaderException {
+ int[] histogram = { 0, 0, 11, 43, 37, 18, 3, 1, 0, 0, 13, 36, 24, 0, 11, 2 };
+ int point = BlackPointEstimator.findBestValley(histogram);
+ assertEquals(8, point);
+ }
+
+ public void testTooLittleRange() {
+ try {
+ int[] histogram = { 0, 0, 0, 0, 0, 0, 1, 43, 48, 18, 3, 1, 0, 0, 0, 0 };
+ BlackPointEstimator.findBestValley(histogram);
+ fail("Should have thrown an exception");
+ } catch (ReaderException re) {
+ // good
+ }
+ }
+
+}
\ No newline at end of file
}
// This is expensive and should be used very sparingly.
- protected int getLuminance(int x, int y) {
+ public int getLuminance(int x, int y) {
image.getRGB(pixelHolder, 0, getWidth(), x, y, 1, 1);
int pixel = pixelHolder[0];
}
// For efficiency, the RGB data and the luminance data share the same array.
- protected int[] getLuminanceRow(int y, int[] row) {
+ public int[] getLuminanceRow(int y, int[] row) {
int width = getWidth();
if (row == null || row.length < width) {
row = new int[width];
return row;
}
- protected int[] getLuminanceColumn(int x, int[] column) {
+ public int[] getLuminanceColumn(int x, int[] column) {
int height = getHeight();
if (column == null || column.length < height) {
column = new int[height];
* where R, G, and B are values in [0,1].
*/
@Override
- protected int getLuminance(int x, int y) {
+ public int getLuminance(int x, int y) {
int pixel = image.getRGB(left + x, top + y);
// Coefficients add up to 1024 to make the divide into a fast shift
return (306 * ((pixel >> 16) & 0xFF) +
}
@Override
- protected int[] getLuminanceRow(int y, int[] row) {
+ public int[] getLuminanceRow(int y, int[] row) {
int width = getWidth();
if (row == null || row.length < width) {
row = new int[width];
}
@Override
- protected int[] getLuminanceColumn(int x, int[] column) {
+ public int[] getLuminanceColumn(int x, int[] column) {
int height = getHeight();
if (column == null || column.length < height) {
column = new int[height];