java 二維碼原理以及用java實現的二維碼的生成、解碼(轉)


QR碼生成原理(一)

一、什么是QR

QR碼屬於矩陣式二維碼中的一個種類,由DENSO(日本電裝)公司開發,由JISISO將其標准化。QR碼的樣子其實在很多場合已經能夠被看到了,我這還是貼個圖展示一下:

這個圖如果被正確解碼,應該看到我的名字和郵箱。

二、QR碼的特點

說到QR碼的特點,一是高速讀取(QR就是取自“Quick Response”的首字母),對讀取速度的體驗源自於我手機上的一個軟件,象上面貼出的碼圖,通過攝像頭從拍攝到解碼到顯示內容也就三秒左右,對攝像的角度也沒有什么要求;

二是高容量、高密度;理論上內容經過壓縮處理后可以存7089個數字,4296個字母和數字混合字符,29538位字節數據,1817個漢字;

三是支持糾錯處理;糾錯處理相對復雜,目前我還沒有深入了解,按照QR碼的標准文檔說明,QR碼的糾錯分為4個級別,分別是:

·         level L : 最大 7%的錯誤能夠被糾正;

·         level M : 最大 15%的錯誤能夠被糾正;

·         level Q : 最大 25%的錯誤能夠被糾正;

·         level H : 最大 30%的錯誤能夠被糾正;

四是結構化;看似無規則的圖形,其實對區域有嚴格的定義,下圖就是一個模式2、版本1QR圖結構(關於QR碼的"模式""版本"將在后面進行介紹)

在上圖21*21的矩陣中,黑白的區域在QR碼規范中被指定為固定的位置,稱為尋像圖形(finder pattern)和定位圖形(timing pattern)。尋像圖形和定位圖形用來幫助解碼程序確定圖形中具體符號的坐標。

黃色的區域用來保存被編碼的數據內容以及糾錯信息碼。

藍色的區域,用來標識糾錯的級別(也就是Level LLevel H)和所謂的"Mask pattern",這個區域被稱為格式化信息format information)。

五是擴展能力。QR碼的Structure Append特點,使一個QR碼可以分解成多個QR碼,反之,也可以將多個QR碼的數據組合到一個QR碼中來。

  

三、QR碼的模式和版本

前面提到過QR碼的模式(Model)和版本(Version)QR碼分為Model1Model2兩種模式,Model1是對QR的初始定義,Model2是對Model1的擴展,目前使用較為普遍的是Model2,本文的所有說明也僅用於Model2

QR圖的大小(size)被定義為版本(Version),版本號從140。版本1就是一個21*21的矩陣,每增加一個版本號,矩陣的大小就增加4個模塊(Module),因此,版本40就是一個177*177的矩陣。(版本越高,意味着存儲的內容越多,糾錯能力也越強)。

三、QR碼支持的編碼內容

QR碼支持編碼的內容包括純數字、數字和字符混合編碼、8位字節碼和包含漢字在內的多字節字符。其中:

數字:每三個為一組壓縮成10bit

字母數字混合:每兩個為一組,壓縮成11bit                                 

8bit字節數據:無壓縮直接保存。

多字節字符:每一個字符被壓縮成13bit

 

 

QR碼編碼原理二(編碼)

 

編碼就是把常見的數字、字符等轉換成QR碼的方法。說具體的編碼之前,先說一下QR碼的最大容量問題。

一、最大容量

QR碼的最大容量取決於選擇的版本、糾錯級別和編碼模式(Mode:數字、字符、多字節字符等)。以版本1、糾錯級別為Level QQR碼為例,可以存儲27個純數字,或17個字母數字混合字符或118bit字節數據。如果要存儲同樣多的內容同時提高糾錯級別,則需要采用更高的版本。版本1~9數據容量、糾錯碼容量對照如下表:

(version)

(error correcting level)

(count of data code words)

count of EC code words

(numeric)

(alphanumeric)

8bit

1

L

19

7

41

25

17

M

16

10

34

20

14

Q

13

13

27

16

11

H

9

17

17

10

7

2

L

34

10

77

47

32

M

28

16

63

38

26

Q

22

22

48

29

20

H

16

28

34

20

14

3

L

55

15

127

77

53

M

44

26

101

61

42

Q

34

36

77

47

32

H

26

44

58

35

24

4

L

80

20

187

114

78

M

64

36

149

90

62

Q

48

52

111

67

46

H

36

64

82

50

34

5

L

108

26

255

154

106

M

86

48

202

122

84

Q

62

72

144

87

60

H

46

88

106

64

44

6

L

136

36

322

195

134

M

108

64

255

154

106

Q

76

96

175

108

74

H

60

112

139

84

58

7

L

156

40

370

224

154

M

124

72

293

178

122

Q

88

108

207

125

86

H

66

130

154

93

64

8

L

194

48

461

279

192

M

154

88

365

221

152

Q

110

132

259

157

108

H

86

156

202

122

84

9

L

232

60

552

335

230

M

182

110

432

262

180

Q

132

160

312

189

130

H

100

192

235

143

98

如果要了解更詳細的QR碼容量信息,可以到電裝的網站去看看http://www.denso-wave.com/qrcode/vertable1-e.html

 

下面,就舉例說明將“ABCDE123”轉換成為版本1Level HQR碼轉換方法。

二、模式標識符(Mode Indicator)

QR碼的模式(Mode)就是前文提到的數字、字符、8bit字節碼、多字節碼等。對於不同的模式,都有對應的模式標識符(Mode Indicator)來幫助解碼程序進行匹配,模式標識符是4bit的二進制數:

1、數字模式(numeric mode: 0001
2
、混合字符模式(alphanumeric mode : 0010
3
8bit byte mode: 0100
4
、日本漢字(KANJI mode : 1000

5、中國漢字(GB2312):1101

由於示例文本串是混合字符,因此將選擇alphanumeric mode,其標識碼為:0010

三、文本串計數標識符(Character count indicator)

文本串計數標識符用來存儲源內容字符串的長度,在版本1-9QR碼中,文本串長度標識符自身的長度被定義為:
數字 : 10bit 
混合字符 : 9bit
8bit 
字節碼 : 8bit
多字節碼 : 8bit

在本例中,源文本串的長度為8個字符,混合字符的長度為9bit,因此將字符個數8編碼為9位二進制表示:000001000

加上混合字符模式標識碼,總的編碼為0010 000001000

四、數據內容編碼

 1、數字模式下的編碼

在數字模式下,數據被限制為3個數字一段,分成若干段。如:"123456"將分成"123" "456",分別被編碼成10bit的二進制數。“123”10bit二進制表示法為:0001111011,實際上就是二進制的123

當數據的長度不足3個數字時,如果只有1個數字則用4bit,如果有2個數字就用7bit來表示。
如:"9876"被分成"987""6"兩段,因此被表示為"1111011011 0110"

2、混合字符模式下的編碼

混合字符模式編碼,其字符對照表如下:

0

 0

 

 A

 10

 

 K

 20

 

 U

 30

 

 +

 40

1

1

 

B

11

 

L

21

 

V

31

 

-

41

2

2

 

C

12

 

M

22

 

W

32

 

.

42

3

3

 

D

13

 

N

23

 

X

33

 

/

43

4

4

 

E

14

 

O

24

 

Y

34

 

:

44

5

5

 

F

15

 

P

25

 

Z

35

6

6

 

G

16

 

Q

26

 

[sp]

36

7

7

 

H

17

 

R

27

 

$

37

8

8

 

I

18

 

S

28

 

%

38

9

9

 

J

19

 

T

29

 

*

3

 

編碼方式為:

源碼被分成兩個字符一段,如下所示,每段的第一個字符乘上45,再用第二個數字相加。因此每段變成了11bit2進制碼,如果字符個數只有1個,則用6bit表示。

 

示例:

 

 

"AB"

"CD"

"E1"

"23"

 

 

45*10+11

45*12+13

45*14+1

45*2+3

 

 

461

553

631

93

0010

000001000

00111001101

01000101001

01001110111

00001011101


38bit字節數據不經編碼轉換直接保存。

五、編碼終止符(Terminator)

如果編碼后的字符長度不足當前版本和糾錯級別所存儲的容量,則在后續補"0000",如果容量已滿則無需添加終止符。此時得到的編碼串為:

0010 000001000 00111001101 01000101001 01001110111 000010111010000
六、編成8bit碼字(Code words)

將以上的編碼再按8bit一組,形成碼字(code words):

 00100000 01000001 11001101 01000101 00101001 11011100 00101110 10000
如果尾部數據不足8bit,則在尾部充0:

00100000 01000001 11001101 01000101 00101001 11011100 00101110 10000000
如果編碼后的數據不足版本及糾錯級別的最大容量,則在尾部補充 "11101100" "00010001",直到全部填滿。最后,版本1Level H下的"ABCDE123"QR碼是:

00100000 01000001 11001101 01000101 00101001 11011100 00101110 1000000011101100

十進制表示法為:

32 65 205 69 41 220 46 128 236

 

 

 

 

 

 

QR碼編碼原理三(日本漢字和中文編碼)

 

一、日本漢字(KANJI)是兩個字節表示的字符碼,編碼的方式是將其轉換為13字節的二進制碼制。

轉換步驟為:

1、對於JIS值為8140(hex)9FFC(hex)之間字符:

a)將待轉換的JIS值減去8140(hex)

b)將高位字節乘以C0(hex);

c)將b)步驟生成的數據加上低位字節;

d)將結果轉換為13位二進制串。

2、對於JIS值為E040(hex)EBBF(hex)之間的字符:

a)將待轉換的JIS值減去C140(hex);

b)將高位字節乘以C0(hex);

c)b)步驟生成的數據加上低位字節;

d)將結果轉換為13位二進制串。

 

二、中文漢字的與日文漢字轉換步驟相似:

1、對於第一字節為0xA1~0xAA之間,第二字節在0xA1~0xFE之間字符:

a)第一字節減去0xA1

b)上一步結果乘以0x60;

c)第二字節減去0xA1;

d)b)步驟的結果加上c步驟的結果;

e)將結果轉換為13位二進制串。

1、對於第一字節為0xB0~0xFA之間,第二字節在0xA1~0xFE之間字符:

a)第一字節減去0xA6

b)上一步結果乘以0x60;

c)第二字節減去0xA1;

d)b)步驟的結果加上c步驟的結果;

e)將結果轉換為13位二進制串。

 

 

 

 

二維碼,是一種采用黑白相間的平面幾何圖形通過相應的編碼算法來記錄文字、圖片、網址等信息的條碼圖片。如下圖

二維碼的特點:

1.  高密度編碼,信息容量大

可容納多達1850個大寫字母或2710個數字或1108個字節,或500多個漢字,比普通條碼信息容量約高幾十倍。

2.  編碼范圍廣

該條碼可以把圖片、聲音、文字、簽字、指紋等可以數字化的信息進行編碼,用條碼表示出來;可以表示多種語言文字;可表示圖像數據。

3.  容錯能力強,具有糾錯功能

這使得二維條碼因穿孔、污損等引起局部損壞時,照樣可以正確得到識讀,損毀面積達50%仍可恢復信息。

4.  譯碼可靠性高

它比普通條碼譯碼錯誤率百萬分之二要低得多,誤碼率不超過千萬分之一。

5.  可引入加密措施

保密性、防偽性好。

6.  成本低,易制作,持久耐用

正因為以上這些特點,二維碼現在越來越流行,應用也是越來越廣(詳細了解請見百度百科,介紹不是本篇重點),所以掌握如何開發二維碼是非常不錯的知識儲備,因此本篇博文將為大家講解如何生成、解析二維碼。

一、Java

所需jar包:QRCode.jar

http://download.csdn.net/detail/songylwq/5115107

TwoDimensionCode類:二維碼操作核心類

[java]  view plain  copy
 print ?
  1. package qrcode;  
  2.   
  3. import java.awt.Color;  
  4. import java.awt.Graphics2D;  
  5. import java.awt.image.BufferedImage;  
  6. import java.io.File;  
  7. import java.io.IOException;  
  8. import java.io.InputStream;  
  9. import java.io.OutputStream;  
  10.   
  11. import javax.imageio.ImageIO;  
  12.   
  13. import jp.sourceforge.qrcode.QRCodeDecoder;  
  14. import jp.sourceforge.qrcode.exception.DecodingFailedException;  
  15.   
  16. import com.swetake.util.Qrcode;  
  17.   
  18. public class TwoDimensionCode {  
  19.       
  20.     /** 
  21.      * 生成二維碼(QRCode)圖片 
  22.      * @param content 存儲內容 
  23.      * @param imgPath 圖片路徑 
  24.      */  
  25.     public void encoderQRCode(String content, String imgPath) {  
  26.         this.encoderQRCode(content, imgPath, "png"7);  
  27.     }  
  28.       
  29.     /** 
  30.      * 生成二維碼(QRCode)圖片 
  31.      * @param content 存儲內容 
  32.      * @param output 輸出流 
  33.      */  
  34.     public void encoderQRCode(String content, OutputStream output) {  
  35.         this.encoderQRCode(content, output, "png"7);  
  36.     }  
  37.       
  38.     /** 
  39.      * 生成二維碼(QRCode)圖片 
  40.      * @param content 存儲內容 
  41.      * @param imgPath 圖片路徑 
  42.      * @param imgType 圖片類型 
  43.      */  
  44.     public void encoderQRCode(String content, String imgPath, String imgType) {  
  45.         this.encoderQRCode(content, imgPath, imgType, 7);  
  46.     }  
  47.       
  48.     /** 
  49.      * 生成二維碼(QRCode)圖片 
  50.      * @param content 存儲內容 
  51.      * @param output 輸出流 
  52.      * @param imgType 圖片類型 
  53.      */  
  54.     public void encoderQRCode(String content, OutputStream output, String imgType) {  
  55.         this.encoderQRCode(content, output, imgType, 7);  
  56.     }  
  57.   
  58.     /** 
  59.      * 生成二維碼(QRCode)圖片 
  60.      * @param content 存儲內容 
  61.      * @param imgPath 圖片路徑 
  62.      * @param imgType 圖片類型 
  63.      * @param size 二維碼尺寸 
  64.      */  
  65.     public void encoderQRCode(String content, String imgPath, String imgType, int size) {  
  66.         try {  
  67.             BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);  
  68.               
  69.             File imgFile = new File(imgPath);  
  70.             // 生成二維碼QRCode圖片  
  71.             ImageIO.write(bufImg, imgType, imgFile);  
  72.         } catch (Exception e) {  
  73.             e.printStackTrace();  
  74.         }  
  75.     }  
  76.   
  77.     /** 
  78.      * 生成二維碼(QRCode)圖片 
  79.      * @param content 存儲內容 
  80.      * @param output 輸出流 
  81.      * @param imgType 圖片類型 
  82.      * @param size 二維碼尺寸 
  83.      */  
  84.     public void encoderQRCode(String content, OutputStream output, String imgType, int size) {  
  85.         try {  
  86.             BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);  
  87.             // 生成二維碼QRCode圖片  
  88.             ImageIO.write(bufImg, imgType, output);  
  89.         } catch (Exception e) {  
  90.             e.printStackTrace();  
  91.         }  
  92.     }  
  93.       
  94.     /** 
  95.      * 生成二維碼(QRCode)圖片的公共方法 
  96.      * @param content 存儲內容 
  97.      * @param imgType 圖片類型 
  98.      * @param size 二維碼尺寸 
  99.      * @return 
  100.      */  
  101.     private BufferedImage qRCodeCommon(String content, String imgType, int size) {  
  102.         BufferedImage bufImg = null;  
  103.         try {  
  104.             Qrcode qrcodeHandler = new Qrcode();  
  105.             // 設置二維碼排錯率,可選L(7%)、M(15%)、Q(25%)、H(30%),排錯率越高可存儲的信息越少,但對二維碼清晰度的要求越小  
  106.             qrcodeHandler.setQrcodeErrorCorrect('M');  
  107.             qrcodeHandler.setQrcodeEncodeMode('B');  
  108.             // 設置設置二維碼尺寸,取值范圍1-40,值越大尺寸越大,可存儲的信息越大  
  109.             qrcodeHandler.setQrcodeVersion(size);  
  110.             // 獲得內容的字節數組,設置編碼格式  
  111.             byte[] contentBytes = content.getBytes("utf-8");  
  112.             // 圖片尺寸  
  113.             int imgSize = 67 + 12 * (size - 1);  
  114.             bufImg = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB);  
  115.             Graphics2D gs = bufImg.createGraphics();  
  116.             // 設置背景顏色  
  117.             gs.setBackground(Color.WHITE);  
  118.             gs.clearRect(00, imgSize, imgSize);  
  119.   
  120.             // 設定圖像顏色> BLACK  
  121.             gs.setColor(Color.BLACK);  
  122.             // 設置偏移量,不設置可能導致解析出錯  
  123.             int pixoff = 2;  
  124.             // 輸出內容> 二維碼  
  125.             if (contentBytes.length > 0 && contentBytes.length < 800) {  
  126.                 boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);  
  127.                 for (int i = 0; i < codeOut.length; i++) {  
  128.                     for (int j = 0; j < codeOut.length; j++) {  
  129.                         if (codeOut[j][i]) {  
  130.                             gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 33);  
  131.                         }  
  132.                     }  
  133.                 }  
  134.             } else {  
  135.                 throw new Exception("QRCode content bytes length = " + contentBytes.length + " not in [0, 800].");  
  136.             }  
  137.             gs.dispose();  
  138.             bufImg.flush();  
  139.         } catch (Exception e) {  
  140.             e.printStackTrace();  
  141.         }  
  142.         return bufImg;  
  143.     }  
  144.       
  145.     /** 
  146.      * 解析二維碼(QRCode) 
  147.      * @param imgPath 圖片路徑 
  148.      * @return 
  149.      */  
  150.     public String decoderQRCode(String imgPath) {  
  151.         // QRCode 二維碼圖片的文件  
  152.         File imageFile = new File(imgPath);  
  153.         BufferedImage bufImg = null;  
  154.         String content = null;  
  155.         try {  
  156.             bufImg = ImageIO.read(imageFile);  
  157.             QRCodeDecoder decoder = new QRCodeDecoder();  
  158.             content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");   
  159.         } catch (IOException e) {  
  160.             System.out.println("Error: " + e.getMessage());  
  161.             e.printStackTrace();  
  162.         } catch (DecodingFailedException dfe) {  
  163.             System.out.println("Error: " + dfe.getMessage());  
  164.             dfe.printStackTrace();  
  165.         }  
  166.         return content;  
  167.     }  
  168.       
  169.     /** 
  170.      * 解析二維碼(QRCode) 
  171.      * @param input 輸入流 
  172.      * @return 
  173.      */  
  174.     public String decoderQRCode(InputStream input) {  
  175.         BufferedImage bufImg = null;  
  176.         String content = null;  
  177.         try {  
  178.             bufImg = ImageIO.read(input);  
  179.             QRCodeDecoder decoder = new QRCodeDecoder();  
  180.             content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");   
  181.         } catch (IOException e) {  
  182.             System.out.println("Error: " + e.getMessage());  
  183.             e.printStackTrace();  
  184.         } catch (DecodingFailedException dfe) {  
  185.             System.out.println("Error: " + dfe.getMessage());  
  186.             dfe.printStackTrace();  
  187.         }  
  188.         return content;  
  189.     }  
  190.   
  191.     public static void main(String[] args) {  
  192.         String imgPath = "G:/TDDOWNLOAD/Michael_QRCode.png";  
  193.         String encoderContent = "Hello 大大、小小,welcome to QRCode!" + "\nMyblog [ http://sjsky.iteye.com ]" + "\nEMail [ sjsky007@gmail.com ]";  
  194.         TwoDimensionCode handler = new TwoDimensionCode();  
  195.         handler.encoderQRCode(encoderContent, imgPath, "png");  
  196. //      try {  
  197. //          OutputStream output = new FileOutputStream(imgPath);  
  198. //          handler.encoderQRCode(content, output);  
  199. //      } catch (Exception e) {  
  200. //          e.printStackTrace();  
  201. //      }  
  202.         System.out.println("========encoder success");  
  203.           
  204.           
  205.         String decoderContent = handler.decoderQRCode(imgPath);  
  206.         System.out.println("解析結果如下:");  
  207.         System.out.println(decoderContent);  
  208.         System.out.println("========decoder success!!!");  
  209.     }  
  210. }  

TwoDimensionCodeImage 類:二維碼圖片對象

[java]  view plain  copy
 print ?
  1. package qrcode;  
  2.   
  3. import java.awt.image.BufferedImage;  
  4.   
  5. import jp.sourceforge.qrcode.data.QRCodeImage;  
  6.   
  7. public class TwoDimensionCodeImage implements QRCodeImage {  
  8.   
  9.     BufferedImage bufImg;  
  10.       
  11.     public TwoDimensionCodeImage(BufferedImage bufImg) {  
  12.         this.bufImg = bufImg;  
  13.     }  
  14.       
  15.     @Override  
  16.     public int getHeight() {  
  17.         return bufImg.getHeight();  
  18.     }  
  19.   
  20.     @Override  
  21.     public int getPixel(int x, int y) {  
  22.         return bufImg.getRGB(x, y);  
  23.     }  
  24.   
  25.     @Override  
  26.     public int getWidth() {  
  27.         return bufImg.getWidth();  
  28.     }  
  29.   
  30. }  


 

二、.NET

所需dll:ThoughtWorks.QRCode

http://download.csdn.net/detail/songylwq/5115596

這里代碼思路跟上述java大同小異,這里就不給出源碼了,可參見http://download.csdn.net/detail/songylwq/5115107

 

上面的java代碼,筆者已經進行了簡單的封裝,方便大家適用不同場合,希望對大家能有所幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM