2021SC@SDUSC Zxing开源代码(四)QR码的编码(一)

本文详细探讨了QR码的编码流程,包括QR码的基本结构、特点,以及在Zxing库中的QRCodeWriter类如何应用纠错级和编码规则。通过实例展示了编码步骤和关键代码分析,是理解二维码生成技术的实用指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2021SC@SDUSC


前言:在上一篇博客中,我介绍了Zxing生成二维码/条形码的一般步骤和所共用的类及其方法。在本篇中,我将着重对QR码的编码算法进行分析。本篇博客前半部分,主要介绍了QR码的相关知识,以及QR码的编码步骤;后半部分对QR码的编码类QRCodeWriter进行分析。


一、QR码简介

1.1 简介

QR码属于矩阵式二维码中的一个种类,由DENSO(日本电装)公司开发,由JIS和ISO将其标准化。
其基本结构如下:
在这里插入图片描述
位置探测图形、位置探测图形分隔符、定位图形:用于对二维码的定位,对每个QR码来说,位置都是固定存在的,只是大小规格会有所差异;
校正图形:规格确定,校正图形的数量和位置也就确定了;
格式信息:表示改二维码的纠错级别,分为L、M、Q、H;
版本信息:即二维码的规格,QR码符号共有40种规格的矩阵(一般为黑白色),从21x21(版本1),到177x177(版本40),每一版本符号比前一版本 每边增加4个模块。
数据和纠错码字:实际保存的二维码信息,和纠错码字(用于修正二维码损坏带来的错误)。

1.2 QR码的特点

①高速读取:QR就是取自“Quick Response”的首字母,通过摄像头从拍摄到解码到显示内容也就三秒左右,对摄像的角度也没有什么要求;

②高容量、高密度:理论上内容经过压缩处理后可以存7089个数字,4296 个字母和数字混合字符,2953个8位字节数据,1817个汉字;

③支持纠错处理:即使编码变脏或破损,也可自动恢复数据。这一“纠错能力”具备4个级别,用户可根据使用环境选择相应的级别。调高级别,纠错能力也相应提高,但由于数据量会随之增加,编码尺寸也也会变大。内容重复编码以便在部分被遮挡时仍能被扫码识别,因此可以支持中央放置遮挡图标而不影响二维码识别。
level L : 最大 7% 的错误能够被纠正;
level M : 最大 15% 的错误能够被纠正;
level Q : 最大 25% 的错误能够被纠正;
level H : 最大 30% 的错误能够被纠正;

④结构化:看似无规则的图形,其实对区域有严格的定义,下图就是一个模式2、版本1的QR图结构。
在这里插入图片描述
在上图21*21的矩阵中,黑白的区域在QR码规范中被指定为固定的位置,称为寻像图形(finder pattern) 和 定位图形(timing pattern)。寻像图形和定位图形用来帮助解码程序确定图形中具体符号的坐标。
黄色的区域用来保存被编码的数据内容以及纠错信息码。
蓝色的区域,用来标识纠错的级别(也就是Level L到Level H)和所谓的"Mask pattern",这个区域被称为“格式化信息”(format information)。

⑤扩展能力:QR码的Structure Append特点,使一个QR码可以分解成多个QR码,反之,也可以将多个QR码的数据组合到一个QR码中来。如图:
在这里插入图片描述

1.3 QR码的模式和版本

QR码分为Model1和Model2两种模式,Model1是对QR的初始定义,Model2是对Model1的扩展,目前使用较为普遍的是Model2。
QR图的大小(size)被定义为版本(Version),版本号从1到40。版本1就是一个21* 21的矩阵,每增加一个版本号,矩阵的大小就增加4个模块(Module),因此,版本40就是一个177*177的矩阵。(版本越高,意味着存储的内容越多,纠错能力也越强)。

二、QR码的编码过程

  • 数据分析:确定编码的字符类型,按相应的字符集转换成符号字符;
    选择纠错等级,在规格一定的条件下,纠错等级越高,真实数据的容量越小。

  • 数据编码:将数据字符转换为位流,每8位一个码字,整体构成一个数据的码字序列。知道这个数据码字序列就知道了二维码的数据内容。

容量如下:

格式容量
数字最多7089字符
字母最多4296字符
二进制数(8 bit)最多2953字节
日文汉字/片假名最多1817字符(采用Shift JIS)
中文汉字最多984字符(采用UTF-8)
中文汉字最多1800字符(采用BIG5)

模式编码如下:

模式指示符
ECI0111
数字0001
字母数字0010
8位字节0100
日本汉字1000
中文汉字1101
结构链接0011
FNCI0101(第一位置)、1001(第二位置)
终止符(信息结尾)0000

数据可以按照一种模式进行编码,以便进行更高效的解码。
例如:对数据:01234567编码(版本1-H),
①分组:012 345 67
②转成二进制:012→0000001100,,345→0101011001,67 →1000011
③转成序列:0000001100 0101011001 1000011
④字符数 转成二进制:8→0000001000
⑤加入模式指示符(上图数字)0001:0001 0000001000 0000001100 0101011001 1000011
对于字母、中文、日文等只是分组的方式、模式等内容有所区别。基本方法是一致的。

  • 纠错编码:按需要将上面的码字序列分块,并根据纠错等级和分块的码字,产生纠错码字,并把纠错码字加入到数据码字序列后面,成为一个新的序列。
    在二维码规格和纠错等级确定的情况下,其实它所能容纳的码字总数和纠错码字数也就确定了,比如:版本10,纠错等级时H时,总共能容纳346个码字,其中224个纠错码字。
    就是说二维码区域中大约1/3的码字时冗余的。对于这224个纠错码字,它能够纠正112个替代错误(如黑白颠倒)或者224个据读错误(无法读到或者无法译码),这样纠错容量为:112/346=32.4%。

  • 构造最终数据信息:在规格确定的条件下,将上面产生的序列按次序放如分块中。
    按规定把数据分块,然后对每一块进行计算,得出相应的纠错码字区块,把纠错码字区块 按顺序构成一个序列,添加到原先的数据码字序列后面。
    如:D1, D12, D23, D35, D2, D13, D24, D36, … D11, D22, D33, D45, D34, D46, E1, E23,E45, E67, E2, E24, E46, E68,…

  • 构造矩阵:将探测图形、分隔符、定位图形、校正图形和码字模块放入矩阵中。
    在这里插入图片描述
    把上面的完整序列填充到相应规格的二维码矩阵的区域中。

  • 掩摸:将掩摸图形用于符号的编码区域,使得二维码图形中的深色和浅色(黑色和白色)区域能够比率最优的分布。

  • 格式和版本信息:生成格式和版本信息放入相应区域内。
    版本7-40都包含了版本信息,没有版本信息的全为0。二维码上两个位置包含了版本信息,它们是冗余的。
    版本信息共18位,6X3的矩阵,其中6位时数据为,如版本号8,数据位的信息时 001000,后面的12位是纠错位。

三、代码分析

了解了上述QR码的编码过程,对于Zxing源代码的分析有很大的帮助。

QRCodeWriter

QRCodeWriter类是Zxing生成QR码的入口,它继承自父类Writer,其父类在上一篇博客中有提到。QRCodeWriter对象的作用是将二维码渲染为灰度值的二维位矩阵数组。

它默认安静区(Quiet Zone)的值为4(QUIET_ZONE_SIZE=4;),安静区是每个条形码开始和结束时的空白边缘空间,使扫描仪能够准确地读取信息。在大多数情况下,安静区不需要很大。

编码方法如下:此方法继承自Writer父类,具体代码分析都已注释在相应位置

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {
	//要编码的内容为空
    if (contents.isEmpty()) {
      throw new IllegalArgumentException("Found empty contents");
    }
	//要生成的不是QR码
    if (format != BarcodeFormat.QR_CODE) {
      throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
    }
	//要生成的QR码长或宽小于0
    if (width < 0 || height < 0) {
      throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
          height);
    }
	//纠错等级
    ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
    //安静区大小
    int quietZone = QUIET_ZONE_SIZE;
    //获取QR码的其他参数
    if (hints != null) {
    	//获取容错等级
      if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
        errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
      }
      //获取边距
      if (hints.containsKey(EncodeHintType.MARGIN)) {
        quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
      }
    }
	//调用编码方法
    QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
    //返回相应参数
    return renderResult(code, width, height, quietZone);
  }

渲染结果生成的私有方法如下:其输出为BitMatrix位矩阵

  // 请注意,输入矩阵使用0==白色,1==黑色,而输出矩阵使用0==黑色,255==白色(即8位灰度位图)。
  private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
    ByteMatrix input = code.getMatrix();
    if (input == null) {
      throw new IllegalStateException();
    }
    int inputWidth = input.getWidth();
    int inputHeight = input.getHeight();
    int qrWidth = inputWidth + (quietZone * 2);
    int qrHeight = inputHeight + (quietZone * 2);
    int outputWidth = Math.max(width, qrWidth);
    int outputHeight = Math.max(height, qrHeight);

    int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
    // 填充包括安静区和额外的白色像素,以适应要求的尺寸。
    // 例如,如果输入为25x25,QR将为33x33,包括安静区。如果请求的尺寸为200x160,则对于132x132的QR,倍数将为4。这些将处理从100x100(实际QR)到200x160的所有填充。
    int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
    int topPadding = (outputHeight - (inputHeight * multiple)) / 2;

    BitMatrix output = new BitMatrix(outputWidth, outputHeight);

    for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
      // 写入条形码此行的内容
      for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
        if (input.get(inputX, inputY) == 1) {
          output.setRegion(outputX, outputY, multiple, multiple);
        }
      }
    }

    return output;
  }

参考资料

QR码生成原理
二维码(QR code)基本结构及生成原理

1 范围 本标准规定了QR符号的要求。它规定了QR模式2符号的特征,数据字符编码,符号格式,尺寸特征,纠错规则,参考译算法,符号质量要求,以及可由用户选择的应用参数,在附录中给出了QR模式1符号不同于模式2的特性。 2 致性 QR符号(及设计用于生成或识读QR符号的设备)如果满足QR模式2或模式1规定的要求,应认作符合本规范。然而要注意,模式2是推荐用于新的和放式系统应用的符号方式。 3 引用标准 下列标准文件所包含的条文,通过在本标准中引用而构成为本标准的条文。对于注明日期的引用标准,以后进行的补充和修改并不适用,然而,鼓励基于国际标准的协议各方对应用以下标准文件最新版本的可能性进行调研,无注明日期的引用标准适用于提交应用的最近的版本。ISO和IEC的成员仍然是当前有效标准的注册机构。 ISO/IEC 15424 信息技术—— 自动识别和数据采集技术 —— 数据载体/符号标识 ISO/IEC 15416 信息技术—— 自动识别和数据采集技术 —— 条印刷质量测试规范 —— 线性条 EN 1556 条——术语 JIS X0201 信息交换用JIS 8位字符集 JIS X0208—1997 信息交换用日语图形字符集 ANSI X 3.4 编码字符集——信息交换用7位美国国家标准(7位ASCII) AIM国际技术规范 扩展解释:第部分:识别方案与协议(称作“AIM ECI规范”) 4 术语和定义 EN1556中的术语和下列各项适用于本标准: 4.1 校正图形(Alignment Pattern) 用于确立矩阵符号位置的个固定的参照图形,译软件可以通过它在图象有中等程度损坏的情况下,再同步图像模块的坐标映象。 4.2 字符计数指示符(Character Count Indicator) 定义某模式下的数据串长度的位序列。 4.3 ECI指示符(ECI designator) 6位数字,用于标识具体的ECI任务。 4.4 编码区域(encoding region) 在符号中没有被功能图形占用,可以对数据或纠错字进行编码的区域。 4.5 扩充解释(Extended Channel Interpretation (ECI)) 在某些制中,对输出数据流允许有与缺省字符集不同的解释的协议。 4.6 扩展图形(Extension Pattern) 模式1中,不表示数据的种功能图形。 4.7 格式信息(Format Information) 种功能图形,它包含符号使用的纠错等级以及使用的掩模图形的信息,以便对编码区域的剩余部分进行译。 4.8 功能图形(function pattern) 符号中用于符号定位与特征识别的特定图形。 4.9 掩模图形参考(Mask Pattern Reference) 用于符号的三位掩模图形标识符。 4.10 掩模(masking) 在编码区域内,用掩模图形对位图进行XOR操作,其目的是使符号中深色与浅色模块数的比例均衡,并且减少影响图像快速处理的图形出现。 4.11 模式(mode) 将特定的字符集表示成位串的方法。 4.12 模式指示符(Mode Indicator) 4位标识符,指示随后的数据序列所用的编码模式。 4.13 填充位(Padding Bit) 值为0,不表示数据,用于填充数据位流最后字中终止符后面的空位。 4.14 位置探测图形(Position Detection Pattern) 组成寻象图形的三个相同的图形之。 4.15 剩余位(Remainder Bit) 值为0,不表示数据,当编码区域不能正好被8位的字填满时,用于填充最后字后的空位。 4.16 剩余字(Remainder Codeword) 种填充字,当所有的数据字和纠错字不能正好填满符号的容量时,用于填充所空字位置,它们紧跟在最后个纠错字之后。 4.17 段(segment) 以同ECI或编码模式编码的数据序列。 4.18 分隔符(Separator) 全部由浅色模块组成的功能图形,宽度为个模块,用于将位置探测图形与符号的其余部分分。 4.19 终止符(Terminator) 用于结束表示数据位流的位图0000。 4.20 定位图形(Timing Pattern) 深色与浅色模块交错的图形,便于决定符号中模块的坐标。 4.21 版本(Version) 用于表示符号规格的系列。某特定版本是根据它在所允许的规格系列中的位置来确定的。QR所允许规格系列为21×21模块(版本1)~177×177模块(版本40)。它也可同时指示符号所应用的纠错等级。 4.22 版本信息(Version Information) 在模式2符号中,包含符号版本的信息及该数据纠错位的功能图形。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值