C# QRCode二维码的解析与生成
已知一张二维码图片,怎么生成一张一模一样的图片出来?
最近有个项目,需要用到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二维码的解析与生成的更多相关文章
- java代码生成二维码以及解析二维码
package com.test; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedI ...
- h5端呼起摄像头扫描二维码并解析
2016年6月29日补充: 最近做了一些与表单相关的项目,使用了h5的input控件,在使用过程中遇到了很多的坑.也包括与这篇文章相关的. 首先我们应该知道使用h5新提供的属性getUserMedia ...
- 用Java画QRCode二维码
支付宝.微信扫码支付的二维码,第三方的类库QRCode.jar 还是很好用的.下面贴出来这个东东生成二维码的代码. 使用时注意包括图片地址.编码内容.图片属性等几个参数,支付宝的它们的扫码回调地址. ...
- Android开发 QRCode二维码开发第三方框架
前言 Android开发里二维码开发经常用到,这里简单的介绍下Android开发里的二维码. 最广泛使用的二维码库zxing zxing是最广泛的二维码库各个平台都可以适用它,但是Android平台使 ...
- iOS - QRCode 二维码
1.QRCode 在 iOS7 以前,在 iOS 中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing. 这两大组件我们都有用过,这里总结下各自的缺点: 1.ZBar 在扫描 ...
- (转)QRCODE二维码介绍及常用控件推荐
什么是QR Code码? QR Code码是由日本Denso公司于1994年9月研制的一种矩阵二维码符号,它具有一维条码及其它二维条码所具有的信息容量大.可靠性高.可表示汉字及图象多种文字信息.保密防 ...
- jQuery生成QRcode二维码
jQuery生成QRcode二维码示例 <!DOCTYPE html> <html> <head> <meta charset="utf-8&quo ...
- PHP生成QRCode二维码
php生成QRCode二维码示例 <?php //引入 phpqrcode 类库 //phpqrcode下载地址:https://github.com/t0k4rt/phpqrcode //或从 ...
- 用CIFilter生成QRCode二维码图片
用CIFilter生成QRCode二维码图片 CIFilter不仅仅可以用来做滤镜,它还可以用来生成二维码. CIFilterEffect.h + CIFilterEffect.m // // CIF ...
- C# QRCode 二维码
/*********************************************************************** * C# QRCode 二维码 * 说明: * 本文记 ...
随机推荐
- 2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range。x有序,x[i]表示i号怪兽在x轴上的位置;hp[i]表示i号怪兽的血量 。range表示法师如果站在x位置,用A
2021-05-08:给定两个非负数组x和hp,长度都是N,再给定一个正数range.x有序,x[i]表示i号怪兽在x轴上的位置:hp[i]表示i号怪兽的血量 .range表示法师如果站在x位置,用A ...
- 2021-11-05:摆动排序 II。给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。你可以假设所有输入数组都可以
2021-11-05:摆动排序 II.给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]- 的顺序.你可以假设所有 ...
- Windows server 2012 r2 激活方法
slmgr /ipk W3GGN-FT8W3-Y4M27-J84CP-Q3VJ9 slmgr /skms kms.03k.org slmgr /ato
- Module build failed: Error: Plugin/Preset files are not allowed to export objects, only functions.
运行项目是提示Module build failed: Error: Plugin/Preset files are not allowed to export objects, only funct ...
- Java(数组使用、Arrays、稀疏数组)
1.数组的使用 For-Each循环 int[] arrays = {1,2,3,4,5}; //打印全部的数组元素 JDK1.5 没有下标 for (int array : arrays) { Sy ...
- FnOnce , FnMut <RUST>
FnOnce 1 #[lang = "fn_once"] 2 #[must_use = "closures are lazy and do nothing unless ...
- C#.NET CORE .NET6 RSA 私钥签名 公钥验签(验证签名) ver:20230614
C#.NET CORE .NET6 RSA 私钥签名 公钥验签(验证签名) ver:20230614 环境说明: .NET CORE 版本:.NET 6 . .NET CORE 对于RSA的支持: 1 ...
- C++面试八股文:什么是智能指针?
某日二师兄参加XXX科技公司的C++工程师开发岗位第19面: 面试官:什么是智能指针? 二师兄:智能指针是C++11引入的类模板,用于管理资源,行为类似于指针,但不需要手动申请.释放资源,所以称为智能 ...
- Gamma:强大的AI制作PPT神器,用完再也回不去了!
看过许多 AI 制作 PPT 软件,最终还是被 Gamma 惊艳到. Gamma 是一款基于人工智能技术的 PPT 制作工具,可以帮助用户轻松制作高质量的 PPT 演示文稿. 痛点解决 相比传统制作 ...
- Codeforces Round #879 (Div. 2) A-E
比赛链接 A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { i ...