转载请注明出处
作者:AboutJoke ( http://blog.csdn.net/u013200308 )
原文链接:http://blog.csdn.net/u013200308/article/details/68067564
最近在项目中需要实现对二维码的识别,所以第一时间就想到了zxing,期间各种编译,然后又从网上找了很多的文章终于把需要的功能实现了出来,下面就和大家分享一下。
拍照识别
总体来说,拍照识别还是比较容易的。我们设置好需要修改的参数后,然后调用zxing的CaptureActivity就可以打开拍照识别的页面,识别完成后,结果会在onActivityResult中返回,在这里我们就可以对结果进行处理了。
打开拍照页面
private void callCapture(String characterSet) {
/**
* 获取屏幕的宽度,并将宽度的2/3作为扫码区宽度
*/
int width = wm.getDefaultDisplay().getWidth() * 2 / 3;
Intent intent = new Intent();
intent.setAction(Intents.Scan.ACTION);
intent.putExtra(Intents.Scan.MODE, Intents.Scan.QR_CODE_MODE);
intent.putExtra(Intents.Scan.CHARACTER_SET, characterSet);
/**
* WIDTH==HEIGHT 设置方形扫码取景区
* 取景区的总宽度是屏幕宽度的2/3——适配所有机型
* */
intent.putExtra(Intents.Scan.WIDTH, width);
intent.putExtra(Intents.Scan.HEIGHT, width);//
intent.setClass(this, CaptureActivity.class);//进入zxing模块中的CaptureActivity
startActivityForResult(intent, CAMERA_OK);
}
获取扫描到的结果
String result = data.getStringExtra(Intents.Scan.RESULT);
String recode = recode(result);
从相册选取图片识别
从相册选取图片识别,处理步骤会比拍照识别稍微复杂一点。在这里我们有两种方法可以使用,具体的选择就看你了。具体的逻辑为
- 从相册选取图片
- 根据图片路径生成bitmap并进行解析
- 处理解析结果防止乱码
下面我们就按照上面逻辑来一步步的实现:
从相册选取图片
private void photo() {
// 激活系统图库,选择一张图片
Intent innerIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Intent wrapperIntent = Intent.createChooser(innerIntent, "选择二维码图片");
startActivityForResult(wrapperIntent, REQUEST_CODE);
}
在onActivityResult中获取选中图片的路径
String[] proj = {MediaStore.Images.Media.DATA};
// 获取选中图片的路径
Cursor cursor = this.getContentResolver().query(data.getData(),
proj, null, null, null);
if (cursor.moveToFirst()) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
photo_path = cursor.getString(column_index);
if (photo_path == null) {
photo_path = getPath(this,
data.getData());
}
}
cursor.close();
根据图片路径生成bitmap并进行解析
在这里我们需要拿到选择图片的路径,然后生成一张bitmap。
构建bitmap
if (TextUtils.isEmpty(path)) {
return null;
}
// DecodeHintType 和EncodeHintType
Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); // 设置二维码内容的编码
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 先获取原大小
scanBitmap = BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false; // 获取新的大小
int sampleSize = (int) (options.outHeight / (float) 200);
if (sampleSize <= 0)
sampleSize = 1;
options.inSampleSize = sampleSize;
scanBitmap = BitmapFactory.decodeFile(path, options);
有了bitmap后,我们可以选取两种不同的解析方式来解析图片。
将bitmap转为yuv解析
LuminanceSource source1 = new PlanarYUVLuminanceSource(
rgb2YUV(scanBitmap), scanBitmap.getWidth(),
scanBitmap.getHeight(), 0, 0, scanBitmap.getWidth(),
scanBitmap.getHeight());
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
source1));
MultiFormatReader reader1 = new MultiFormatReader();
Result result1;
try {
result1 = reader1.decode(binaryBitmap);
String content = result1.getText();
Log.e("123content", content);
} catch (NotFoundException e1) {
e1.printStackTrace();
}
将bitmap转为rgb解析
RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap);
BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
try {
return reader.decode(bitmap1, hints);
} catch (NotFoundException | ChecksumException | FormatException e) {
e.printStackTrace();
}
大部分二维码的识别都是基于二值化的方法,在色域的处理上,YUV的二值化效果要优于RGB,并且RGB图像在处理中不支持旋转。因此,一种优化的思路是将所有ARGB编码的图像转换成YUV编码,再使用PlanarYUVLuminanceSource去处理生成的结果,所以个人是比较推荐第一种解析方法的,这里还需要一个rgb2YUV的方法。
public byte[] rgb2YUV(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
int len = width * height;
byte[] yuv = new byte[len * 3 / 2];
int y, u, v;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int rgb = pixels[i * width + j] & 0x00FFFFFF;
int r = rgb & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb >> 16) & 0xFF;
y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
y = y < 16 ? 16 : (y > 255 ? 255 : y);
u = u < 0 ? 0 : (u > 255 ? 255 : u);
v = v < 0 ? 0 : (v > 255 ? 255 : v);
yuv[i * width + j] = (byte) y;
// yuv[len + (i >> 1) * width + (j & ~1) + 0] = (byte) u;
// yuv[len + (i >> 1) * width + (j & ~1) + 1] = (byte) v;
}
}
return yuv;
}
处理解析结果
其实在上一步的时候我们已经得到了扫描的结果,但我们还需要对结果进行一下编码处理,以防止最后显示的结果乱码
private String recode(String str) {
String format = "";
try {
boolean ISO = Charset.forName("ISO-8859-1").newEncoder()
.canEncode(str);
if (ISO) {
format = new String(str.getBytes("ISO-8859-1"), "GB2312");
Log.i("1234 ISO8859-1", format);
} else {
format = str;
Log.i("1234 stringExtra", str);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return format;
}
到这里我们的二维码识别功能就已经实现了,具体的效果呢大家可以下载源码去体验一下,识别速度还是很快的。
自定义内容
如果你对二维码的拍照页面不是很满意的话,可以去ViewfinderView类中修改页面,里面有注释可以很容易的读懂。如果你下载了我的demo体验的话你会发现拍照识别成功是有声音提示的,这个功能的实现在BeepManager中,还可以选择识别成功后是否震动,具体的可以通过阅读代码去修改。
下来有时间我会在集成一个类似微信,检测到环境较为黑暗的时候提示是否打开闪光灯的功能,扫下面的二维码下载demo去体验吧。