基于QRcode创建和识别二维码的研究

摘要:
至于什么是二维码,大家都用过,其实更生动。与之前的条形码相比,很容易理解它是基于小竖条的水平排列和不同宽度来表达不同的信息。二维码基于二维小黑白方块,按照一定的规则排列在矩形区域中,以形成用于传输信息的编码方法。

至于什么是二维码,大家都使用过,其实比较形象,对比之前的条形码,就很容易理解,就是基于水平方向排列的通过小竖条的宽度不同表示不同的信息,而二维码,表达信息的方式是基于二维的黑白相间(不一定就是黑白,多数看到的可能是黑白,其实颜色是可以随着自己的需要,灵活调整的)的小方块,按照一定的规则排列的一个矩形区域内,形成一个传递信息的编码方式。

二维码(本博客重点介绍的是目前主流的矩形二维码,堆叠的或者其他的模式,不做介绍),有其固有的特点,由定位矩形框,信息表达区域等功能识别器,特征明显,如下图:

基于QRcode创建和识别二维码的研究第1张

一个可视的二维码,乍一看,就是上面这个样子,只是因为里面有很多信息码元(黑白相间的矩形块),看起来比这个更丰富。

就算这种矩阵式的二维码,也有几个不同的分支,请看下面的概要图片:

基于QRcode创建和识别二维码的研究第2张

关于二维码的理论,这里先不做过多的介绍,后面会专题介绍理论,今天主要从代码实现的角度,介绍如何生成二维码,以及一些基本的参数调整,对二维码的影响。

下面直接上代码:

package qcode;

import com.swetake.util.Qrcode;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 *
 * 基于QRcode包实现的矩阵式二维码的创建过程研究和学习
 *
 * Created by shihuc on 2020/12/13.
 */
public class QRCodeCreate {
    public static void main(String[] args) throws IOException {

        int v = 3;
        for (v = 1; v <= 40; v++) {
            try {
                qrCreator(v, "Quick Response", 15, 3, 3);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



    }

    public static void qrCreator(int ver, String info, int margin, int w, int h) throws IOException {
        /**
         * 计算二维码图片的高宽比
         * API文档规定计算图片宽高的方式 ,v是本次测试的版本号,v为0时,表示生成的二维码尺寸随存储内容的变化而变化
         */
        int v = ver;
        int width = 67 + 12 * (v - 1);
        int height = 67 + 12 * (v - 1);


        Qrcode x = new Qrcode();
        /**
         * 纠错等级分为
         * level L : 最大 7% 的错误能够被纠正;
         * level M : 最大 15% 的错误能够被纠正;
         * level Q : 最大 25% 的错误能够被纠正;
         * level H : 最大 30% 的错误能够被纠正;
         * 设置二维码排错率,可选L(7%)、M(15%)、Q(25%)、H(30%),排错率越高可存储的信息越少,但对二维码清晰度的要求越小
         */
        x.setQrcodeErrorCorrect('L');
        //注意版本信息 N代表数字 、A代表 a-z,A-Z、B代表 其他)
        x.setQrcodeEncodeMode('B');
        //版本号1-40, 设置设置二维码尺寸,取值范围1-40,值越大尺寸越大,可存储的信息越大
        x.setQrcodeVersion(v);

        //设置二维码的边框留白信息,就是在原有的二维码外边框留下白色的边距,让二维码显得有结构感
        int qr_margin = margin;


        //String qrData = "https://www.cnblogs.com/shihuc";//内容信息
        String qrData = info + " " + v;

        // 获得内容的字节数组,设置编码格式,汉字转格式需要抛出异常
        byte[] d = qrData.getBytes("utf-8");

        //缓冲区
        BufferedImage bufferedImage = new BufferedImage(width+2*qr_margin, height+2*qr_margin, BufferedImage.TYPE_INT_BGR);

        //绘图, 用来画出二维码信息
        Graphics2D gs = bufferedImage.createGraphics();

        //设置二维码背景颜色和前景颜色,这里,可以设计出自己想要的颜色的二维码了,很方便
        gs.setBackground(Color.WHITE);
        gs.setColor(Color.RED);
        gs.clearRect(0, 0, width+2*qr_margin, height+2*qr_margin);

        /**
         * 偏移量 3, 可以将二维码较好的控制在最后输出的二维码图片的中央位置,太大,会导致向右下角偏移,
         * 太小,会导致向左上角偏移, 偏移太多,二维码识别时,无法找到三个定位的正方形,从而无法识别二维码
         */
        int pixOff = 3 + qr_margin;


        /**
         * 容易踩坑的地方
         * 1.注意for循环里面的i,j的顺序,
         *   s[j][i]二维数组的j,i的顺序要与这个方法中的 gs.fillRect(j*3+pixOff,i*3+pixOff, 3, 3);
         *   顺序匹配,否则会出现解析图片是一串数字
         *
         *  另外,还有需要注意的地方,fillRect里面后面的两个参数 width,height,指的是二维码里面填充的小方块,通常是指那个黑色
         *  的方块,太大了,导致整个二维码图片里面黑色太多,太小,里面的白色区域偏多,通常是选择长宽都为3居多, 不管是偏大还是偏小,
         *  都会导致最后的二维码识别有困难,甚至识别不出来
         */
        /**
         * 仔细观察,下面s数组的维度,就是码元的维度,这里将体现出版本v和码元大小的关系, 21 + 4*(v - 1)
         * v=1: 21*21
         * v=2: 25*25
         * v=3: 29*29
         * v=4: 33*33
         * .......
         */
        boolean[][] s = x.calQrcode(d); //计算需要打印前景色的位置,二维码核心算法函数
        for (int i = 0; i < s.length; i++) {
            for (int j = 0; j < s.length; j++) {
                if (s[j][i]) {
                    gs.fillRect(j * 3 + pixOff, i * 3 + pixOff, w, h);
                }
            }
        }
        gs.dispose();

        bufferedImage.flush();
        //设置图片格式,与输出的路径
        ImageIO.write(bufferedImage, "png", new File("D:\ProTempHome\margin\qrcode" + v + ".png"));
        System.out.println( "v: " + v + ",二维码生成完毕");
    }
}

这里要说明的是,生成二维码所需的包,只有一个QRcode.jar,可以到我的网盘下载(https://pan.baidu.com/s/1X8dNd47-5cYPQ7hgMUZB3g,提取密码:8v5q),就是一个jar文件,将其copy到项目的lib路径下,并添加到classpath路径下。

若想在maven项目里面使用这个包,其实可以自己基于maven的安装指令,将这个jar变成maven格式的dependency配置信息。

mvn install:install-file -Dfile=D:ProgramQRCodeQRCode.jar -DgroupId=QRCode -DartifactId=QRCode -Dversion=3.0 -Dpackaging=jar 

安装完成后,在maven的pom文件中,添加下面的内容:

<dependency>
       <groupId>QRCode</groupId>
       <artifactId>QRCode</artifactId>
       <version>3.0</version>
</dependency>

这里,代码里面的注释信息已经很丰富,下面,需要对几个重点内容强调一下:

1. 上述案例的代码,支持设置边框留白的大小,其实,二维码生成逻辑还是蛮容易理解的,基于一套算法(纠错级别L、M、Q、H,及Model,再就是版本v都能影响这个算法的输出),得出一个布尔值类型的二维数组,然后,控制绘图程序,在矩形区域将码元信息绘制出来,最后得到我们熟悉的二维码。

1.1 这里纠错级别,和版本号,模式等参数,影响生成的二维码的大小,能够容纳的信息,抗干扰的能力。下面,给出了版本v只有前3个的变化表,可以粗略对比,就能有所了解。

基于QRcode创建和识别二维码的研究第3张

1.2 在model,纠错参数配置不变的情况下,版本v的取值(1-40)越大,得到的二维码的尺寸也越大,最终识别这个二维码的难度也越大(识别的时间会加长),但是能够存储的信息也越多,下面给几个基于上面的代码生成的二维码的图片,大家可以对比观察下。

v=1

基于QRcode创建和识别二维码的研究第4张

v=6

基于QRcode创建和识别二维码的研究第5张

v=11

基于QRcode创建和识别二维码的研究第6张

v=16

基于QRcode创建和识别二维码的研究第7张

v=21

基于QRcode创建和识别二维码的研究第8张

v=26

基于QRcode创建和识别二维码的研究第9张

v=40

基于QRcode创建和识别二维码的研究第10张

大家若有兴趣,可以用微信的扫一扫功能,可以体会一下,这个识别的过程是不是会越来越有困难,时间会变得比较长。

2. 基于上诉代码,不仅可以实现各种尺寸,各种纠错码模式,各种编码模式的二维码的生成(纠错模式越高,【L最低,Q最高】,存储信息越少,抗干扰,或者说越容易识别,反之亦然)。另外,还有一个很有意思的点,需要分享,那就是码元块的大小是可以调整的,就是上述代码中的这个函数的后面两个参数w,h。

gs.fillRect(j * 3 + pixOff, i * 3 + pixOff, w, h);

当这个参数变大时,生成的二维码显得更厚重,这两个参数的值越小时,生成的二维码给人的感觉很稀疏,如下图:

基于QRcode创建和识别二维码的研究第11张               基于QRcode创建和识别二维码的研究第12张            基于QRcode创建和识别二维码的研究第13张            基于QRcode创建和识别二维码的研究第14张              

2.1 上面的图片是基于上述的代码(w,h有变化)生成的,v取值5。 从左向右,w=h=5, 3,2,1. 大家可以用微信扫一扫试试,最右边的这个w=1的二维码,我的手机微信识别不出来。基于这里的调整试验,得出一个信息,二维码的定位矩形框的大小(v不变的情况下),受到代码中pixOff的影响【准确的说是受到代码中pixOff = 3 + qr_margin中的3的影响,这个3变大,二维码中的定位矩形框越大,反之亦然;然而,仅仅调整这个数字是不科学的,会导致二维码不能完整的打印在公式中BufferImage定义的图片中】,基于上述代码,3已经是一个调整的算是不错的大小因子。

2.2 接下来说明w,h,他们的取值,在二维码定位矩形框大小不变时,w,h值越大,二维码看起来很厚重,w,h值越小,二维码越稀薄,上述的图片,当定位矩形框不是线条时(此时的码元大小也是缺失的),会导致二维码识别不出来。

3. 程序识别二维码

package qcode;

import jp.sourceforge.qrcode.data.QRCodeImage;

import java.awt.image.BufferedImage;

/**
 * 实现QRCodeImage接口,
 * 设置解码的图片信息
 * 核心就是告诉codeDecoder.decode()将要解析的图片类型
 *
 * Created by shihuc on 2020/12/13.
 */
public class MyQRCodeImage implements QRCodeImage{


    BufferedImage bufferedImage;

    public MyQRCodeImage(BufferedImage bufferedImage){
        this.bufferedImage=bufferedImage;
    }

    //
    @Override
    public int getWidth() {
        return bufferedImage.getWidth();
    }

    //
    @Override
    public int getHeight() {
        return bufferedImage.getHeight();
    }

    //获取给定坐标位置的RGB点位的像素值
    @Override
    public int getPixel(int i, int j) {
        return bufferedImage.getRGB(i,j);
    }
}
package qcode;

import jp.sourceforge.qrcode.QRCodeDecoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * 程序进行识别二维码的信息
 * 有点类似模拟手机微信扫一扫识别二维码的逻辑,只是这里图片已经存在,不像APP里面扫描二维码是拍摄照片,然后再识别。
 *
 * Created by shihuc on 2020/12/13.
 */
public class QRCodeRead {
    public static void main(String[] args) throws IOException {

        int v = 1;
        for ( v = 1; v <= 40; v++) {
            qrReader(v);
        }
    }

    public static void qrReader(int v) throws IOException {

        //图片路径
        File file = new File("D:\ProTempHome\margin\qrcode" + v + ".png");

        //读取图片到缓冲区
        BufferedImage bufferedImage = ImageIO.read(file);

        //QRCode解码器
        QRCodeDecoder codeDecoder = new QRCodeDecoder();

        //通过解析二维码获得信息
        String result = new String(codeDecoder.decode(new MyQRCodeImage(bufferedImage)), "utf-8");
        System.out.println("识别二维码 v=" + v + "的内容: " + result);
    }
}

直接上了代码,这里需要说明的是,基于前面的二维码生成算法,得到的二维码图片,经过上述的二维码解码程序,都能很好的识别出来。 但是,针对上面的调整w,h(其他参数都不变的情况下),会导致有些二维码识别不出来,直接报错了(v变高的时候,w,h值对识别的影响越发明显,具体的变化根源,有待后续进一步研究,有人若知道,可以给我留言,告知根源是什么)。 

微信的扫一扫对二维码识别的能力还是比较强的,如前面,我将二维码变得厚重后,微信基本还是能识别出二维码的内容(不能厚重的太过分。。。)

好了,今天,博文就分享到这里,还是有很多内容需要进一步去研究,希望看官,可以分享一下你的二维码研究心得。

下一篇博客,打算研究一下给二维码中间插入一个漂亮的logo的解决方案,并且研究一下矩阵式二维码的原理和理论,欢迎探讨!

免责声明:文章转载自《基于QRcode创建和识别二维码的研究》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Nginx + Tomcat7 + redis session一致性问题Android 知识图谱下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

web打印,web打印控件的三种实现方法

做管理系统的时候,打印一直是个棘手的问题,做B/S的系统这个问题就更加突出了!下面举出三种常用的web打印处理方式1、利用word或者excel来实现web打印(如果不修改ie设置,可以在web服务器端生成xls文件,然后通过xlBook = xls.Workbooks.Open(remotePath) 获取对象打印)   实现过程:先将需要打印的数据导入...

weex 引导页(guide)页面

slider 和 indicator 都是 weex 的内置组件,且 indicator 是 slider 的子组件。 1.报错处理 原因解析:indicator 样式页面渲染慢 解决方案:indicator 的样式写为 内联样式 2.Guide.vue <!-- 引导页 --> <template> <div cl...

用C#实现的条形码和二维码编码解码器

本篇介绍可以在C#中使用的1D/2D编码解码器。条形码的应用已经非常普遍,几乎所有超市里面的商品上面都印有条形码;二维码也开始应用到很多场合,如火车票有二维码识别、网易的首页有二维码图标,用户只需要用手机扫描一下就可以看到手机版网易的网址,免去了输入长串字符的麻烦。 条形码的标准: 条形码的标准有ENA条形码、UPC条形码、二五条形码、交叉二五条形码、库德...

同一个二维码支持多种支付的实现思路

我们在一些商店使用手机支付时会遇到使用支付宝或者微信都只扫一个二维码就能完成付款。 方式一 判断应用类型做相应跳转微信和支付宝融合到一张二维码上是可以实现的,这个二维码就是一个地址链接url实际上是一个支付网页(我们自己网站的页面),支付宝和微信的扫一扫实际上就是在应用内部打开的这个网页。 网页在被打开的时候会判断打开这个网页的应用类型,如果是微...

web的几种轮播

我们在开发当中经常用到轮播。我在这里总结了一下几种,仅供参考: 第一种:   1、jQuery:用display :none/block控制的一种轮播; // CSS部分 #igs { margin: 30px auto; 1200px; height: 460px; position: relative; }...

一个简单的WPF MVVM实例【转载】

引用地址:http://blog.csdn.net/yl2isoft/article/details/20838149 1新建WPF应用程序WPFMVVMExample 程序结构如下图所示。 2Model实现 在Model文件夹下新建业务类StudentModel(类文件StudentModel.cs),类的详细代码如下所示。 [csharp]vie...