已知一张二维码图片,怎么生成一张一模一样的图片出来?

  最近有个项目,需要用到QRCode,之前只做过Datamatrix格式的,想着应该也是差不多的,于是就依葫芦画瓢,掏出我的陈年OnBarcode类库,一通修改,生成了个崭新的QRCode,与客户提供的二维码图片一比对,虽然扫出来内容一样,但明显图案并不相同,于是我就意识到,事情并不简单。原图如下:

  首先第一个怀疑的就是参数问题啦,看了一下OnBarcode.Barcode.QRCode的属性,可疑的参数有DataMode、ECL、Version等等,毕竟这几个应该是常见的参数。最大的可能就是ECL,这个是二维码的纠错级别,一般是L/M/Q/H四个等级,对应可遮挡7%/15%/25%/30%,一般如果想在二维码中插入logo的话,就要把纠错级别调高一些。

  于是我尝试了一下,修改了不同的ECL,对应输出的图案都是不同的,但还是没有生成我想要的图案。

  难道是我的陈年OnBarcode类库跟不上时代了?于是我换成了QRCoder,又是一通折腾,还是没对上。再是找了一些在线生成二维码网站(见下方链接),逐个比较,发现还真是五花八门,有些是不提供设置直接生成,有些是可修改版本及ECL的,最后结果都不太一样,幸好有些网站生成的图片是对上了的,总算有条退路。

  既然有了备用方案,那我就可以慢慢研究了。查了下QRCode的生成原理,参考《【来龙去脉系列】QRCode二维码的生成细节和原理》,这一篇讲得不错,不过他在讲Mask那里有点模糊,这也导致我一不留神就掉坑了,后面看了另外两篇才纠正回来。看完大概有了概念,至少明确了,我这个码应该是是version1,Alphanumeric mode 字符编码,同时也知道还有个掩码参数(即Mask),而且从图案中可以看到Format Information,那里面存放着ECL跟Mask。文中将Format Information标注0-14,在左上角从上往下从右往左,并且说15个bits中包括5个数据bits:其中,2个bits用于表示使用什么样的Error Correction Level, 3个bits表示使用什么样的Mask,那我自然就认为0-4就是所谓的数据bits了,看了下是11100,跟10101异或得出01001,ECL是01-L?但我在线生成的时候是选的H,那就肯定不对。

  这时候我发现有个在线生成网站写明了用的是ZXing,于是我又引用了ZXing尝试了一下,按照常规的参数配置了ECL-H,还是不行,一度陷入瓶颈。

  我还突发奇想试着把原图上传到在线网站去解析,说不定有哪个网站能给出点提示,但最终也只是能看到内容。不过这么一来我又打开了思路,我可以自己解析,说不定就能拿到配置参数了,感觉可行性还是有的。

  刚好ZXing就有解码的功能,尝试一下:

 1 static void ParseQRCode(string imagePath, out string data, out IDictionary<ResultMetadataType, object> hints)
2 {
3 hints = null;
4 BarcodeReader reader = new BarcodeReader();
5 reader.Options.PossibleFormats = new List<BarcodeFormat> { BarcodeFormat.QR_CODE };//可加可不加
6 Bitmap bitmap = new Bitmap(imagePath);
7 Result result = reader.Decode(bitmap);
8 if (result != null)
9 {
10 data = result.Text;
11 hints = result.ResultMetadata;
12 }
13 else
14 {
15 data = null;
16 }
17 }

  输入图片路径,找了一下result的属性,果然在ResultMetadata里面存放着我要的信息:

  ECL是H,那就没错了,剩下的参数里面,这个QR_MASK_PATTERN不就是掩码参数咯,剩下两个看了下参数介绍,应该不是很重要,于是重点关注QR_MASK_PATTERN。刚好手头的代码引用的是ZXing,这里要注意的是,ZXing的Hints是不能整个赋值的,只能用Add的方式逐个插入参数:

 1 public static Bitmap CreateQRCode(string data)
2 {
3 Bitmap bitmap = null;
4 GC.Collect();
5 BarcodeWriter barCodeWriter = new BarcodeWriter();
6 barCodeWriter.Format = BarcodeFormat.QR_CODE; // 生成码的方式(这里设置的是二维码),有条形码\二维码\还有中间嵌入图片的二维码等
7 //barCodeWriter.Options.Hints.Add(EncodeHintType.CHARACTER_SET, "UTF-8");// 支持中文字符串
8 barCodeWriter.Options.Hints.Add(EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.H);
9 barCodeWriter.Options.Hints.Add(EncodeHintType.QR_MASK_PATTERN, 2);
10 barCodeWriter.Options.Height = 200;
11 barCodeWriter.Options.Width = 200;
12 barCodeWriter.Options.Margin = 0; //设置的白边大小
13 ZXing.Common.BitMatrix bm = barCodeWriter.Encode(data);
14 bitmap = barCodeWriter.Write(bm);
15 return bitmap;
16 }

  果然,结果跟原图一样(外面的圈是我自己加的):

  问题已经解决了,但我还是有个疑惑,为什么我根据图案的Format Information得出的参数是不对的,于是我继续翻资料,终于在《[译] 为程序员写的Reed-Solomon码解释》这一篇里面看到了,是逆时针读的bit,wxxxx,最开始看的那一篇是顺时针标注的啊,那不就是反过来,仔细一看,读出来是00111,跟10101异或得10010,ECL是10-H,Mask是010-2,对上了。只能说,还是得多方考证吧。

  完结撒花。

  • 参考资料:

《【来龙去脉系列】QRCode二维码的生成细节和原理》 https://www.cnblogs.com/tuyile006/p/10916075.html

《二维码生成原理》 https://zhuanlan.zhihu.com/p/543574464

《[译] 为程序员写的Reed-Solomon码解释》 https://www.felix021.com/blog/read.php?2116

《C# 生成二维码方法(QRCoder)》 https://www.cnblogs.com/yakniu/p/16917897.html

《.NET Core(C#)使用ZXing.Net生成条码(Barcode)和二维码(QR code)图片及示例代码》 https://www.cnblogs.com/fireicesion/p/16809637.html

《C# 利用ZXing.Net来生成条形码和二维码》 https://blog.csdn.net/lwf3115841/article/details/128429605

  • 在线工具:

OSCHINA https://tool.oschina.net/qr

互联二维码 https://www.hlcode.cn/decode

草料二维码 https://cli.im/text

二维码工坊 https://www.2weima.com/?text=A0010101

C# QRCode二维码的解析与生成的更多相关文章

  1. java代码生成二维码以及解析二维码

    package com.test; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedI ...

  2. h5端呼起摄像头扫描二维码并解析

    2016年6月29日补充: 最近做了一些与表单相关的项目,使用了h5的input控件,在使用过程中遇到了很多的坑.也包括与这篇文章相关的. 首先我们应该知道使用h5新提供的属性getUserMedia ...

  3. 用Java画QRCode二维码

    支付宝.微信扫码支付的二维码,第三方的类库QRCode.jar 还是很好用的.下面贴出来这个东东生成二维码的代码. 使用时注意包括图片地址.编码内容.图片属性等几个参数,支付宝的它们的扫码回调地址. ...

  4. Android开发 QRCode二维码开发第三方框架

    前言 Android开发里二维码开发经常用到,这里简单的介绍下Android开发里的二维码. 最广泛使用的二维码库zxing zxing是最广泛的二维码库各个平台都可以适用它,但是Android平台使 ...

  5. iOS - QRCode 二维码

    1.QRCode 在 iOS7 以前,在 iOS 中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing. 这两大组件我们都有用过,这里总结下各自的缺点: 1.ZBar 在扫描 ...

  6. (转)QRCODE二维码介绍及常用控件推荐

    什么是QR Code码? QR Code码是由日本Denso公司于1994年9月研制的一种矩阵二维码符号,它具有一维条码及其它二维条码所具有的信息容量大.可靠性高.可表示汉字及图象多种文字信息.保密防 ...

  7. jQuery生成QRcode二维码

    jQuery生成QRcode二维码示例 <!DOCTYPE html> <html> <head> <meta charset="utf-8&quo ...

  8. PHP生成QRCode二维码

    php生成QRCode二维码示例 <?php //引入 phpqrcode 类库 //phpqrcode下载地址:https://github.com/t0k4rt/phpqrcode //或从 ...

  9. 用CIFilter生成QRCode二维码图片

    用CIFilter生成QRCode二维码图片 CIFilter不仅仅可以用来做滤镜,它还可以用来生成二维码. CIFilterEffect.h + CIFilterEffect.m // // CIF ...

  10. C# QRCode 二维码

    /*********************************************************************** * C# QRCode 二维码 * 说明: * 本文记 ...

随机推荐

  1. 2020-12-10:i++是原子操作吗?为什么?

    福哥答案2020-12-10: 不是原子操作.i++分为三个阶段:1.内存到寄存器.2.寄存器自增.3.写回内存.这三个阶段中间都可以被中断分离开.***[评论](https://user.qzone ...

  2. 玩转服务器之环境篇:PHP和Python环境部署指南

    前几篇文章中讲解了如何搭建docker和Java Web环境的方法,本篇文章来教大家搭建一个好的PHP和Python环境,可以帮助开发和运行PHP和Python应用程序,使其更加高效和稳定. 一. P ...

  3. .join()字符串操作函数

    join函数是一个字符串操作函数 str.join(item)str表示字符串(字符),item表示一个成员,注意括号里必须只能有一个成员,比如','.join('a','b')这种写法是行不通的 举 ...

  4. shrio QuickStart

    Shrio三大对象:  springboot整合shrio 登录拦截认证 创建项目时勾选web,导入依赖: <dependency> <groupId>org.apache.s ...

  5. 基于.NetCore开源的Windows的GIF录屏工具

    推荐一个Github上Start超过20K的超火.好用的屏幕截图转换为 GIF 动图开源项目. 项目简介 这是基于.Net Core + WPF 开发的.开源项目,可将屏幕截图转为 GIF 动画.它的 ...

  6. Java的Atomic原子类

    Java SDK 并发包里提供了丰富的原子类,我们可以将其分为五个类别,这五个类别提供的方法基本上是相似的,并且每个类别都有若干原子类. 对基本数据类型的变量值进行原子更新: 对对象变量的指向进行原子 ...

  7. qq飞车端游最全按键指法教学

    目录 起步篇 超级起步 弹射起步 段位起步 基础篇 点飘 撞墙漂移 撞墙点喷 进阶篇 双喷 叠喷 断位漂移 段位双喷 侧身漂移 快速出弯 CW WCW CWW 牵引 甩尾点飘 甩尾漂移 右侧卡 左侧卡 ...

  8. Python Excel 操作 | xlrd+xlwt 模块笔记

    Python 的pandas模块使用xlrd作为读取 excel 文件的默认引擎.但是,xlrd在其最新版本(从 2.0.1 版本开始)中删除了对 xls 文件以外的任何文件的支持. xlsx fil ...

  9. [Java SE] 彻底搞懂Java程序的三大参数配置途径:系统变量与JVM参数(VM Option)/环境变量/启动程序参数args

    0 序言 一次没搞懂,处处受影响.这个问题属于基础问题,但又经常踩坑,不得不重视一下了. 1 Java程序动态参数的配置途径:系统变量与JVM参数(VM Option) vs 环境变量 vs 启动程序 ...

  10. 通过模仿学会Python爬虫(一):零基础上手

    好家伙,爬虫来了 爬虫,这玩意,不会怎么办, 诶,先抄一份作业回来 1.别人的爬虫  Python爬虫史上超详细讲解(零基础入门,老年人都看的懂)_ChenBinBini的博客-CSDN博客 # -* ...