• 已知一张二维码图片,怎么生成一张一模一样的图片出来?
  • 发布于 2个月前
  • 248 热度
    0 评论
已知一张二维码图片,怎么生成一张一模一样的图片出来?最近有个项目,需要用到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就有解码的功能,尝试一下:
static void ParseQRCode(string imagePath, out string data, out IDictionary<ResultMetadataType, object> hints)
        {
            hints = null;
            BarcodeReader reader = new BarcodeReader();
            reader.Options.PossibleFormats = new List<BarcodeFormat> { BarcodeFormat.QR_CODE };//可加可不加
            Bitmap bitmap = new Bitmap(imagePath);
            Result result = reader.Decode(bitmap);
            if (result != null)
            {
                data = result.Text;
                hints = result.ResultMetadata;
            }
            else
            {
                data = null;
            }
        }
输入图片路径,找了一下result的属性,果然在ResultMetadata里面存放着我要的信息:

ECL是H,那就没错了,剩下的参数里面,这个QR_MASK_PATTERN不就是掩码参数咯,剩下两个看了下参数介绍,应该不是很重要,于是重点关注QR_MASK_PATTERN。刚好手头的代码引用的是ZXing,这里要注意的是,ZXing的Hints是不能整个赋值的,只能用Add的方式逐个插入参数:
public static Bitmap CreateQRCode(string data)
        {
            Bitmap bitmap = null;
            GC.Collect();
            BarcodeWriter barCodeWriter = new BarcodeWriter();
            barCodeWriter.Format = BarcodeFormat.QR_CODE; // 生成码的方式(这里设置的是二维码),有条形码\二维码\还有中间嵌入图片的二维码等
            //barCodeWriter.Options.Hints.Add(EncodeHintType.CHARACTER_SET, "UTF-8");// 支持中文字符串
            barCodeWriter.Options.Hints.Add(EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.H);
            barCodeWriter.Options.Hints.Add(EncodeHintType.QR_MASK_PATTERN, 2);
            barCodeWriter.Options.Height = 200;
            barCodeWriter.Options.Width = 200;
            barCodeWriter.Options.Margin = 0; //设置的白边大小
            ZXing.Common.BitMatrix bm = barCodeWriter.Encode(data);
            bitmap = barCodeWriter.Write(bm);
            return bitmap;
        }
果然,结果跟原图一样(外面的圈是我自己加的):

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

完结撒花。
用户评论