使用Zxing实现拍照,选取图片识别二维码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013200308/article/details/68067564

转载请注明出处
作者: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去体验吧。

这里写图片描述

猜你喜欢

转载自blog.csdn.net/u013200308/article/details/68067564