现如今zxing是作为普遍二维码库扫描封装的较好的库了,但是我们都知道zxing代码是开源的,是可以更改的,那么我就来看看要提高识别率的问题要怎么处理?
扫描精度问题:
使用过zxing自带的二维码扫描程序来识别二维码的童鞋应该知道,zxing二维码的扫描程序很慢,而且有可能扫不出来。zxing在配置相机参数和二维码扫描程序参数的时候,配置都比较保守,兼顾了低端手机,并且兼顾了多种条形码的识别。如果说仅仅是拿zxing项目来扫描和识别二维码的话,完全可以对项目中的一些配置做精简,并针对二维码的识别做优化。
PlanarYUVLuminanceSource
官方的解码程序主要是下边这段代码:
private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;
// 构造基于平面的YUV亮度源,即包含二维码区域的数据源
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
if (source != null) {
// 构造二值图像比特流,使用HybridBinarizer算法解析数据源
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try {
// 采用MultiFormatReader解析图像,可以解析多种数据格式
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
// continue
} finally {
multiFormatReader.reset();
}
}
···
// Hanlder处理解析失败或成功的结果
···
}
再来看看YUV亮度源是怎么构造的,在CameraManager里,首先获取预览图像的聚焦框矩形getFramingRect(),这个聚焦框的矩形大小是根据屏幕的宽高值来做计算的,官方定义了最小和最大的聚焦框大小,分别是240240和1200675,即最多的聚焦框大小为屏幕宽高的5/8。获取屏幕的聚焦框大小后,还需要做从屏幕分辨率到相机分辨率的转换才能得到预览聚焦框的大小,这个转换在getFramingRectInPreview()里完成。这样便完成了亮度源的构造。
private static final int MIN_FRAME_WIDTH = 240;
private static final int MIN_FRAME_HEIGHT = 240;
private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080
/** * A factory method to build the appropriate LuminanceSource object based on the format of the preview buffers, as * described by Camera.Parameters. * * @param data A preview frame. * @param width The width of the image. * @param height The height of the image. * @return A PlanarYUVLuminanceSource instance. */
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
// 取得预览框内的矩形
Rect rect = getFramingRectInPreview(); if (rect == null) { return null;
}
// Go ahead and assume it's YUV rather than die.
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
}
/** * Like {@link #getFramingRect} but coordinates are in terms of the preview frame, not UI / screen. * * @return {@link Rect} expressing barcode scan area in terms of the preview size */
public synchronized Rect getFramingRectInPreview() { if (framingRectInPreview == null) {
Rect framingRect = getFramingRect(); if (framingRect == null) { return null;
}
// 获取相机分辨率和屏幕分辨率
Rect rect = new Rect(framingRect);
Point cameraResolution = configManager.getCameraResolution();
Point screenResolution = configManager.getScreenResolution(); if (cameraResolution == null || screenResolution == null) { // Called early, before init even finished
return null;
}
// 根据相机分辨率和屏幕分辨率的比例对屏幕中央聚焦框进行调整
rect.left = rect.left * cameraResolution.x / screenResolution.x;
rect.right = rect.right * cameraResolution.x / screenResolution.x;
rect.top = rect.top * cameraResolution.y / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
framingRectInPreview = rect;
}
return framingRectInPreview;
}
/** * Calculates the framing rect which the UI should draw to show the user where to place the barcode. This target * helps with alignment as well as forces the user to hold the device far enough away to ensure the image will be in * focus. * * @return The rectangle to draw on screen in window coordinates. */
public synchronized Rect getFramingRect() { if (framingRect == null) { if (camera == null) { return null;
}
// 获取屏幕的尺寸像素
Point screenResolution = configManager.getScreenResolution(); if (screenResolution == null) { // Called early, before init even finished
return null;
}
// 根据屏幕的宽高找到最合适的矩形框宽高值
int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); // 取屏幕中间的,宽为width,高为height的矩形框
int leftOffset = (screenResolution.x - width) / 2; int topOffset = (screenResolution.y - height) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
Log.d(TAG, "Calculated framing rect: " + framingRect);
}
return framingRect;
}
private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) { int dim = 5 * resolution / 8;