分享

关于二维码实验的总结(前言+小试牛刀)

 风雪夜归人_95 2014-09-12
        最近一段时间由于需要,差不多花了一周的时间,研究了一下二维码的问题。大致的时间分配是这样的:1~2天学习二维码的原理知识,1~2天分析学习网上的例程,1~2天根据需要修改例程。
        最开始的时候,对于二维码一无所知,因而到网上去看了很多二维码原理方面的东西。也根据自己看的一些东西准备整理一些自己认为重要的东西(见上篇《二维码原理简介》)。但是,看到最后发现,二维码的原理中涉及到很多数学方面的知识,很多文章在讲解的同时会掺杂很多专业性较强的东西。笔者自认为不是一个数学天才,也不愿意花费过多的时间去阅读数学方面的知识。所以那篇《二维码原理简介》的确称得上是简介了。笔者只是希望能有一个感性的认识,知道诸如“通过识别三个边角上的正方形,即可唯一确定二维码”、“二维码的具体信息对应图片的哪一块区域”、“二维码的容错的能力”等等。

1.初试牛刀
      首先,笔者在网上下载了一个用java语言实现的二维码生成和编写的程序。其结构非常简单,如下图:
 
        其主要的底层功能主要由QRCode.jar包来实现(即具体的那些底层东西)。QR Code码是由日本Denso公司于1994年9月研制的一种矩阵二维码符号,它具有一维条码及其它二维条码所具有的信息容量大、可靠性高、可表示汉字及图象多种文字信息、保密防伪性强等优点。
        在这个demo中,主要有2个类,一个就是EncodeImg,另一个就是TwoDimensionCodeImage。TwoDimensionCodeImage类中定义了一个BufferedImage类的变量,这个变量就是二维码图片的长、宽和颜色(黑色与白色)的信息。在后面解码二维码图片会调用它。
        EncodeImg则实现了所有的逻辑。这个类中定义了6个方法(还有一些方法调用这几个方法,类似于接口,此处略去不提),分别是:2个生成二维码方法、1个生成二维码公共方法(前面2个方法会调用它)、2个解析二维码 和一个main方法。结构也是十分清晰。我们逐个来分析。

生成QRCode图片 1:
 /**
     * 生成二维码(QRCode)图片
     * @param content 存储内容
     * @param imgPath 图片路径
     * @param imgType 图片类型
     * @param size 二维码尺寸
     */
    public void encoderQRCode(String content, String imgPath, String imgType, int size) {
        try {
            BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);
            
            File imgFile = new File(imgPath);
            // 生成二维码QRCode图片
            ImageIO.write(bufImg, imgType, imgFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

生成QRCode图片 2:
 /**
     * 生成二维码(QRCode)图片
     * @param content 存储内容
     * @param output 输出流
     * @param imgType 图片类型
     * @param size 二维码尺寸
     */
    public void encoderQRCode(String content, OutputStream output, String imgType, int size) {
        try {
            BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);
            // 生成二维码QRCode图片
            ImageIO.write(bufImg, imgType, output);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

大家可以仔细比对一下这两个方法,会发现二者仅仅在参数上有略微不同。一种是以图片的形式保存生成的二维码,一种则把结果写入到数据流中。既然涉及数据流,多半都是和网络传输部分有关(这句是个人臆想)。而二者的共同点就是一开始都调用了this.qRCodeCommon(content, imgType, size)这个方法。那么这个方法具体是实现了什么呢?

生成QRCode的公共方法:
     /**
     * 生成二维码(QRCode)图片的公共方法
     * @param content 存储内容
     * @param imgType 图片类型
     * @param size 二维码尺寸
     * @return
     */
    private BufferedImage qRCodeCommon(String content, String imgType, int size) {
        BufferedImage bufImg = null;
        try {
            Qrcode qrcodeHandler = new Qrcode();
            // 设置二维码排错率,可选L(7%)、M(15%)、Q(25%)、H(30%),排错率越高可存储的信息越少,但对二维码清晰度的要求越小
            qrcodeHandler.setQrcodeErrorCorrect('M');
            qrcodeHandler.setQrcodeEncodeMode('B');
            // 设置设置二维码尺寸,取值范围1-40,值越大尺寸越大,可存储的信息越大
            qrcodeHandler.setQrcodeVersion(size);
            // 获得内容的字节数组,设置编码格式
            byte[] contentBytes = content.getBytes("utf-8");
            // 图片尺寸
            int imgSize = 67 + 12 * (size - 1);
            bufImg = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB);
            Graphics2D gs = bufImg.createGraphics();
            // 设置背景颜色
            gs.setBackground(Color.WHITE);
            gs.clearRect(0, 0, imgSize, imgSize);

            // 设定图像颜色> BLACK
            gs.setColor(Color.BLACK);
            // 设置偏移量,不设置可能导致解析出错
            int pixoff = 2;
            // 输出内容> 二维码
            if (contentBytes.length > 0 && contentBytes.length < 800) {
                boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);
                for (int i = 0; i < codeOut.length; i++) {
                    for (int j = 0; j < codeOut.length; j++) {
                        if (codeOut[j][i]) {
                            gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);
                        }
                    }
                }
            } else {
                throw new Exception("QRCode content bytes length = " + contentBytes.length + " not in [0, 800].");
            }
            gs.dispose();
            bufImg.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufImg;
    }

        相信配上注释,代码的可读性大大提高。可见,红色部分标记的语句是完成输出内容到二维码转换的重要步骤。可惜,这个方法qrcodeHandler.calQrcode(contentBytes)被封装在QRCode.jar文件中,无法继续分析。有了这一步,后面的逻辑就很简单了,遍历二维数组,codeOut[j][i]如果是1则将图片相应位置设为黑色。最后得到二维码图片。

对应地,有2种解码方法。

解析QRCode方法1:
 /**
     * 解析二维码(QRCode)
     * @param imgPath 图片路径
     * @return
     */
    public String decoderQRCode(String imgPath) {
        // QRCode 二维码图片的文件
        File imageFile = new File(imgPath);
        BufferedImage bufImg = null;
        String content = null;
        try {
            bufImg = ImageIO.read(imageFile);
            QRCodeDecoder decoder = new QRCodeDecoder();
            content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
            e.printStackTrace();
        } catch (DecodingFailedException dfe) {
            System.out.println("Error: " + dfe.getMessage());
            dfe.printStackTrace();
        }
        return content;
    }

解析QRCode方法2:
 /**
     * 解析二维码(QRCode)
     * @param input 输入流
     * @return
     */
    public String decoderQRCode(InputStream input) {
        BufferedImage bufImg = null;
        String content = null;
        try {
            bufImg = ImageIO.read(input);
            QRCodeDecoder decoder = new QRCodeDecoder();
            content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
            e.printStackTrace();
        } catch (DecodingFailedException dfe) {
            System.out.println("Error: " + dfe.getMessage());
            dfe.printStackTrace();
        }
        return content;
    }
二者的区别,在此不再赘述。二者蓝色部分就是解码的部分。同样被封装到QRcode.jar中。我们无法看到更多的内容。

最后再来看看main方法:
 public static void main(String[] args) {
        String imgPath = "F:/Michael_QRCode3.png";
        String encoderContent = "我名为宇智波斑 ";
        EncodeImg handler = new EncodeImg();
        handler.encoderQRCode(encoderContent, imgPath, "png");
        System.out.println("========encoder success");
        
        
        String decoderContent = handler.decoderQRCode(imgPath);
        System.out.println("解析结果如下:");
        System.out.println(decoderContent);
        System.out.println("========decoder success!!!");
    }

运行该程序,即可在电脑的F盘找到生成的二维码图片Michael_QRCode3.png。在eclipse的控制台窗口也是可以看到解析的结果。此时,你用手机上的二维码扫描软件扫描一下生成的二维码,同样可以得到正确的结果。

总结:可以看到,这个java程序的接口还是相对较简单的,使用起来也很方便。笔者本来是要写一个Android的程序的。于是想把这个Java程序移植到一个Android工程上。但事情远比想象中的复杂。当把这些文件放到一个Android程序中发现,程序中出现了很多错误。这些错误主要集中在那些绘图相关的包上。网上有人说Android和java在绘图上的实现好像是不兼容的,需要自己做相应的移植。笔者仍不想放弃,看到"JRE System Libruary"下面有很多jar包,将它们全部导入Android程序中。则程序可以成功编译。但是程序在运行时,点击生成二维码时出现闪退的现象。根据打印的Log发现仍然是因为Android中不包含相应绘图APi。至此,笔者决意放弃这个java程序。




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约