Zbar调用摄像头扫码,没什么好说的,其他前辈写过无数文章,大概就是写布局--->调用摄像头(android 6.0以后需要判断权限)---->用Zbar的Decoder将摄像头拿到的数据进行解码。
最近项目需要扫码功能支持相机扫码,也支持本地图片扫码,按照惯例,到网上各路搜索相关例子,看着代码都不多,但是就是移植到自己项目里就不行,本地图片扫码总是得到null,觉得很是奇怪,找了很多文章,有几篇大概提到Bitmap转YUV格式之后再去解码,于是参照说明,尝试发现部分可以解码,部分不能解码,很是不解,用微信的扫码测试却都可以得到正确的结果。于是又尝试各种方法,仍然不行,过程中发现我的程序界面得到结果(null)所用的时间较长,心想是不是图片太大,处理耗时太多,于是尝试压缩下,哇哈哈~~~突然间柳暗花明又一村,之前不能解码的图片现在也能解码了,啊哈哈哈,好开森~~
于是继续压缩,结果发现玩大了额,之前能解码的现在又解码了了~~~压缩过头了,最终用了个自我感觉效果不错的压缩后大小。
相关代码如下:
第一部分:发起查找图片的Intent
/** 这个地方需要注意,同样的Intent,Android 6.0之前返回的数据与6.0及之后返回的数据不一样,具体见onActivityResult部分 */
Intent innerIntent = new Intent();
innerIntent.setAction(Intent.ACTION_GET_CONTENT);
innerIntent.setType("image/*");
Intent wrapperIntent = Intent.createChooser(innerIntent, "选择二维码图片");
startActivityForResult(wrapperIntent, 0x0202);
第二部分:onActivityResult数据处理
private String photo_path;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0x0202) {
if (resultCode == Activity.RESULT_OK) {
DebugLog.d(data.getData().toString());
Uri intentData = data.getData();
/** 之前说的数据不同之处即在这里,6.0之前,得到的Uri字符以“content”开头,需要使用cursor获取路径,而6.0及之后,直接返回了以file开头的文件路径,可以直接使用 */
if (intentData.toString().startsWith("content")) {
String[] proj = { MediaStore.Images.Media.DATA };
// 获取选中图片的路径
Cursor cursor = 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 = FileUtils.getPath(getApplicationContext(), data.getData());
}
}
cursor.close();
} else if (intentData.toString().startsWith("file")) {
photo_path = intentData.toString().substring("file://".length());
}
DebugLog.d("photo_path:" + photo_path);
new Thread(new Runnable() {
@Override
public void run() {
// 创建Zbar解码器实例
ZBarDecoder decoder = new ZBarDecoder();
scanHandler.sendEmptyMessage(MSG_WHAT_START);
// 得到Bitmap实例
Bitmap bitmap = BitmapFileUtils.getDiskBitmap(photo_path);
if (bitmap == null) {
scanHandler.sendEmptyMessage(MSG_WHAT_NO_PERMISSION);
return;
}
DebugLog.e("oldWidth:" + bitmap.getWidth() + "/oldHeight:" + bitmap.getHeight());
// 压缩图片至宽、高最大400像素
bitmap = BitmapFileUtils.reduce(bitmap, 400, 400, true);
int wi = bitmap.getWidth();
int he = bitmap.getHeight();
DebugLog.e("width:" + wi + "/height:" + he);
// 此处进行解码,先将bitmap转为YUV格式
String qrcode = decoder.decodeRaw(Bmp2YUV.getYUV420sp(wi, he, bitmap), wi, he);
DebugLog.e("--===scan result is : " + qrcode);
if (qrcode == null || qrcode.isEmpty()) {
scanHandler.sendEmptyMessage(MSG_WHAT_FAILED);
} else {
Message msg = Message.obtain(scanHandler);
msg.what = MSG_WHAT_SUCCESS;
msg.obj = qrcode;
scanHandler.sendMessage(msg);
}
bitmap.recycle();
bitmap = null;
}
}).start();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
第三部分:Bitmap转YUV格式
public class Bmp2YUV {
/**
* YUV420sp
*
* @param inputWidth
* @param inputHeight
* @param scaled
* @return
*/
public static byte[] getYUV420sp(int inputWidth, int inputHeight, Bitmap scaled) {
int[] argb = new int[inputWidth * inputHeight];
scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
// 这里我踩平了一个坑,数组长度要按如下格式写,其他同理
//方法亦可。网络上其他前辈写的=new byte[inputWidth
// * inputHeight*3/2]在某些情况下会出现数组越界异常
byte[] yuv = new byte[inputWidth
* inputHeight
+ ((inputWidth % 2 == 0 ? inputWidth : (inputWidth + 1)) * (inputHeight % 2 == 0 ? inputHeight
: (inputHeight + 1))) / 2];
encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
scaled.recycle();
return yuv;
}
/**
* RGB转YUV420sp
*
* @param yuv420sp
* inputWidth * inputHeight * 3 / 2
* @param argb
* inputWidth * inputHeight
* @param width
* @param height
*/
private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
// 帧图片的像素大小
final int frameSize = width * height;
// ---YUV数据---
int Y, U, V;
// Y的index从0开始
int yIndex = 0;
// UV的index从frameSize开始
int uvIndex = frameSize;
// ---颜色数据---
int a, R, G, B;
//
int argbIndex = 0;
//
// ---循环所有像素点,RGB转YUV---
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
// a is not used obviously
a = (argb[argbIndex] & 0xff000000) >> 24;
R = (argb[argbIndex] & 0xff0000) >> 16;
G = (argb[argbIndex] & 0xff00) >> 8;
B = (argb[argbIndex] & 0xff);
//
argbIndex++;
// well known RGB to YUV algorithm
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 = Math.max(0, Math.min(Y, 255));
U = Math.max(0, Math.min(U, 255));
V = Math.max(0, Math.min(V, 255));
// NV21 has a plane of Y and interleaved planes of VU each
// sampled by a factor of 2
// meaning for every 4 Y pixels there are 1 V and 1 U. Note the
// sampling is every other
// pixel AND every other scanline.
// ---Y---
yuv420sp[yIndex++] = (byte) Y;
// ---UV---
if ((j % 2 == 0) && (i % 2 == 0)) {
//
yuv420sp[uvIndex++] = (byte) V;
//
yuv420sp[uvIndex++] = (byte) U;
}
}
}
}
}
Demo源码地址:http://download.csdn.net/detail/chicet/9729077