Skip to content

Commit 9e46a5f

Browse files
committed
add zbar Implementation
1 parent 228239b commit 9e46a5f

9 files changed

Lines changed: 510 additions & 22 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.zbar.lib;
2+
3+
public class ZbarManager {
4+
5+
static {
6+
System.loadLibrary("zbar");
7+
}
8+
9+
public native String decode(byte[] data, int width, int height, boolean isCrop, int x, int y, int cwidth, int cheight);
10+
}

android/src/main/java/org/reactnative/camera/tasks/BarCodeScannerAsyncTask.java

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
import com.google.zxing.NotFoundException;
66
import com.google.zxing.PlanarYUVLuminanceSource;
77
import com.google.zxing.Result;
8-
import com.google.zxing.common.HybridBinarizer;
8+
import com.google.zxing.ResultPoint;
9+
import com.google.zxing.BarcodeFormat;
10+
// import com.google.zxing.common.HybridBinarizer;
11+
// import com.google.zxing.common.GlobalHistogramBinarizer;
12+
import org.reactnative.camera.utils.GlobalHistogramBinarizer;
13+
import org.reactnative.camera.utils.HybridBinarizer;
14+
import com.google.zxing.PlanarYUVLuminanceSource;
15+
import com.zbar.lib.ZbarManager;
16+
17+
import android.util.Log;
918

1019
public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Result> {
1120
private byte[] mImageData;
@@ -14,6 +23,8 @@ public class BarCodeScannerAsyncTask extends android.os.AsyncTask<Void, Void, Re
1423
private BarCodeScannerAsyncTaskDelegate mDelegate;
1524
private final MultiFormatReader mMultiFormatReader;
1625

26+
private final ZbarManager mZarBarManager;
27+
1728
// note(sjchmiela): From my short research it's ok to ignore rotation of the image.
1829
public BarCodeScannerAsyncTask(
1930
BarCodeScannerAsyncTaskDelegate delegate,
@@ -27,6 +38,7 @@ public BarCodeScannerAsyncTask(
2738
mHeight = height;
2839
mDelegate = delegate;
2940
mMultiFormatReader = multiFormatReader;
41+
mZarBarManager = new ZbarManager();
3042
}
3143

3244
@Override
@@ -40,28 +52,22 @@ protected Result doInBackground(Void... ignored) {
4052
try {
4153
BinaryBitmap bitmap = generateBitmapFromImageData(mImageData, mWidth, mHeight);
4254
result = mMultiFormatReader.decodeWithState(bitmap);
43-
} catch (NotFoundException e) {
44-
BinaryBitmap bitmap = generateBitmapFromImageData(rotateImage(mImageData,mWidth, mHeight),mHeight,mWidth);
45-
try {
46-
result = mMultiFormatReader.decodeWithState(bitmap);
47-
} catch (NotFoundException e1) {
48-
//no barcode Found
55+
Log.d("codingpapi", "recognize with zxing result:" + result);
56+
if (result == null) {
57+
result = getQRByZbar(mImageData, mWidth, mHeight);
4958
}
59+
} catch (NotFoundException e) {
60+
result = getQRByZbar(mImageData, mWidth, mHeight);
61+
// No barcode found, result is already null.
5062
} catch (Throwable t) {
5163
t.printStackTrace();
5264
}
65+
if (result != null) {
66+
Log.d("codingpapi", "recognize with zxing result:" + result.getText());
67+
}
5368

5469
return result;
5570
}
56-
private byte[] rotateImage(byte[]imageData,int width, int height) {
57-
byte[] rotated = new byte[imageData.length];
58-
for (int y = 0; y < height; y++) {
59-
for (int x = 0; x < width; x++) {
60-
rotated[x * height + height - y - 1] = imageData[x + y * width];
61-
}
62-
}
63-
return rotated;
64-
}
6571
@Override
6672
protected void onPostExecute(Result result) {
6773
super.onPostExecute(result);
@@ -71,17 +77,44 @@ protected void onPostExecute(Result result) {
7177
mDelegate.onBarCodeScanningTaskCompleted();
7278
}
7379

74-
private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height) {
80+
private Result getQRByZbar(byte[] imageData, int width, int height) {
81+
int tX = width / 5;
82+
int tY = height / 4;
83+
int tXW = tX * 3;
84+
int tYW = tY * 2;
85+
86+
Result result = null;
87+
PlanarYUVLuminanceSource source = generateLuminanceSource(imageData, width, height);
88+
String stringResult = mZarBarManager.decode(source.getMatrix(), tXW, tYW, false, 0, 0, tXW, tYW);
89+
if (stringResult != null) {
90+
byte[] fakeYuvData = new byte[10];
91+
ResultPoint[] fakePoints = new ResultPoint[10];
92+
result = new Result(stringResult, fakeYuvData, fakePoints, BarcodeFormat.QR_CODE);
93+
}
94+
Log.d("codingpapi", "recognize with zbar result:" + result);
95+
return result;
96+
}
97+
98+
private PlanarYUVLuminanceSource generateLuminanceSource(byte[] imageData, int width, int height) {
99+
int tX = width / 5;
100+
int tY = height / 4;
101+
int tXW = tX * 3;
102+
int tYW = tY * 2;
75103
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
76104
imageData, // byte[] yuvData
77105
width, // int dataWidth
78106
height, // int dataHeight
79-
0, // int left
80-
0, // int top
81-
width, // int width
82-
height, // int height
107+
tX, // int left
108+
tY, // int top
109+
tXW, // int width
110+
tYW, // int height
83111
false // boolean reverseHorizontal
84112
);
85-
return new BinaryBitmap(new HybridBinarizer(source));
113+
return source;
114+
}
115+
116+
private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height) {
117+
PlanarYUVLuminanceSource source = generateLuminanceSource(imageData, width, height);
118+
return new BinaryBitmap(new GlobalHistogramBinarizer(source));
86119
}
87120
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* Copyright 2009 ZXing authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.reactnative.camera.utils;
18+
19+
import com.google.zxing.Binarizer;
20+
import com.google.zxing.LuminanceSource;
21+
import com.google.zxing.NotFoundException;
22+
import com.google.zxing.common.*;
23+
import android.util.Log;
24+
25+
/**
26+
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
27+
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
28+
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
29+
* and gradients.
30+
*
31+
* Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
32+
*
33+
* @author dswitkin@google.com (Daniel Switkin)
34+
* @author Sean Owen
35+
*/
36+
public class GlobalHistogramBinarizer extends Binarizer {
37+
38+
private static final int LUMINANCE_BITS = 5;
39+
private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
40+
private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
41+
private static final byte[] EMPTY = new byte[0];
42+
43+
private byte[] luminances;
44+
private final int[] buckets;
45+
46+
public GlobalHistogramBinarizer(LuminanceSource source) {
47+
super(source);
48+
luminances = EMPTY;
49+
buckets = new int[LUMINANCE_BUCKETS];
50+
}
51+
52+
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
53+
@Override
54+
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
55+
LuminanceSource source = getLuminanceSource();
56+
int width = source.getWidth();
57+
if (row == null || row.getSize() < width) {
58+
row = new BitArray(width);
59+
} else {
60+
row.clear();
61+
}
62+
63+
initArrays(width);
64+
byte[] localLuminances = source.getRow(y, luminances);
65+
int[] localBuckets = buckets;
66+
for (int x = 0; x < width; x++) {
67+
localBuckets[(localLuminances[x] & 0xff) >> LUMINANCE_SHIFT]++;
68+
}
69+
int blackPoint = estimateBlackPoint(localBuckets);
70+
71+
if (width < 3) {
72+
// Special case for very small images
73+
for (int x = 0; x < width; x++) {
74+
if ((localLuminances[x] & 0xff) < blackPoint) {
75+
row.set(x);
76+
}
77+
}
78+
} else {
79+
int left = localLuminances[0] & 0xff;
80+
int center = localLuminances[1] & 0xff;
81+
for (int x = 1; x < width - 1; x++) {
82+
int right = localLuminances[x + 1] & 0xff;
83+
// A simple -1 4 -1 box filter with a weight of 2.
84+
if (((center * 4) - left - right) / 2 < blackPoint) {
85+
row.set(x);
86+
}
87+
left = center;
88+
center = right;
89+
}
90+
}
91+
return row;
92+
}
93+
94+
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
95+
@Override
96+
public BitMatrix getBlackMatrix() throws NotFoundException {
97+
LuminanceSource source = getLuminanceSource();
98+
int width = source.getWidth();
99+
int height = source.getHeight();
100+
BitMatrix matrix = new BitMatrix(width, height);
101+
102+
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
103+
// more robust on the blackbox tests than sampling a diagonal as we used to do.
104+
initArrays(width);
105+
int[] localBuckets = buckets;
106+
for (int y = 1; y < 5; y++) {
107+
int row = height * y / 5;
108+
byte[] localLuminances = source.getRow(row, luminances);
109+
int right = (width * 4) / 5;
110+
for (int x = width / 5; x < right; x++) {
111+
int pixel = localLuminances[x] & 0xff;
112+
localBuckets[pixel >> LUMINANCE_SHIFT]++;
113+
}
114+
}
115+
int blackPoint = estimateBlackPoint(localBuckets);
116+
117+
// We delay reading the entire image luminance until the black point estimation succeeds.
118+
// Although we end up reading four rows twice, it is consistent with our motto of
119+
// "fail quickly" which is necessary for continuous scanning.
120+
byte[] localLuminances = source.getMatrix();
121+
for (int y = 0; y < height; y++) {
122+
int offset = y * width;
123+
for (int x = 0; x < width; x++) {
124+
int pixel = localLuminances[offset + x] & 0xff;
125+
if (pixel < blackPoint) {
126+
matrix.set(x, y);
127+
}
128+
}
129+
}
130+
131+
return matrix;
132+
}
133+
134+
@Override
135+
public Binarizer createBinarizer(LuminanceSource source) {
136+
return new GlobalHistogramBinarizer(source);
137+
}
138+
139+
private void initArrays(int luminanceSize) {
140+
if (luminances.length < luminanceSize) {
141+
luminances = new byte[luminanceSize];
142+
}
143+
for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
144+
buckets[x] = 0;
145+
}
146+
}
147+
148+
private static int estimateBlackPoint(int[] buckets) throws NotFoundException {
149+
// Find the tallest peak in the histogram.
150+
int numBuckets = buckets.length;
151+
int maxBucketCount = 0;
152+
int firstPeak = 0;
153+
int firstPeakSize = 0;
154+
for (int x = 0; x < numBuckets; x++) {
155+
if (buckets[x] > firstPeakSize) {
156+
firstPeak = x;
157+
firstPeakSize = buckets[x];
158+
}
159+
if (buckets[x] > maxBucketCount) {
160+
maxBucketCount = buckets[x];
161+
}
162+
}
163+
164+
// Find the second-tallest peak which is somewhat far from the tallest peak.
165+
int secondPeak = 0;
166+
int secondPeakScore = 0;
167+
for (int x = 0; x < numBuckets; x++) {
168+
int distanceToBiggest = x - firstPeak;
169+
// Encourage more distant second peaks by multiplying by square of distance.
170+
int score = buckets[x] * distanceToBiggest * distanceToBiggest;
171+
if (score > secondPeakScore) {
172+
secondPeak = x;
173+
secondPeakScore = score;
174+
}
175+
}
176+
177+
// Make sure firstPeak corresponds to the black peak.
178+
if (firstPeak > secondPeak) {
179+
int temp = firstPeak;
180+
firstPeak = secondPeak;
181+
secondPeak = temp;
182+
}
183+
184+
// If there is too little contrast in the image to pick a meaningful black point, throw rather
185+
// than waste time trying to decode the image, and risk false positives.
186+
if (secondPeak - firstPeak <= numBuckets / 16) {
187+
//throw NotFoundException.getNotFoundInstance();
188+
}
189+
190+
// Find a valley between them that is low and closer to the white peak.
191+
int bestValley = secondPeak - 1;
192+
int bestValleyScore = -1;
193+
for (int x = secondPeak - 1; x > firstPeak; x--) {
194+
int fromFirst = x - firstPeak;
195+
int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
196+
if (score > bestValleyScore) {
197+
bestValley = x;
198+
bestValleyScore = score;
199+
}
200+
}
201+
Log.d("mmmmm", "maxBucketCount:" + maxBucketCount + ",maxColor:" + firstPeak + "second:" + secondPeak + ",bestValley:" + bestValley + ",numBuckets:" + numBuckets);
202+
203+
return bestValley << LUMINANCE_SHIFT;
204+
}
205+
206+
}

0 commit comments

Comments
 (0)