Zbar扫码(摄像头+本地图片+6.0权限)

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

你可能感兴趣的:(Android,Java)