第4章 QRコードを読む

76回生 watcol

4.1 はじめに

はじめまして。76回生(高1)のwatcolです。初めて部誌を書きます。拙い文章ですが、最後まで目を通していただけると幸いです。それでは、早速本題に移りましょう。

4.2 QRコードとは

さて、みなさんは下のようなものを見たことがあるでしょうか。

QRコード

図4.1: QRコード

近年見かけることの多い(この文化祭の会場でもよく見かけますね)この形、ご存知の方も多いと思いますが、QRコードと呼ばれるものです。

ここにURLなどの情報を入れておくと、読み取った時にWebサイトへアクセスできたり、LINEなどでは友達が追加できたりもします。(ちなみに、上のQRコードを読み取るとNPCAの公式Webサイト*1にアクセスできます。)

このQRコードは「マトリックス型2次元コード」と呼ばれるものの一つで、一種のバーコードのようなものです。しかし、一般的なバーコードに比べて多くの情報を詰め込める*2ほか、汚れや破損に強く、少しなら壊れていても読み取ることができます(経験がある方もいるのではないでしょうか)。

[*2] 数字のみだと7,089文字、英語だと4,926文字、日本語でも1,817文字詰め込めます。

4.3 QRコードを読む

しかし、このQRコードには一つ致命的な欠点があります。それは人間には理解できないということです。これでは手元に携帯やスマートフォンがなければ何が書いてあるか全くわかりません。皆さんも読めるようになりたいですよね??

そこで、この記事では、QRコードの読み方について紹介していこうと思います。

4.4 データの解釈の仕方

QRコードでは、2進数の1を黒いセル(1つ1つのブロックのこと)に、0を白いセルに置き換え、決まった順番で並べることによってデータを数値の列として表現しています。なので、どんなデータがどのように並べられているか、そしてそれをどのように解釈すればいいのかがわかれば、(理論上は)我々人間にもQRコードの内容を理解することができます。ということで、まずはQRコードの大まかな構造を見てみましょう。

4.5 QRコードの構造

QRコードの構造

図4.2: QRコードの構造

図4.2を見てください。これは、先程のQRコードをデータの役割ごとに色付けしたものです。早速、それぞれの部分について見ていきましょう。

形が決まっている部分(水色)

定常部の詳解図

図4.3: 定常部の詳解図

どのQRコードでも同じ形をしている部分です。次のように分類されます。

①余白領域
「ただの余白??」思われるかもしれませんが、たかが余白、されど余白。この部分がないと正しく読み取ることができません。公式の仕様では上下左右共に最低で4セル分必要とされています。
②切り出しシンボル
おなじみの形ですね。機械はこれを見てQRコードを認識しています。ファインダーパターンとも呼ばれます。注意しなければいけないのは、周りの白い部分を含めて切り出しシンボルだということです。
③アライメントパターン
皆さんは②の形が右下にだけ無いのを不思議に思ったことはありませんか?これは機械的に向きを判定できるようにする機構です。機械でこの形が右下に来るようにQRコードを回転させればいいので、私たちは逆さのままQRコードを読み取ることができるというわけです。
④タイミングパターン
すべてのQRコードにこの形があることを知っていた方は少ないのではないのでしょうか?これはQRコードが歪んでいるときやセルの位置がずれているときに、読み取るデータがそのままずれてしまうのを防いでいます。
⑤ダークモジュール
忘れられがちですが、このセルは必ず黒になっています。後述のフォーマット情報領域でセル数を調整するためにあります。

フォーマット情報領域(赤色)

フォーマット情報領域は、そのQRコードについての情報を表す部分です。この情報によって後述するデータ本体の読み方が変わるため、注意して読む必要があります。

フォーマット情報領域の読み方

図4.4: フォーマット情報領域の読み方

さて、この領域の読み方ですが、まず上の図のような順番で黒を1、白を0として1セルずつ読んでいきます。このとき①と②では同じ情報になっているはずです。これはどちらかがかくれていたり、汚れていたりする場合でも読み取れるようにするための工夫なのですが、今回はどちらかを無視してもらって構いません。

その後、このデータ(上のQRコードでは111110110101010になります。)をそれぞれの桁について101010000010010と比べて、次のようなルールで処理します。(このような処理をXOR演算といって、競技プログラミングなどでよくつかわれます。)

  • それぞれの数が同じならば、その桁を0とします。
  • それぞれの数が異なる、その桁を1とします。

この例では次のようになります。

$$ \begin{array}{rr} & \texttt{111110110101010} \\ & \texttt{101010000010010} \\ \hline & \texttt{010100110111000} \\ \end{array}$$

010100110111000となりましたね。このうち最初の2桁は「誤り訂正レベル」、その次の3桁は「マスクパターン」と呼ばれる情報です。残りの10桁は「誤り訂正符号」と呼ばれる、読み取りが間違っていないか調べるためだけの情報なので無視して構いません。では、この2つの情報について詳しく見ていきましょう。

誤り訂正レベル

QRコードがどのくらいの汚れを許容するかという情報が入っていて、低い方から「L」(7%の誤りを許容)、「M」(15%の誤りを許容)、「Q」(25%の誤りを許容)、「H」(30%の誤りを許容)です(ここでのパーセンテージは後述するデータ領域を100としたときのもの)。上で求めた数との対応は以下の通りです。

表4.1: 誤り訂正レベル

コード誤り訂正レベル
01L
00M
11Q
10H

誤り訂正レベルが高くなるほど汚れには強くなりますが、その代わりにQRコードが大きくなったり、読み取りに時間がかかったりするので、シチュエーションに合ったものを選ぶ必要があります。

ここまで言っておいてなんですが、正直誤り訂正レベルはQRコードを読むときには関係無いので、今回は無視してもらって構いません。

マスクパターン

QRコードでは、コード内の白と黒のバランスを良くしたり、コード内に誤ってファインダーパターンが出て来たりしないようにするために、後述のデータ領域を加工するのですが、そのために使われるのがこのマスクパターンです。上で求めた数との対応は以下の通りです。

表4.2: マスクパターン

コードマスクパターン
000\( (i+j) \bmod 2 = 0 \)
001\( i \bmod 2 = 0 \)
010\( j \bmod 3 = 0 \)
011\( (i+j) \bmod 3 = 0 \)
100\( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)
101\( ij \bmod 2 + ij \bmod 3 = 0 \)
110\( (ij \bmod 3 + ij) \bmod 2 = 0 \)
111\( (ij \bmod 3 + i + j) \bmod 2 = 0 \)

ここで、\( i \)は一番上の行を0としたときの行番号、\( j \)は一番左の列を0としたときの行番号です。この数式が成り立つようなセルを反転(白を黒に、黒を白にする)して読む必要があります。

数式を見ても分かりにくいと思うので、図にしてみました。

<span class="equation">\( (i+j) \bmod 2 = 0 \)</span>

図4.5: \( (i+j) \bmod 2 = 0 \)

<span class="equation">\( i \bmod 2 = 0 \)</span>

図4.6: \( i \bmod 2 = 0 \)

<span class="equation">\( j \bmod 3 = 0 \)</span>

図4.7: \( j \bmod 3 = 0 \)

<span class="equation">\( (i+j) \bmod 3 = 0 \)</span>

図4.8: \( (i+j) \bmod 3 = 0 \)

<span class="equation">\( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)</span>

図4.9: \( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)

<span class="equation">\( ij \bmod 2 + ij \bmod 3 = 0 \)</span>

図4.10: \( ij \bmod 2 + ij \bmod 3 = 0 \)

<span class="equation">\( (ij \bmod 3 + ij) \bmod 2 = 0 \)</span>

図4.11: \( (ij \bmod 3 + ij) \bmod 2 = 0 \)

<span class="equation">\( (ij \bmod 3 + i + j) \bmod 2 = 0 \)</span>

図4.12: \( (ij \bmod 3 + i + j) \bmod 2 = 0 \)

この図のピンクの部分を白黒反転させることになります。

早見表

これまでの作業、正直面倒ですよね。

ということでフォーマット情報から誤り訂正符号、マスクパターンが一瞬で分かる表を作りました。

表4.3: 早見表

フォーマット情報誤り訂正符号マスクパターン
111011111000100L\( (i+j) \bmod 2 = 0 \)
111001011110011L\( i \bmod 2 = 0 \)
111110110101010L\( j \bmod 3 = 0 \)
111100010011101L\( (i+j) \bmod 3 = 0 \)
110011000101111L\( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)
110001100011000L\( ij \bmod 2 + ij \bmod 3 = 0 \)
110110001000001L\( (ij \bmod 3 + ij) \bmod 2 = 0 \)
110100101110110L\( (ij \bmod 3 + i + j) \bmod 2 = 0 \)
101010000010010M\( (i+j) \bmod 2 = 0 \)
101000100100101M\( i \bmod 2 = 0 \)
101111001111100M\( j \bmod 3 = 0 \)
101101101001011M\( (i+j) \bmod 3 = 0 \)
100010111111001M\( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)
100000011001110M\( ij \bmod 2 + ij \bmod 3 = 0 \)
100111110010111M\( (ij \bmod 3 + ij) \bmod 2 = 0 \)
100101010100000M\( (ij \bmod 3 + i + j) \bmod 2 = 0 \)
011010101011111Q\( (i+j) \bmod 2 = 0 \)
011000001101000Q\( i \bmod 2 = 0 \)
011111100110001Q\( j \bmod 3 = 0 \)
011101000000110Q\( (i+j) \bmod 3 = 0 \)
010010010110100Q\( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)
010000110000011Q\( ij \bmod 2 + ij \bmod 3 = 0 \)
010111011011010Q\( (ij \bmod 3 + ij) \bmod 2 = 0 \)
010101111101101Q\( (ij \bmod 3 + i + j) \bmod 2 = 0 \)
001011010001001H\( (i+j) \bmod 2 = 0 \)
001001110111110H\( i \bmod 2 = 0 \)
001110011100111H\( j \bmod 3 = 0 \)
001100111010000H\( (i+j) \bmod 3 = 0 \)
000011101100010H\( ((i \div 2) + (j \div 3)) \bmod 2 = 0 \)
000001001010101H\( ij \bmod 2 + ij \bmod 3 = 0 \)
000110100001100H\( (ij \bmod 3 + ij) \bmod 2 = 0 \)
000100000111011H\( (ij \bmod 3 + i + j) \bmod 2 = 0 \)

ご参考までに。

データ領域(無色)

ところで、上の例でのフォーマット情報領域は111110110101010でしたね。これを上の早見表に照し合わせると、マスクパターンは「\( j \bmod 3 = 0 \)」であることが分かります。ルールに従ってセルを反転させると以下のようになります。

マスク処理後

図4.13: マスク処理後

さらに、これを色分けすると以下のとおりです。

データ領域

図4.14: データ領域

この章(節?)では、これらの領域を解読していきます。

はじめに、このデータ領域の読み進め方ですが、以下のように右下から2列ずつ、ギザギザに読んでいきます。この場合も白は0、黒(と赤)は1に変換します。

データ領域の読み方

図4.15: データ領域の読み方

例えば図4.14の右下の黄緑の部分は「0100」となります。それではこのことを頭に入れつつ、データ領域を解読していきましょう。

①モード指示子

QRコードでは扱える文字数を少しでも増やすために扱うデータの種類(数字、英語、日本語など)に応じて符号化モードと呼ばれるものが存在します。このモード指示子はそのQRコードで使われる符号化モードを示すためのものです。

表4.4: モード指示子

モード指示子符号化モード扱える文字データ密度
0001数字モード数字(0~9)10セル/3文字
0010英数字モード数字(0~9)、大文字アルファベット(A-Z)、空白、$、%、*、+、-、.、/、:11セル/2文字
01008ビットモード全てのデータ8セル/1バイト
1000漢字モード漢字、ひらがな、カタカナ、全角英数字など13セル/1文字

②文字数指示子

後述するデータコード語の文字数(8ビットモードの場合はバイト数)を表すものです。この領域の大きさはQRコードの大きさや符号化モードによって変わり、その対応は以下の通りです。(QRコードの大きさは「バージョン」というもので表現され、例の場合はバージョン2です。)

表4.5: 文字数指示子の大きさ(セル数)

バージョン数字モード英数字モード8ビットモード漢字モード
1~910988
10~2612111610
27~4014131612

上の例ではバージョン2、8ビットモードなので8セル読み進めると、00010011で、10進数に変換すると19になります。なので、下のデータコード語は8セル×19バイトで152セルあることが分かります。

③データコード語

やっと本題です。ここからQRコードの中身(「https://・・・」といった情報)を読んでいきます。符号化モードによって読み方が大きく異なるので、それぞれ見ていきましょう。

数字モード

まず10セル読み進めて、10進数に直します。例えば0001111011の場合「123」です。このように、上で得た文字数と照し合わせながら、3文字ずつ読み進めていきます。最後に1文字余ったら4セル、2文字余ったら7セル読み進めて、10進数に直します。これを順番に繋ぎ合わせると、数字が出来上がります。

英数字モード

まず11セル読み進めて、10進数に直します。そして、これを45で割って、商、余りをそれぞれ以下の表を見て英数字に直します。

表4.6: 英数字モード

コード文字
00
11
99
10A
35Z
36空白
37$
38%
39*
40+
41-
42.
43/
44:

例えば10000011000の場合10進数に直すと1048で、45で割ると商が23、余りが13だから「ND」となります。このように2文字ずつ読み進め、1文字余ったら6セル読んで10進数に直し、上の表で英数字に直します。これを繋ぎ合わせると、英数字の文字列が出来上がります。

8ビットモード

このモードは様々な種類のデータを表現できるのですが、英数字の文字列として読むときは、8セルずつ読み進めて、10進数に直し、以下の表(Asciiコード表といいます。)を使って文字に変換します。

表4.7: アスキーコード表

コード文字
32空白
33!
34"
35#
36$
37%
38&
39'
40(
41)
42*
43+
44,
45-
46.
47/
480
491
579
58:
59;
60<
61=
62>
63?
64@
65A
90Z
91[
92\
93]
94^
95_
96`
97a
122Z
123{
124|
125}
126~

これを繋ぎ合わせると、文字列ができます。コードがこの表に無い場合、諦めて別の方法で読む必要があります。(ここでは解説しません。)

漢字モード

まず13セル読み進め、16進数(123456789abcdefで表す)に直します。例えば0000100100000なら120になります。そしてこれをc0で割った商について、001eなら81を、1f2aならc1を足します。(例の場合は120÷c0=01だから01+81=82)また、余りについては40を足します。(例だと120%c0=60だから60+40=a0)そしてこれらをつなげたもの(82a0)をこちらの表を使って文字に変換します。(例だと「あ」)このように13セルずつ読んでいき、一文字ずつ繋げると文字列が出来上がります。

図にすると

こんな感じ

QRコードを読む

図4.16: QRコードを読む

ということでこのQRコードの中身は「https://www.npca.jp」(NPCAの公式サイト)であることが分かりました。ここからはおまけです。

④終端パターン・埋め草ビット

白を最低4つ、残りのセル数が8で割り切れるようになるまで続けます。入っているデータがギリギリの場合は省略できます。ちなみに「埋め草」とは元々穴を埋めるために使った草や雑木のことで、そこから転じて新聞や雑誌ではスペースを埋めるための記事のことを「埋め草」と言ったりします。ここでは空いたスペースを埋めるデータ列のことを言います。(話題不足)

⑤埋め草コード語

11101100」、「00010001」が交互に並んでいるものです。なぜ「00000000」ではダメかというと白が極端に多くなるからです。ちなみに「埋め草」とは元々穴を埋めるために使った草や雑木のことで、そこから転じて新聞や雑誌ではスペースを埋めるための記事のことを「埋め草」と言ったりします。ここでは空いたスペースを埋めるデータ列のことを言います。(2回目)(ホントに話題がない)

⑥誤り訂正符号

QRコードに汚れなどがあったり、真ん中に変なロゴが入っていたり、単純に読み取りミスをしたりしたときなど、それを確認・修正できないと読み取り精度が格段に下がってしまいます。そこで、誤り訂正という読み取り時のミスを訂正するための技術があり、そのためのデータがここに入っています。QRコードではリード・ソロモン符号という技術が使われているのですが、僕には何のことかさっぱり分かりません。数学などに詳しいという方は調べてみてください。そして僕に教えてください。

4.6 まとめ

これで長い長い解説は終わりです。とはいえ、読み方自体の説明はごく少量でしたね。ほんまか?

ということで、これからはQRコードを最速で読む方法を解説します。

  1. まずフォーマット情報の3〜5番目のセルを読みます。
  2. 「早見表」で対応する3〜5番目の数字を見つけます(これがマスクパターンになります)
  3. (以下、色の反転を意識しつつ、右下から)モード指示子を読みます。(「①モード指示子」参照)
  4. 今読んでいるQRコードのバージョン、符号化モードを踏まえて、文字数指示子の長さを確認して、文字数を把握します。(「②文字数指示子」参照)
  5. データコード語を4で得た文字数分読みます。(「③データコード語」参照)

ということで、暇なときに試してみてはいかがでしょうか。時間が溶けますよ。

4.7 おわりに

気付けばなかなかの大作になってしまいましたが、これでやっとおしまいです。軽い気持ちでページを開けて、後悔した方もいるのではないのでしょうか。ちなみに、この記事中に大量に存在する画像は「Krita」というソフトで作成しました。興味のある方は調べてみてください。非常にドット絵を作るのに向いています。(個人の感想)最後に、この記事を最後まで読んでくださった奇特な方、本当にありがとうございました。

4.8 参考