Zxing 128条码识别

Zxing

官方地址,我这里下载的的ZXing 3.4.1 版本,下载解压后需要将其中几个模块分别导入Android Studio工程,同时修改build.gralde的引用便可以编译Android 测试APK.
工程截图,我这里仅琢磨了108条码的识别,查看源码逻辑后,发现其他编码的解码与编码使用的相同的思路。

1. 打开相机(简单描述)

    1.1 com.google.zxing.client.android.PreviewCallback,获取 Android Camera接口获取相机数据

Camera Data
1.2 在同包名的CaptureActivityHandler中 将DecodeThread.getHandler传递给 PreviewCallback,以此来将相机数据传递给DecodeThread
Handler
1.3 DecodeThread构造方法中负责配置decodeFormat也就是识别的码值类型
1.4 DecodeHandler的handleMessage(),到这里所有的相机数据便进入识别
Decode Data

2. Decode数据处理

2.1 Decode 获取扫码框内的数据YUV,并将其封装PlanarYUVLuminanceSource对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);

* This object extends LuminanceSource around an array of YUV data returned from the camera driver,
* with the option to crop to a rectangle within the full data. This can be used to exclude
* superfluous pixels around the perimeter and speed up decoding.
*
* It works for any pixel format where the Y channel is planar and appears first, including
* YCbCr_420_SP and YCbCr_422_SP.

封装YUV数据,分离Y通道
//因为是108条码识别,本质上只需要截取任意一行,根据点的分布即可
@Override
public byte[] getRow(int y, byte[] row) {
if (y < 0 || y >= getHeight()) {
throw new IllegalArgumentException("Requested row is outside the image: " + y);
}
int width = getWidth();
if (row == null || row.length < width) {
row = new byte[width];
}
int offset = (y + top) * dataWidth + left;
System.arraycopy(yuvData, offset, row, 0, width);
return row;
}

演示

2.2 使用source,new HybridBinarizer(source),构造HyBridBinarizer类,其父类GlobalHistogramBinarizer中便是关键,也是解码的主要操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
LuminanceSource source = getLuminanceSource();
int width = source.getWidth();
if (row == null || row.getSize() < width) {
row = new BitArray(width);
} else {
row.clear();
}
initArrays(width);//初始化BitArray
byte[] localLuminances = source.getRow(y, luminances);
savelocalLuminances(localLuminances);//此处为自己添加
int[] localBuckets = buckets;
for (int x = 0; x < width; x++) {//锐化后构建颜色直方图
localBuckets[(localLuminances[x] & 0xff) >> LUMINANCE_SHIFT]++;
}
//获取直方图中第一高点,第二高点 以及2点之间的波谷
int blackPoint = estimateBlackPoint(localBuckets);

if (width < 3) {
// Special case for very small images
for (int x = 0; x < width; x++) {
if ((localLuminances[x] & 0xff) < blackPoint) {
row.set(x);
}
}
} else {//使用一个-1 4 -1 过滤对数据进行锐化
int left = localLuminances[0] & 0xff;
int center = localLuminances[1] & 0xff;
for (int x = 1; x < width - 1; x++) {
int right = localLuminances[x + 1] & 0xff;
// A simple -1 4 -1 box filter with a weight of 2.
if (((center * 4) - left - right) / 2 < blackPoint) {
row.set(x);
}
left = center;
center = right;
}
}
return row;
}

如下流程有待完善,等待后期再次更新
Y数据处理说明图
获取直方图波谷
-1 4 -1 filter转BitArray

2.3 解码
Code128Reader解码BitArray,可以看到108条码有三种类型
BitArray解码

3. 结论

3.1 解码过程有待细化以及对算法进行调整
3.2 条形码的矩形框依赖于人工,可通过TFLite 对象检测来进行识别,自动送入解码过程
3.3 源码中的实现我自己测试不是响应很快,有时间需要再看看
3.4 BitArray转编码过程还未调试,需要再次阅读源码更新