7.15 QR Code
Limax参考ISO/IEC 18004-2006提供QR Code编解码支持(Micro QR Code除外)。
所有语言版本都实现在单个文件中,没有任何语言标准库之外的依赖,便于引用。
-
7.15.1 编码器
-
原型
Java QrCode limax.codec.QrCode.encode(byte[] data, int ecl) C# QrCode limax.codec.QrCode.encode(byte[] data, int ecl) C99 QrCode qr_encode(void *data, int size, int ecl) Javascript QrCode encode(data, ecl) 1. 参数
1.1. data,字节数组,特别的,对于Javascript,提供方法QrCode.toUTF8,可以将浏览器中输入的字符串转换为UTF8字节数组。(可以参考javascript目录下的qrcode.html)
1.2. ecl,各语言定义的常数ECL_L,ECL_M,ECL_Q,ECL_H决定纠错级别。
2. 返回值
2.1. QrCode对象, 输入数据过长以至于VERSION 40的QR Code都不能表示, 返回null。特别的, 对于C99, QrCode对象需要通过qrcode->release(qrcode);的方式释放。
2.2. 所有QrCode对象都提供方法toSvgXML方法,返回SvgXML字符串,输出到文件,便于浏览器直接显示。
2.3. Java,C#的QrCode对象提供方法getModules(),返回表示QrCode模块的boolean数组,数组长度开平方取整,即可获得尺寸,C99中直接访问QrCode的成员modules,size即可,通过模块数组可以方便地生成其它形式的表示,比如图片(可以参考limax.executable.QrCodeTool.java的实现)
-
ISO/IEC 18004-2006相容性
1. 只支持单一的数字模式,字母数字模式,字节模式编码,不使用混合模式。输入数据全部符合数字模式选择数字模式,全部符合字母数字模式选择字母数字模式,否则使用字节模式。
2. 不支持ECI,不支持FNC1,不支持KANJI,不支持结构链。
-
应用讨论
1. 通常输入数据应该考虑使用UTF8编码。
2. 通过QR Code交换数据,如果要求生成尽量小的QR Code, 使用纯数字最有效, 数字模式生成短编码, 加入大写字母, 字母数字模式生成较短编码。避免使用小写字母。
3. 如果QR Code中间需要嵌入图片,选择较高纠错级别,比如,ECL_Q,ECL_H。
-
-
7.15.2 解码器(解码一张黑白图片中的QR Code)
-
原型
Java QrCode.Info limax.codec.QrCode.decode(byte[] image_1bit, int width, int height, int sample_granularity); C99 QrCodeInfo qr_decode(char *image_1bit, int width, int height, int sample_granularity); 1. 参数
1.1. image_1bit,输入应该为1bit黑白图片,一个字节表示一个像素,0为黑。
1.2. width,图片宽度
1.3. height,图片高度
1.4. sample_granularity,采样粒度,1为逐点采样,较大的图片选择较大采样粒度可以获得更好的性能,但是可能导致解码失败。
2. 返回值
2.1. 无论解码成功失败,都返回QrCode.Info,或者QrCodeInfo对象,C99中的返回对象info应该通过info->release(info)方式释放。成功失败由其中的status字段表示。C99中对象字段直接访问即可,Java中通过get方式获取。
2.2. status,状态字段。Java中的enum QrCode.Info.Status,C99中的,enum scan_status。有多种返回状态值。
2.2.1. SCAN_OK,扫描成功,可以获取正确的解码数据。
2.2.2. ERR_POSITIONING,图片中的QR Code定位失败。
2.2.3. ERR_VERSION_INFO,获取版本信息失败。
2.2.4. ERR_ALIGNMENTS,定位QR Code中的ALIGNMENT PATTERNS失败。
2.2.5. ERR_FORMAT_INFO,获取格式信息(纠错级别和掩码)失败。
2.2.6. ERR_UNRECOVERABLE_CODEWORDS,码字错误数量超过纠错容量。
2.2.7 UNSUPPORTED_ENCODE_MODE, 不支持的编码模式, 这种情况下从Info中获取的数据是纠错完成的未解码数据, 应用如果有能力应该自己解码。
2.3. data,length,返回数据的字节数组表示,C99中不可理解为NULL终止串。
2.4. reverse,图片是否是反转的。
2.5. mirror,图片是否是镜像的。
2.6. version,版本
2.7. ecl,纠错级别
2.8. mask,掩码号
-
ISO/IEC 18004-2006相容性
1. 不支持Micro QR Code解码
2. 支持QR Code的反转和镜像
3. 支持混合模式,数字模式,字母数字模式,字节模式,KANJI模式(KANJI模式编码的日文字符转换为UTF8输出)
4. 支持ECI模式,两种FNC1模式,使用ISO/IEC 15424指定的符号标识方式输出。(ECI, FNC1能否交错, 如何交错, 两个标准都没有提及, 实现为允许随意交错)
5. 不支持结构链模式
-
-
7.15.3 示例工具
-
Javascript版本
javascript目录下的qrcode.html,可以根据输入串,生成纠错级别为H的QR Code。
-
Java版本
java -jar limax.jar qrcode encode <filename> <(L|M|Q|H)> <text>
为text文字串生成QR Code,输出到filename指定的文件中。文件内容由文件名后缀决定,如果是svg,生成SvgXML,除此之外生成图形文件,文件名后缀必须为ImageIO.write方法支持的文件格式。
java -jar limax.jar qrcode decode <filename> [sample_granularity=1] [bwthreshold=64] [meanfilter=2]
解码图片,可以使用一张包含QR Code的图片执行解码。
sample_granularity为采样粒度, bwthreshold为图片转换为灰度图之后进一步转换为黑白图的阈值(这里存在一个比较奇怪的问题, jdk的灰度转换结果不符合Y = 0.299R + 0.587G + 0.114B的灰度转换公式, 明显偏暗, 不得已选择了bwthreshold=64), meanfilter决定了均值过滤器尺寸, 使用周边(N*2+1)^2个像素参与中心像素均值计算,均值过滤器过滤掉可能影响解码的图像噪点。
默认参数比较适合解码照片,如果要解码屏幕截图,这种参数选择可能导致失败。
java –jar limax.jar qrcode decode capture.jpg 1 128 0
也许是合适的选择,屏幕分辨率通常比照片小得多,meanfilter=2有可能把截图中的QR Code模块破坏掉, 另外, 屏幕截图通常不存在噪点问题, 0才是更合适的选择。
-
-
7.15.4 高级话题
-
解码性能
1. 显而易见,越小的图片解码性能越高,图片越小细节越少,解码高版本QR Code可能存在问题, 缩小原始图像或者截取部分原始图像属于工程手段,不作重点讨论。
2. 解码操作属于内存密集型操作, java版本性能终归比不上C99版本,对性能有要求的应用建议使用C99版本。
3. 彩色图像到黑白图像的预处理过程比解码操作本身开销更大, 可以考虑使用能够充分利用GPU能力的图像处理库, demo中使用了opencv。pc上实测,比java版本可以有10倍以上提升。
4. 上述手段都用上了,还不能满足要求,可以选择较大的sample_granularity解码参数。
5. 更加极端的需求,就只能对解码器进行并行优化了,并行优化的运行环境相关性太大,这里不提供,源码中scanFinder方法就是最大的性能瓶颈,可以重点考虑。
-
图像质量
1. 图像质量是能否成功解码的先决条件,受各种因素影响。
2. ISO/IEC 18004-2006对QR Code图像的规格,印刷规则有明确指导。现实中确有很多不合规QR Code存在, 例如,Border明确要求至少4倍模块宽度,却有不少QR Code在Border范围内印刷文字。
3. QR Code使用RS纠错算法, 对于QR Code内图像污染有一定的抵抗能力, 但是无法解决三个角上的FinderPattern的污染。此外, 3个FinderPattern并不足以进行透视矫正, 当前实现通过扫描底边和右边确定QR Code的右下角, 所以对底部空白, 右边空白有一定的要求, 对于扫描范围内的污染没有太好的抵抗能力。
4. 版本越高的QR Code对于对焦的要求越高, 对焦不好, 边缘模糊的QR Code图像滤波之后黑模块与白模块的宽度比例会出现明显偏差, FinderPattern不能确定下来,直接导致解码失败。
5. 照度不足将导致相机选择更高感光度, 产生更多图像噪点, 图像滤波可以解决一定的问题。 如何选择更好的滤波方案只能根据具体应用环境实测。
6. 不支持同一图片上多个QR Code, 多QR Code可能导致FinderPattern选择错误。PC屏幕上某些选中的Radio Button具有明显的FinderPattern特征, 实际环境中同样可能存在这类干扰。当前实现, 以面积最大为标准选择3个FinderPattern,不作方差过滤(较重的透视变形使得远端FinderPattern明显偏小,方差过滤可能滤掉这样的FinderPattern)。面积最大的标准,决定了QR Code面积在图片中占比越大, 解码成功率越高,扫描明显很小QR Code可能需要进行图像截取,或者局部放大。
7. 不可能完全支持非标准的创意QR Code, 某些创意QR Code贴合特定解码器的参数条件进行设计, 为这样QR Code调试参数没有太大意义, 除非把兼容这些QR Code作为应用的设计目标。
-
Demo
1. demo/qrdecode目录下提供了pc与android两个版本的demo,编译这些版本均需要下载opencv库,进行相应配置。
2. pc版本与java版本功能相同,用来解码照片中的QR Code,有极大的性能提升。
3. pc版本与android版本的关键代码完全相同,可以辅助设计特定场景下的android应用,试验和选择图像滤波算法以及参数。
4. android版本利用相机直接实现一个通用的实时解码器,显示QR Code信息。
-