已知一張二維碼圖片,怎麼生成一張一模一樣的圖片出來? 最近有個項目,需要用到QRCode,之前只做過Datamatrix格式的,想著應該也是差不多的,於是就依葫蘆畫瓢,掏出我的陳年OnBarcode類庫,一通修改,生成了個嶄新的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
本文來自博客園,作者:MaQaQ,轉載請註明原文鏈接:https://www.cnblogs.com/magicMaQaQ/p/17639708.html