开源项目地址:https://github.com/zxing/zxing
新项目预研的时候,研究了下开源项目ZXing,总结如下:
公司对于decode和encode核心算法自己实现了一套(采用C/C++,Android使用jni),因为当时考虑到性能和识别正确性等问题,还有就是公司核心的东西,最好是自己掌握;而ZXing自带的decode和encode核心库,只需要关注MultiFormatReader、MultiFormatWriter、BitMatrix、PlanarYUVLuminanceSource及其相关的几个类即可。这里主要记录的笔记是关于使用ZXing自带的decode和encode库。
主要参考:
http://blog.csdn.net/hellogv/article/details/6101663,非常感谢这哥们儿。
http://code.google.com/p/zxing/issues/detail?id=1439,遇到的一个问题的解决方法。
1. 对二维码图片Decode
decode模块主要分为相机管理和解码。相机管理主要是对相机的预览(帧)和自动聚焦进行管理,并在相机的SurfaceView中进行预览;解码是在一个独立的线程中执行解码并回调到相机管理中。
相机管理的PreView(预览)、auto focus(自动聚焦)、相机的基本配置。
相机的auto focus,设计成一个循环的回调事件,通过回调方式循环实现自动定焦,AutoFocusCallback继承Camera.AutoFocusCallback,并重写public void onAutoFocus(boolean success, Camera camera)方法,实现每隔2s循环自动定焦;相机的预览回调是PreviewCallback,继承Camera.PreviewCalback,在相机扫描二维码图片的每一帧都会回调public void onPreviewFrame(byte[] data, Camera camera)方法,data就是当前图片帧的字节码数据,然后在该函数中对data数据进行解码,失败则继续回调该函数,成功则回调回相机管理模块。
对于相机的PreView得到的每一帧图片数据,相机驱动直接转换为了byte[]格式,我们可以直接拿来使用;但是对于从相册等文件路径中获得的图片本身,则需要转换为byte[]格式,方法如下:
bitmap = BitmapFactory.decodeFile(mPhotoPath, options); // 获得Bitmap
bitmap = scaleBitmap(mPhotoPath, options.outWidth, options.outHeight); // 缩放bitmap图片
ByteBuffer buf = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4); // 分配内存用于保存bitmap图片转换后的bytes
bitmap.copyPixelsToBuffer(buf);
byte[] bytes = buf.array();
接下来,就可以decode了。
使用Zxing下的MultiFormatReader类可以解析多格式二维码,MultiFormatReader的对象有个方法叫decode(BinaryBitmap bitmap),其中BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));该source是LuminanceSource的子类对象,它是一个通过yuv编码格式数组生成的对象(PlanarYUVLuminanceSource)。你现在需要把图片先转换成yuv格式,再通过上述方法识别。
代码如下:
/**
* 使用ZXing自带的decode算法实现解码
* @param data 二维码图片生成的字节码
* @param width 二维码图片的宽度
* @param height 二维码图片的高度
*/
private void decodeUsingZXing(byte[] data, int width, int height) {
Handler handler = mCallback.getHandlerScanCode();
PlanarYUVLuminanceSource source = HimCameraManager.get().buildLuminanceSource(data, width, height);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
MultiFormatReader reader = new MultiFormatReader();
try {
Result result = reader.decode(bitmap);
String strResult = result.getText();
Log.e("Habby", "strResult = " + strResult);
if (handler != null) {
Message message = Message.obtain(handler, IScanCodeCallBack.ID_DECODE_SUCCEEDED, strResult);
Bundle bundle = new Bundle();
bundle.putParcelable(IScanCodeCallBack.BARCODE_BITMAP, source.getLastImage());
message.setData(bundle);
message.sendToTarget();
}
} catch (Exception e) {
/*
* 这个异常地方必须处理,因为这个ZXing自带的解码算法,如果当前Preview帧没有识别到(相机中没有
* 可识别的二维码)二维码,则会出现com.google.zxing.NotFoundException异常不断的抛出。
*/
Log.e("Habby", "Exception = " + e.toString());
if (handler != null) {
Message message = Message.obtain(handler, IScanCodeCallBack.ID_DECODE_FAILED);
message.sendToTarget();
}
}
}
2. Encode二维码图片:
参考:http://blog.csdn.net/kazeik/article/details/8280111
用字符串生成二维码,对应的Client类是MultiFormatWriter,使用encode类即可生成相熟矩阵BitMatrix,在根据这个矩阵中的像素点为1时,生成二维码图片。
代码如下:
/**
* 生成二维码图片
* @author GeXianglin
*/
public class QRPictureCreater {
public static Bitmap createQRCode(String str) throws WriterException {
// 生成二维矩阵,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败
BitMatrix matrix = new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, 400, 400);
int width = matrix.getWidth();
int height = matrix.getHeight();
/*
* 像素数组,二维矩阵转为一维像素数组,也就是一直横着排了,可以根据matrix矩阵中的像素位来
* 填充二维码图片像素颜色。
*/
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (matrix.get(x, y)) {
pixels[y * width + x] = 0xff00ff00; // 这个值的bit位代表颜色AlphaRGB,目前是不透明的Green
}
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 通过像素数组生成bitmap,具体参考api
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
}
下次再写一个二维码decode和encode算法分析的笔记。