在玩二维码扫描解码时碰到的问题

今天老大出差,没人给任务,闲来无事就玩玩与公司相关的一些小Demo。



二维码生成简单,固定不变的代码,玩来玩去还是这些代码

代码如下:

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String s=editText.getText().toString();//s的长度约长,生成的二维码越密集,空白越少,  4个基点不能遮挡
                if(!TextUtils.isEmpty(s)){
                    QRCodeWriter writer=new QRCodeWriter();
                    try {
                        Map<EncodeHintType,Object> map=new HashMap<EncodeHintType, Object>();
                        map.put(EncodeHintType.CHARACTER_SET,"UTF_8");//字符集
                        map.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);//H最高容错等级,但输入的字符不能太短
                        BitMatrix encode= writer.encode(s, BarcodeFormat.QR_CODE,500,500,map);
                        Bitmap bitmap=Bitmap.createBitmap(500,500, Bitmap.Config.RGB_565);
                        for (int i = 0; i < encode.getWidth(); i++) {
                            for (int i1 = 0; i1 < encode.getHeight(); i1++) {
                                if(encode.get(i,i1)){//每个像素点
                                    bitmap.setPixel(i,i1, Color.BLACK);
                                }else{
                                    bitmap.setPixel(i,i1,Color.WHITE);
                                }
                            }
                        }
                        Bitmap icon= BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);//二维码的LOGO
                        new Canvas(bitmap).drawBitmap(icon,new Rect(0,0,icon.getWidth(),icon.getHeight()), new RectF(200,200,300,300),new Paint());
                        imageView.setImageBitmap(bitmap);
                    } catch (WriterException e) {
                        e.printStackTrace();
                    }
                }
                        }
        });
    }




至于二维码的扫描解码,开始我是百度了很多Demo,但都是对Zxing的简化,但我还是想弄懂原理,可百度的结果总是不尽人意。先上一张图,代表二维码的解码流程




一、初始化相机

相机使用的是android.hardware.Camera这个类,在Android 5.0之后,推荐使用更强大的android.hardware.Camera2这个类,为了兼容更低版本Android系统,我们在这里仍然使用Camera类来实现。

Camera可以通过setDisplayOrientation()方法设置预览图像的方向,旋转度数只能是0、90、180、270中的一个,根据需求,本例中设置为90度。

Camera还可以通过Camera.Parameters类设置预览图像的分辨率,但是只能在气可支持的分辨率中选择一个,不能随便设置,我们需要根据屏幕大小,在其中选出一个最佳的预览图像分辨率,太大浪费资源,太小会显示不清楚,具体选择方法,在代码中有,这里就不细述了。

Camera可以通过setPreviewFormat()方法来设置预览图像的数据格式,推荐选择的有ImageFormat.NV21和ImageFormat.YV12,默认是NV21。NV21属于YUV图像,和RGB图像有所不同。

注意在使用Camera时,需要在AndroidManifest.xml里声明一些权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.camera.flash" />

二、绑定SurfaceView

在SurfaceView创建好后,通过Camera类的setPreviewDisplay()方法,将SurfaceHolder传入Camera。

调用Camera的startPreview()方法,Camera捕捉到的图像就会显示到SurfaceView上了。

三、获取一帧图像

调用Camera的setOneShotPreviewCallback(PreviewCallback cb)方法,可以请求获取一帧图像,获取到图像后,会调用PreviewCallback的onPreviewFrame(byte[] data, Camera camera)方法,其中的data参数就是图像的YUV数据了。

四、图像预处理

根据二维码的原理,我们只需要图像的亮度信息来进行二维码解析,所以我们要把获取到的彩色图像转换为灰度图像。YUV图像转换为灰度图像的方法,以及RGB图像转换为灰度图像的方法这里有个坑,待会详细介绍。

五、二维码解析

在这里,二维码解析使用的是google的zxing开源框架,把上一步处理后的灰度图像,封装为zxing的LuminanceSource,再封装为zxing的BinaryBitmap,然后就可以进行二维码解析了。

值得一提的是,利用zxing解析二维码是耗时操作,为避免ANR,需要写到子线程中来处理,我这里只是玩,就不写到子线程了。

六、解析结果

解析成功后可以调用result.getText();来获得解码后的字符串,怎么进行处理就是你们自己的事了。


开始就是初始化Camera和在SurfaceView上显示,做过Camera开发的都知道流程,我就不写了。

camera.setOneShotPreviewCallback()之后进入onPreviewFrame(byte[] bytes,Camera camera){}

关键的地方来了,我的解码操作也是在里面进行。(切记玩玩可以放在主线程,正规的时候还是利用Handle来进行解码的操作,否则小心出现ANR)

上代码


public void onPreviewFrame(byte[] bytes, Camera camera) {

/*开始我的想法也是利用BitmapFactory来直接利用bytes生成一个bitmap,但总是得到个空的Bitmap

*后来查了下,原来是因为格式的问题,这里我们要对bytes做下处理

*/


        Camera.Size previewSize = camera.getParameters().getPreviewSize();
        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        YuvImage yuvImage=new YuvImage(bytes, ImageFormat.NV21,previewSize.width,previewSize.height,null);
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),80,baos);
        byte[] data = baos.toByteArray();
        Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length);


                int[] pixels=new int[bitmap.getWidth()*bitmap.getHeight()];
               bitmap.getPixels(pixels,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
                RGBLuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(),bitmap.getHeight(),pixels);
                BinaryBitmap binaryBitmap=new BinaryBitmap(new HybridBinarizer(source));
                QRCodeReader reader=new QRCodeReader();
                try {
                    Result result = reader.decode(binaryBitmap);
                    Toast.makeText(this,result.getText(),Toast.LENGTH_SHORT).show();
                    decodeSucess=true;
                    Intent intent=new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse(result.getText()));
                    startActivity(intent);
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (ChecksumException e) {
                    e.printStackTrace();
                } catch (FormatException e) {
                    e.printStackTrace();
                }
        if(!decodeSucess){
            camera.autoFocus(this);
        }

还有个问题,我刚刚做的时候是手动触摸聚焦,然后调用setOneShotPreviewCallback的方法去获取一帧图像进行解码,后来我想让它自动的去聚焦,网上查来的方法试来试去发现还是只聚焦了一次。最后自己想了个蠢办法,设置一个boolean类型的Flag,每次解码后判断是否解码成功(并不是每次解码都能成功的),没有成功我就再次去手动的调用聚焦,间接的实现了自动聚焦的效果。


菜鸟一枚,不喜勿喷,也欢迎你的宝贵意见


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值