marich1224 のメモ

上限 140 文字の SNS ばっかりやってたら長文書けなくなったのでリハビリします.

真心のこもった手づくりQRコードの作り方(本編)

はじめに

みなさん、こんにちは。

あのブログから今日でちょうど1年が経ちました。

marich1224.hatenablog.com

簡単に前回のブログについてまとめると、

  • 僕の推し、夏川椎菜さんのライブツアーで、「QRコード*1」が舞台上の画面に出現する演出がありました。
  • 上演中はスマホ禁止だったので、直接スマホをかざして読み取ることはできませんでした。
  • そこで、何人かのオタクと協力して「QRコードのメモ」を取ることにし、最終的に解読に成功しました。

という話でした。このブログはたくさんの反響を頂き、とても嬉しいです!

さて、前回は「夏川さんからQRコードを受け取った」ことになりますが、受け取ったものは返したいですよね??

返報性の原理には逆らえないので、今回は「手づくりのQRコード」を夏川さんに捧げたいと思います!

 

早速始めていきます。

 

まずはどんなメッセージを贈るかを決めましょう。「ファンレター」のつもりで考えているので、僕が夏川さんのことを大好きなことと、これからも応援を続けたい思いを詰めて、

 

「夏川さん大好きです!ずっと応援します!」

 

にしようと思います。

夏川さんも先日のブログで、ファンレターは嬉しいものだというお話をしてくださいました。きっと喜んでくれると思います。

ameblo.jp

メッセージを決めたら、あとはQRコードを作るだけです。ネットで調べればジェネレーターが出てきますので、それで生成したQRコードを描き写せば完成します。

 

今回作った手づくりQRコードは次のようになりました。

完成した手づくりQRコード

完成です!

 

 

いかがでしたか?

 

 

真心を感じられましたか?

 

 

確かに手描きの温かさはあるかもしれませんが、さっき説明したやり方だと、QRコードを作ったのは「ジェネレーター」ですよね?

ジェネレーターで生成したQRコードを描き写すやり方って、僕にとってはChatGPTのような生成AIで作った文章を便箋に書き写しているのと大差ないように思うんです。

いちばん大変な作業を機械に任せておいて「真心」などとは笑止千万ですよね。

 

 

そこで今回は、真心を最大限に込めるべく、

すべて手作業で、JIS規格票に書かれた情報だけを基に、QRコードを作る

のをやっていきたいと思います!!

 

準備

ここからは句読点をピリオドとコンマにします.筆者的にはやはりこっちのほうがしっくりきますね.あと一人称も「筆者」にします.「僕」とかしっくりこないんですよね.

さて,本性を現したところで,この記事のブログの目次を明かします.

 

 

では,やっていきましょう!

JIS規格票

まずはQRコードの規格票を入手するところから始まります.QRコードの規格票は「JIS X0510」等でググれば発見することができます.

kikakurui.com

 

筆者がこの記事を書こうと思い立ったとき(1年前)は,図表が省略されたサイトしか閲覧できませんでした.ブログを書くに当たって再度調べてみると,図表込みで閲覧可能になっていました.いい時代になりましたね.

ちなみにその他のJIS規格票も,会員登録することで,以下のサイトから閲覧できます.

www.jisc.go.jp

 

このJIS規格票の閲覧サイトですが,1つ目に紹介したサイトに図表が載っていなかったときに参照していました.しかし,閲覧したときはそんなにUIが整っているわけではなく,個人的に見やすいとは思えなかったため――

購入した「JIS X 0510:2018」規格票の書籍.

書籍を買ってしまいました.

以下のページから購入できます.

webdesk.jsa.or.jp

 

買った当時(2022年11月)では本体価格5400円でした.

(4+1)! * (7 - 4*1) + 7! 円ですね.

個人が買うことをあまり想定されてない価格設定でした.

 

さて,この規格票を基にQRコードを作っていきますが,もうひとつ必要な規格票があります.

それは「JIS X0208: 7ビット及び8ビットの2バイト情報交換用符号化漢字集合」です.いわゆるシフトJISに関する規格票ですね.

QRコードの規格票では「シフト符号化表現」と呼んでいます.

これはメッセージ情報(漢字・ひらがな・記号など)をビット列に変換するために使います.

 

以上で必要な規格票は揃いました.

リード・ソロモン符号の計算のための変換表

リード・ソロモン符号とは,QRコードで使われる「誤り訂正符号」のことです.この符号のおかげで,QRコードは汚れに対してロバストになります.

この符号の計算には,「(多項式)基底表示」と「指数表示」の相互変換表が必要なのですが,規格票には計算方法のみの記載で,具体的な対応表が載っていませんでした.

 

そこで,この変換表の準備から始めなければならないのですが,この準備の経緯も書くと相当な分量になってしまうので,別記事に分けることにしました.

この記事では,準備が完了したというところから話を始めようと思います.

(「準備編」は後日公開の予定です.)

 

当然この表も手作業で用意しましたが,これは一度作ってしまえばずっと使える表ですので,「手作業でQRコードを作る」だけならこの手順は省略できます.

この記事の作成にあたって,後ほどPCで生成した表を共有しますので,皆さんはこの表をお使いください.

 

ちなみに,「準備編」で作った相互変換表は以下です.

「指数表示」から「基底表示」への変換表.後ほど使います.

「基底表示」から「指数表示」への変換表.これも後ほど使います.

これを作るのにも長い話があったので,詳細は後日公開の準備編をお楽しみに.

 

QRコードを作る

準備が整ったら,いよいよ規格票に従ってQRコードを書き起こしていきます!

ここでQRコード完成までのロードマップを確認しておきましょう.

以下で示す手順は,お手元のJIS X 0510規格票の p.90 から始まる「附属書I(参考)シンボルの符号化例」に倣っています.

  1. データの符号化
    1. (漢字モードの場合)まずQRコードに入れる文字列データをシフトJISのバイト列に変換し,続いてQRコード用のビット列(13ビット長)に変換する.
    2. ヘッダ情報(モード指示子・文字数データ)に続いて,13桁のビット列を並べる.
    3. 並べ終わったら,最後に「終端パターン」と「埋め草ビット」を並べる.
    4. シンボルのデータコード語の容量いっぱいになるまで,「埋め草コード語」を埋めていく.
    5. 並べたビット列を8ビット(=1バイト)ずつに区切って,符号化は完了.
  2. 「誤り訂正コード語」(リード・ソロモン符号)を計算する
    1. 規格票本体 7.5.2. (p.43) から始まる方法に基づいて,誤り訂正コード語を計算する.(最大の関門)
  3. QRコードに描き起こす.
    1. 「機能パターン」(位置検出パターン,タイミングパターンなど)を配置する.
    2. 「マスク」を決めて,マスク情報を記入する.
    3. マスクでビット反転する箇所に注意しながら,塗り絵する.

1. の手順で「漢字モードの場合」と但し書きがありますが,これについて説明します.

QRコードの「モード」は,主に以下の4種類のモードがあります.

  1. 0〜9までの数字のみを扱う「数字モード」
  2. 英数字と9つの記号(半角スペース,$,%,*,+,−,.,/,:)を扱う「英数字モード」
  3. 8ビット文字*2をそのまま符号化する「8ビットモード」
  4. 漢字を効率よく符号化できる「漢字モード」

このあたりの内容については,規格票本体 p.22 の「表2−QRコードのモード指示子」に詳細があります.

今回は日本語の文章を想定しているので,「漢字モード」での手順を説明します.

 

それでは,データの符号化から始めて行きましょう.

 

データの符号化

シフトJISバイト列への変換

まずは文字列データをシフトJISのバイト列に変換します.これの変換にはシフトJISの規格票「JIS X0208」を使います.漢字の位置は「区点」で表現されているので,その位置を「本体006」から探し,続いてその区点を基にバイト列に変換していきます.

区点から2バイト符号に変換する計算式が与えられているのですが,大変ありがたいことに変換された値が規格票の「本体007(ひらがな・記号)」と「本体009(漢字)」に載っているので,それを利用します.

書き出すと次のようになります.

メッセージをシフトJISのバイト列に変換したもの.
QRコード用のビット列への変換

続いて,シフトJISバイト列に変換した値を,QRコード用のビット列(13ビット長)に変換します.

この部分を引用*3すると,

a) 8140HEX 〜 9FFCHEX までのシフト符号化表現値の文字

    1) シフト符号化表現値から 8140HEX を減算する。

    2) 得られた値の上位バイトに C0HEX を乗じる。

    3) 2) の結果に下位バイトを加算する。

    4) 結果を13ビット2進文字列に変換する。

「JIS X 0510:2018」規格票本体 7.4.6 (p.27) より引用

とあります.これに従って計算していきましょう.

 

ところで,こうした処理を噛ませることで2バイト長(=16ビット長)から13ビット長へ3ビットぶんの圧縮を試みているわけですが,情報を極力圧縮してQRコードに詰め込もうという思想が感じられて,個人的にとても好きです.

QRコードをただ作るだけなら「8ビットモード」だけでいいところを,数字モードとか英数字モードとかいろいろ分けてあるのも,使う文字を制限することで,可能な限り使うビット数を圧縮しようとする意図があります.規格票を読むと,設計者の想いが伝わってきて素晴らしいですね.正直ちょっと計算めんどくさいけど.

 

ここでは16進数の計算が絡んできますが,16進数の計算には慣れてないので計算ミスの可能性があります.そこで,頭を極力使わずに計算できるように工夫します.なぜなら頭を使うとミスるからです.

 

以下の話では,16進数や2進数の話がたくさん登場します.慣れていない方は,例えば次のサイトが力になってくれるでしょう.

www.infraexpert.com

 

まずは手順1「8140HEX を減算」するところからです.今回は 0x40 (=40HEX. これ以降,引用以外は頭に「0x」をつけることで16進数を表します) の引き算によって,繰り下がりが起きることはなさそうなので,

  • 上2桁から 0x81 を引き算する.
  • 下から2桁目から 0x4 を引き算する.

の2段階に分けて計算します.

これらの引き算の計算は,予め「0x81 の足し算の表」と「0x4 の足し算の表」を用意して,そこから「探して写す」ことで行ないました.次の図で計算方法と計算結果を示します.

0x8140 の引き算の手順.右側の写真はすべて計算し終わったあとの結果.

計算例として,「川」のシフトJISバイト列 0x90EC から 0x8140 を引いてみましょう.

まず,上2桁(= 0x90) から 0x81 を引き算します.0x81 の足し算表から,結果が 0x90 になるものを探すと,「0x81 + 0x0F = 0x90」を見つけます.これから,「0x90 - 0x81 = 0x0F」が得られました.

続いて,下から2桁目(= 0xE) から 0x4 を引き算します.0x4 の足し算表から,結果が 0xE になるものを探すと,「0x4 + 0xA = 0xE」を見つけます.これから,「0xE - 0x4 = 0xA」が得られました.

下1桁は0の引き算なので,結果は変わりません.なのでそのまま元々あった「0xC」を使います.

以上から,「0x90EC - 0x8140 = 0x0FAC」と計算できました.

同様の計算をすべてのバイト列について行ないます.

これで手順1「8140HEX を減算」が完了しました.

 

続いての手順2〜4は一気にやってしまいます.

まずは,手順2「得られた値の上位バイトに C0HEX を乗じる」です.足し算ですら不安だったというのに,掛け算なんて死では??という感じですが,ここで一旦2進数を経由することを考えます.

なぜなら2進数の掛け算はめっちゃ簡単だからです.

普通の数(10進数)でも,「10000倍」のような「最初が1で残りは0」という形の掛け算は,桁をズラせばいいだけ(たとえば 417×10000 = 4170000)なので,めっちゃ簡単ですよね.

2進数ではそもそも各桁に「1」と「0」しか現れないので,掛け算は「桁をズラしたものの足し算」だけで計算できてしまいます*4

このことを使って,「0xC0 の掛け算」について考えてみましょう.まずは16進数を2進数に変換する作業からですが,ここでも頭を極力使わないために,

  • 0000 --> 0
  • 0001 --> 1
  • 0010 --> 2
  • ...
  • 1010 --> A
  • 1011 --> B
  • 1100 --> C
  • 1101 --> D
  • 1110 --> E
  • 1111 --> F

のように,ただ1桁の16進数の2進表記を書き出しただけの表を,手元に用意しておきます.

この表を使うと,「0xC0」は2進表記で「0b11000000」であるとわかります.頭の「0b」は2進数であることを示す記号です.ここで,

0b11000000 = 0b10000000 + 0b01000000

と分解してみます.すると,最初が1で残りは0という「掛け算が簡単な形」の足し算の形になります.これを使って 0xC0 倍の計算をしてみましょう.

 

ここからは,先ほど引き算を計算した「川」の例にとって説明します.

手順1では,「川」に関して「0x90EC - 0x8140 = 0x0FAC」と計算できましたが,手順通りに,「得られた値」(=0x0FAC) の「上位バイト」(= 0x0F) に「C0HEX を乗じ」てみましょう.

「0x0F」の2進表記は,先ほど書き出した16進数と2進数の相互変換表を使うと,「0b1111」とわかります.

まずは「0b1111」を「0b10000000」倍します.見た目はイカついですが,ただ「0b1111」をゴッソリ7桁ぶん左にズラすだけです.同様にして「0b01000000」倍,つまり「0b1111」をゴッソリ6桁ぶん左にズラします.こうしてズラしたもの同士を「足し算」すれば完成です.

上で言ったことを筆算の形で書いたのが次の図です.

0b1111 × 0b11000000 (= 0x0F × 0xC0) の計算.桁をズラした結果を足し算するだけで済む.

言葉だけの説明よりこちらのほうがわかりやすいですね.

 

「足し算」についてですが.基本的には普通の「足し算の筆算」の要領で計算していけばよいです……が,「2進数の足し算では,1+1 は繰り上がって 10 になる」ことに注意しましょう!

繰り上がりさえ気をつければあとは簡単に計算できます.結果は「0b101101000000」と求まりました.

この結果を16進数に直してもいいのですが,結局このあと2進表記に戻すことになるので,このままで大丈夫です.以上で手順2が完了しました.

 

続いては,手順3「2) の結果に下位バイトを加算する」パートです.

これも先ほどの「川」の例を使いましょう.今のところ

  • 手順1: 0x90EC - 0x8140 = 0x0FAC
  • 手順2: 0x0F × 0xC0 = 0b101101000000

まで来ましたが,次は「下位バイト」(=0xAC) を加算するところです.

まずはこれを2進数に直すところからですが,先の対応表によれば「0xA = 0b1010」「0xC = 0b1100」なので,これをそのまま並べれば「0xAC = 0b10101100」と,簡単にわかります.

あとはこれを単純に足し合わせればいいだけです.繰り上がりに注意して足すと,

  1 0 1 1 0 1 0 0 0 0 0 0 ← 0x0F × 0xC0
+)         1 0 1 0 1 1 0 0 ← 0xAC
  1 0 1 1 1 1 1 0 1 1 0 0  

と求まります.これで手順3が完了しました.

  • 手順3: (0x0F × 0xC0) + 0xAC = 0b101111101100

最後に仕上げとして,手順4「結果を13ビット2進文字列に変換」します.いま求まった値は12ビット長なので,最初に0を付け足して13ビット長にします.

  • 手順4: "01011 11101 100"

これで完成です!

 

さて,ここまで一気に説明してきた手順2〜4を,他の文字に関しても遂行していくわけですが,もっと機械的に行なえるように「筆算法」を考えました.

0x8140 の引き算した結果から,QRコード用の13桁のビット列を計算する筆算法.この図でも「0x0FAC」に関して処理を行なった例を示した.

この筆算の原理は,これまで説明してきた内容そのものです.原理を忘れて機械的に計算できるようにしておくことで,考えることを減らし,判断疲れからのミスを減らす目論見です.

 

では,この筆算法を使って,残りの文字についてもQRコード用のビット列に変換していきましょう.

筆算によって各文字情報をQRコード用の13ビット列に変換した結果.縦線は8ビット目の印のつもりであるが,筆算の試行錯誤の結果生まれたもので,本質的ではない.

できました!結構長かったですね.以上で,文字情報をQRコード用のビット列に変換する作業は終了です.

 

データコード語の作成

ここからは,本セクションの始めに示したロードマップの 1.2. 〜 1.5. を処理します.つまり,

  1. ヘッダ情報(モード指示子・文字数指示子)に続いて,13桁のビット列を並べる.
  2. 並べ終わったら,最後に「終端パターン」と「埋め草ビット」を並べる.
  3. シンボルのデータコード語の容量いっぱいになるまで,「埋め草コード語」を埋めていく.
  4. 並べたビット列を8ビット(=1バイト)ずつに区切って,符号化は完了.

の残り4つの手順を終わらせていきます.

とは言っても,ここの部分は,ほぼ計算処理はなくて,基本的に仕様に沿って0と1を並べていくだけの作業になります.サクッと終わらせてしまいましょう.

ここらへんの話は,前回のQRコード解読ブログで詳しく説明しましたので,そちらも併せて御覧ください.

marich1224.hatenablog.com

 

ここで,まずはQRコードのサイズを決定します.そのために,符号化する文の「文字数」を数えます.

今は「漢字モード」なので,単純に記録する文字列の文字数を数えればよいです.今回は「夏川さん大好きです!ずっと応援します!」を符号化するので,この文字数を数えます.すると「19文字」になります.

続いては,漢字モードで「19文字」の情報を含むことのできるQRコードのサイズを探します.規格票本体 表7 (p.31) を眺めると,

  • 型番(QRコードの大きさ): 2
  • 誤り訂正レベル: L

QRコードのデータ容量が「20」であることがわかります.これは19文字の情報を含むことができるので,今回はこの型のQRコードを使います.以降では,このQRコードを「2-L型のQRコード」と呼ぶことがあります.

 

さて,準備が整ったので,QRコードのデータコード部分を作っていきます.データコード部分の構造は,

「モード指示子」+「文字数指示子」+「文字列から変換したビット列」+「終端パターン」+「埋め草ビット」+「埋め草コード語」

となっています.順を追って説明します.

 

はじめに「モード指示子」に関しては,規格票本体 「表2−QRコードのモード指示子」(p.22)に記載されています.今回は「漢字モード」を使用しましたが,対応するモード指示子は「1000」です.

続いて「文字数指示子」です.先ほど数えたように,文字数は「19」です.これを2進数に変換した「00010011」を使って文字数指示子を作ります.規格票本体 表3 (p.21) には,文字数指示子のビット数が記載されており,今回の「型番2」で「漢字モード」の場合は「8ビット」で記録するように指示されています.したがって,今回の文字数指示子は「00010011」となります.

「文字列から変換したビット列」の部分は,先ほど頑張って計算した13ビット長のQRコード用ビット列を並べていけば良いです.

「終端パターン」は「0000」固定です*5.ここまでが本体データですよ〜という指示のための,ある種の「ピリオド」みたいなものですね.

最後に「埋め草ビット」を入れます.これは「0」固定で,ここまで書いてきたビット数が8の倍数になるように調整のために入れられます.

 

以上の処理を施して得られたビット列は,以下の図に示すとおりです.

書き出したデータコード語(ただし「埋め草コード語」を除く).1バイト(=8ビット)おきに黄色マーカで区別している.

これでほぼほぼ完成ですが,この図では「埋め草コード語」を記入していません.このビット列に続いて,埋め草コード語として「11101100」と「00010001」を交互に,QRコードのデータ容量いっぱいになるまで埋めていけば完成です.これは,規格票本体 7.4.10 (p.30) に基づきます.

完成したビット列を,8ビットずつに分割して書いた結果が以下の図です.

完成したデータコード語.1バイト(=8ビット)おきに分割して書いている.各列の右にスペースがあるのは試行錯誤の形跡.

これでデータ部分の符号化は完了しました!!お疲れ様でした.

 

「誤り訂正コード語」(リード・ソロモン符号)の計算

続いて,誤り訂正コード語の計算です.

こいつがね〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜大変.

 

ここでは,最初の準備と称して作った謎の表が手に入ったという前提のもと,この表の使い方を説明するというテイで話を進めます.

なお,このあたりの話は,規格票本体 7.5.2「訂正コード語の生成」(p.43) に記載があります.

 

これから計算する内容

これから行なうことは,以下で示す「多項式の割り算」です.

数式でビビるかと思いますが,避けられないので一度書いてしまいましょう.

2つの多項式*6  d(x) および  g(x) を以下で定義します.

 

 d(x) = [10000001] \cdot x^{33} + [00110011] \cdot x^{32} + \dots + [00100000] \cdot x + [11101100]

 g(x) = x^{10} + \alpha^{251} x^9 + \alpha^{67} x^8 + \alpha^{46} x^7 + \alpha^{61} x^6 + \alpha^{118} x^5 + \alpha^{70} x^4 + \alpha^{64} x^3 + \alpha^{94} x^2 + \alpha^{32} x + \alpha^{45}

 

ここで, d(x) は先ほど作ったデータコード語を係数に持つ多項式 g(x) は誤り訂正コード語数が10の訂正符号の生成多項式です(規格票 附属書A (p.70) 参照).

また,式中の  [b_7 b_6 \dots b_0]

 b_7 \alpha^7 + b_6 \alpha^6 + \dots + b_0

の略記であり, \alpha は,

 \alpha^8 + \alpha^4 + \alpha^3 + \alpha^2 + 1 = 0

を満たす数です.

さらに,総コード語数  c=44,データコード語数  k=34 と置きます(規格票本体 表9 (p.30) の「2-L型」の場合を参照).

 

以上の設定で,多項式  x^{c-k} d(x) g(x) で割ったあまり,つまり

 e(x) = x^{c-k} d(x) \pmod{ g(x)}

を計算します.こうして求めた  e(x) の係数が,いま必要な誤り訂正コード語です.

式はイカついですが,ただ計算の手間が多いだけで,元気と狂気があれば誰でも計算できます.

 

ところで「多項式  x^{c-k} d(x) g(x) で割ったあまり」の部分ですが,現在の規格票「JIS X 0510:2018」の該当箇所(本体 7.5.2 注記 2 (p.43))には「誤植」があり,「 x^k d(x) g(x) で割る」と読める箇所があります.

今回の挑戦では「規格票に書いてある内容だけでQRコードを作る」という話ですので,ここが誤植なのかハッキリするために,日本規格協会に問い合わせました.すると巡り巡って経産省の担当者の方からお電話いただき,確かに誤植であると認めてくださいました.

今では「正誤表」が公開されています.なんだか貢献した感じがして嬉しいですね.

 

さて,規格票の「誤り訂正」も済んだところで,実際の計算を見てみましょう.

ですが,その前に,準備の節で用意した「変換表」について説明しておきます.

 

準備した「変換表」について

ここで,これまで説明してこなかった「最初の準備と称して作った謎の表」について説明します.流石にこれの説明は避けて通れないので,少々長くなりますがお付き合いください.

準備の節で既に示しましたが,手書きバージョンの表をここで再掲しましょう.

 

最初に「指数表示」→「基底表示」の変換表です.

再掲: 「指数表示」から「基底表示」への変換表.

これが意味するところを説明しましょう.

 

 \alpha^n \alpha^8 + \alpha^4 + \alpha^3 + \alpha^2 + 1 で(多項式の割り算の要領で)割ったあまりは,7次以下の式になります.「 \alpha^8 + \alpha^4 + \alpha^3 + \alpha^2 + 1 = 0」の条件があるので,結局あまりだけが残って,

 \alpha^n = b_7^{(n)} \alpha^7 + b_6^{(n)} \alpha^6 + \dots + b_0^{(n)} 

となります.

この観察から,係数だけわかれば  \alpha^n の計算結果を表現できそうです.そこで, \alpha^7, \dots , \alpha, 1 をいちいち書かずに,係数だけを取り出して

 \alpha^n = [b_7^{(n)} b_6^{(n)} b_5^{(n)} b_4^{(n)} b_3^{(n)} b_2^{(n)} b_1^{(n)} b_0^{(n)}]

と書くことにします.この表記法を「基底表示」と呼ぶことにします.

一方で, \alpha^n の指数部分  n を取り出したものを,「指数表示」と呼ぶことにします.

 

先ほど示した図の意味は,「指数表示  n」と「基底表示  [b_7^{(n)} b_6^{(n)} b_5^{(n)} b_4^{(n)} b_3^{(n)} b_2^{(n)} b_1^{(n)} b_0^{(n)}]」の対応表というわけです.

 

また, n は整数ですが,実は

 \alpha^{255} = \alpha^0

を満たし,それ以降はループするので, 0 \leq n \leq 254 を満たす整数だけを考えれば十分です.

 

次に,「指数表示」→「基底表示」の変換表です.

再掲: 「基底表示」から「指数表示」への変換表.

先ほどの「指数表示→基底表示」ができることに関しては,割と「当たり前」なんですが,ここからがすごいんです.

まず, b_i^{(n)} (i=0,...,7) は0か1のどちらかの値しか取りませんから,基底表示であり得る全部のパターンは

 [b_7 b_6 b_5 b_4 b_3 b_2 b_1 b_0] = [00000000], [00000001], [00000010], \dots, [11111111]

 2^8 = 256 通りがありえますが,実は  \alpha^n を「基底表示」した結果には,[00000000] 以外のすべてが現れます.この著しい特徴が,わざわざ  \alpha なんかを導入した大きな理由になります.

たとえば,実際に計算してみると,

  •  [00000001] = \alpha^{0}
  •  [00000010] = \alpha^{1}
  •  [00000011] = \alpha^{25}
  •  [00000100] = \alpha^{2}
  • ...
  •  [11111110] = \alpha^{88}
  •  [11111111] = \alpha^{175}

となります.不規則なように見えますが, [00000001], ..., [11111111] の 254 個のビット列と, \alpha^n の形 ( n 0 \leq n \leq 254 を満たす整数)が綺麗に 1対1に対応します*7

先ほどの「基底表示」から「指数表示」への変換表を見ると,「1つのマスに2つ数字が入っている」場合が無いことがわかりますが,それが今の説明の直感的理解になります.

ただし,[00000000] に関しては対応する  n が存在しないので,図ではスラッシュを入れてあります(これは普通の数での「ゼロで割る」操作ができないことに対応しています).

 

これ以降は,「データコード語のすべてのバイトは,高々7次の  \alpha多項式  b_7 \alpha^7 + b_6 \alpha^6 + \dots + b_0 の基底表示である」と思って計算していきます.これがリード・ソロモン符号のポイントですね.

 

計算の手順

まだまだ注意しておくことはありますが,筆者が編み出した筆算方法を最初に見せてから,原理などを説明しようと思います.

実際に計算したノートでは,この筆算法を確立するまでの試行錯誤の形跡によってかなりカオスになっており相当わかりにくいので,説明用に書き直したものを示します.

誤り訂正符号の計算のための筆算手順.実際の計算とは別に,説明のために書き直したもの.図中の「基」は「基底表示」,「指」は「指数表示」の略記.

以下で順に補足していきます.

  1. 計算は,データコードを頭から並べるところから始めます.今回使うQRコードのバージョンは「2-L型」で,誤り訂正コード語の長さは「10」になるので,それより1バイト長い「11項」ぶんを並べます.
  2. 次に,最高次数の係数を指数表示に変換します.これで「割られる多項式の最高次の係数は, \alpha^{112} である」とわかりました.
  3. 続いて,割る方の多項式である「生成多項式 g(x) = x^{10} + \alpha^{251} x^9 + \dots + \alpha^{45} の各係数の指数に,112 を足していきます.これはつまり, \alpha^{112} g(x) の各係数の指数」を計算していることに等しいです.普通の多項式の割り算でも,最高次の係数を合わせてから引き算をしていきますよね.ここでは「最高次の係数」をあわせる操作をしています.255 を超えたら 255 を引く操作は,それ以降は同じ数がループして現れるので,表に載っている範囲に収めるためです. 
  4. 今度は,先ほど計算した指数たちを,基底表示に変換していきます.これからの引き算の計算の準備です.
  5. それが終わったら,いよいよ引き算します.ここで要注意ですが,今度は各ビットごとに足し算引き算を計算します.先ほど文字データをシフト符号化した際は繰り上がりに気をつけていましたが,今回は「多項式の係数」を「バイト」と読み替えているので,計算方法が異なります.さらに,多項式の係数は mod 2 で考えるので, -1 \equiv 1 \pmod{2} です.つまり,足し算と引き算は同一ですので,ここは「各ビットについて足し算」すればよいです.計算する際はお気をつけください!!
  6. 5. に引き続き足し算の計算です.ここで計算ミスが無いかを確かめるために,「計算結果のバイト」と「指数表記の下側に書かれたバイト」をビットごとに足し算して,確かに「指数表記の上側に書かれたバイト」になるかどうか,逆算して確認しておきましょう.たとえば,「0011001111010000 = 11100011」と計算されたら,「11100011 11010000 = 00110011」となることを確認します.
  7. ここまで終わったら,データコード語の次のバイトを下ろします.下ろす桁がない場合は,00000000 を下ろします.これで一段目が完了です.
  8. 以上の計算は,計算の最高位の次数がデータコード語の最終バイトを下回ったら終了します(次の図を参照).

割り算の終了タイミング.これは実際の計算の終了部分.ピンクのマーカで示した部分が,最終的に得られた誤り訂正コード語.

また,手順3で「最上位バイトの指数を,生成多項式の各係数の指数に足し合わせる」計算がありますが,ここも逐一チェックしながら進めました.

生成多項式の係数の指数に,最上位バイトの指数を足し算する計算.最左列に最上位バイトの指数を書き,その右に生成多項式の係数の指数に足し算した結果を(暗算で)計算していく.そのチェックとして,今度は引き算を(筆算で)計算して,元通りになるか確認する.

たまにマイナス表記のものがありますが,これは「足してから255を引く」代わりに,「先に255を引いたものを足す」ことで,計算の手数を少なくするためです.

 

さて,この処理を繰り返して,筆算をひとつづきに並べたのが次の写真です.

誤り訂正コード語の計算のための筆算.この計算だけで実働で9時間はかかった.

以上で誤り訂正符号の計算はおしまいです.この計算だけで実働9時間,実際の日にちにして1週間ほどかかりました.長かった…….

 

QRコードに描き起こす

ここからいよいよQRコードに書き起こしていきますが,データの記録方法などは全て前回のブログで解説してしまったので,説明はそちらに全て譲ります.

以下では,実際にQRコードに書き起こしていった様子を,簡単な説明とともに示しておくに留めます.

QRコードへの描き起こす手順.

 

色鉛筆を使ってマスクが来る箇所をマーキングすることで,最後にマーカで色塗りする部分を機械的にできるようにしました.

マスクの選定ですが,本来なら「全パターンを試して,その中でもっとも黒と白がバラけているもの」を選ぶ手順があるのですが,流石に手数が多いので今回はやっていません.

代わりに今回は,生データの0と1を並べたものを「ジッと睨んで」,「同じパターンが固まりがちだから,網目状に並んだマスクを選ぼう」と決定しました.

 

以上でQRコードの生成手順の説明は終了です!

完成したQRコードを(スマホで)読んでみる

さて,長い旅を経て,完成したQRコードがこちらです!

完成したQRコード

では,早速(スマホで)読んでみましょう.

 

完成したQRコードを読み取った結果.

 

「夏川さを大好きです!ずっと応援します!」

 

 

……?

 

 

「夏川さを大好きです!ずっと応援します!」

 

 

さを……???

 

 

さを〜〜〜〜〜〜〜〜〜〜〜〜〜?!?!?!?!?!!!!??????????????????

 

 

さを……(*>△<)

 

おわりに

いかがだったでしょうか?いや〜よかったですね,ちゃんとQRコードが完成して!

手計算から始めてもちゃんとQRコードって描けるんですね!いい経験になりました〜〜〜.

 

それでは,また!

 

参考サイト等

ここで,参考にしたサイトや,関連する内容のサイトを紹介します.

 

まずこちらは「大石泉すき」と出力するQRコードフルスクラッチで作った先行例です.この記事を主に参考にしました.

qiita.com

 

続いて,狂った高校生による規格票準拠でQRコードを作る解説記事です.ものすごく詳細に書かれています.手描きや手計算ではないですが,大いに参考にしました.

sites.google.com

 

こちらは,QRコードの(部分的な)手描きを通して,QRコードの仕組みや工夫を体験しようという,教育的な試みの例です.

and-engineer.com

この記事の本文に,以下の記述があります.

QRコードの生成と読み取りは、実際にはかなり複雑で、すべてを手計算でやるのは難しい。特に訂正符号の計算は、電卓レベルを超えている。

正気を失えば訂正符号の計算も手計算できるので,なんだか狂ってきたなと思ったらチャレンジしてみましょう!今回のブログで紹介した筆算法を活用してください.

 

リードソロモン符号に関しては,次のサイトがとても参考になりました.

siglead.com

 

以上のサイトで知った情報は,すべて規格票に載っていることを確認した上で使用しています.また,載っていなかった情報に関しては,規格票に載っている情報から計算するなどして対応しました.

 

 

 

 

おまけ

……お目覚めのようですね.先ほどは悪い夢を見ましたね.

いや夢じゃないんですが.

 

これさ〜,QRコード完成してさ〜,「やった〜〜やっと完成したぞ〜〜!」って読み取ったらさ(スマホで),そしたら「さを」〜〜〜〜!!「さを」〜〜〜〜〜〜〜!!!!!!!

 

 

クショがよ.

 

 

筆者は「さを」を目撃した直後,現実が受け入れられず絶命しゾンビになりましたが,その後「さを……??さを……????」と言いながら,生前の記憶に導かれてデバッグを開始しました.

「これまでの10日間くらいの作業,全部水泡に帰したっぽいな」と気づいた瞬間,人はみなゾンビになると思います.

 

 

一旦状況を整理しましょう.

「ん」が1つ隣の「を」になったってことは,「ん」を担当するビットの末尾が1個ズレているということです.

その手がかりを基に,シフト符号化の規格書の誤植も視野に血眼で探しました(「JIS X 0510:2018」での前科がありますからね).

その結果,最後のリード・ソロモン符号の計算でミスを発見しました.

 

ミス発見.結構序盤にミスしていた.

誤り訂正コード語を計算するための割り算の部分ですね.

誤り訂正コード語に誤りが埋め込まれているという滑稽なことが起きていました.

 

いっちばん最初の「文字コードへの変換」の段階で間違っていたらどうしようかと思っていましたが,最後の最後で良かったです.

あと1週間かけて再計算すればいいので.

 

これまでの説明と全く同じなので,一気に計算します.

「計算をミスるまでの結果を使い回せないのか」とお思いかもしれませんが,実はほとんど使い物になりません.このミスった部分が割り算の最高位に来るまではミスが埋め込まれませんが,それ以降は全く異なる結果になってしまいます.

また,これは筆者のこだわりですが,「スマホを使ってミスを発見した」ことを利用して書き直すのは,当初の「JIS規格票記載の情報のみで描く」ことに反しそうな気がしたので,リード・ソロモン符号の計算部分はすべてやり直します.

 

誤り訂正符号の計算です.早速もう人生で2度目の計算です.

誤り訂正符号の計算(2度目).めちゃくちゃ慎重に進めて,実働10時間くらいになったが,6日で終わらせた.

 

続いて,QRコードへの書き起こしです.マスクはさっきと同じ「000」を使います.

QRコードに描き起こす(2度目).もう慣れたもんですわ.

マッキーで塗って,鉛筆や色鉛筆で書かれたビットなどを消しゴムで消したものが,冒頭で紹介した「手づくりQRコード」です.

ただの「手描きQRコード」ではありません,「手づくりQRコードです.

再掲: 完成した手づくりQRコード

 

これを読むと,筆者が「ファンレター」のつもりで考えて,筆者が夏川さんのことを大好きなことと,これからも応援を続けたい思いを詰め込んだメッセージが,ちゃんと出力されました.

手づくりQRコードを読み取った結果.

 

 

「夏川さん大好きです!ずっと応援します!」

 

 

 

 

*1:QRコード」はデンソーウェーブの登録商標です.

*2:Unicode (JIS X 0221) 等の使用が「推奨」されている(規格票本体 7.3.5 (p.19) 記載の注記いわく)が,どの文字コードを使うかは仕様上は決められておらず,(読み手と書き手の双方の合意が取れている限りで)自由に文字コードを指定してよいとのこと.

*3:JIS規格票を引用するには許諾が要るとのことなので,連絡して許諾を頂きました.引用・転載先も明記とのことだったので,このブログのURLも提示しました.恥ずかしかったです.

*4:こうしたビット列の「桁をズラす」操作のことを,「ビットシフト(シフト演算)」と言います.

*5:マイクロQRコードだと色々あるらしいですが,QRコードでは「0000」で固定です.

*6:有限体  \mathbb{F}_2 [X] / (X^8 + X^4 + X^3 + X^2 + 1) 係数の多項式です.本記事では  X^8 + X^4 + X^3 + X^2 + 1 の根  \alpha を添加した  \mathbb{F}_2 の拡大体で考えています.

*7:これを使うことで,掛け算・割り算を,指数部分の足し算・引き算で定義できるわけです.