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:これを使うことで,掛け算・割り算を,指数部分の足し算・引き算で定義できるわけです.

QRコードを解読した話【LAWSON presents 夏川椎菜 2nd Live Tour 2022 MAKEOVER 参加記】

注意

この記事では,LAWSON presents 夏川椎菜 2nd Live Tour 2022 MAKEOVER (以下,MAKEOVER) 公演中に登場した QR コード*1が何であったのかに関する重大なネタバレがあります.セトリのネタバレは(1曲を除いて)ありません.

発売した Blu-ray を見ながら自力解読したい方はご注意ください.

また,あまりにも時間が経ちすぎてしまったため,話の展開に合わせて一部脚色している箇所が(たぶん)あります.そちらもご容赦ください.

 

追記 (2022年11月14日 4:17):

夏川椎菜さんがこのブログをご覧になったようです.

 

夏川椎菜さんがこのブログをご覧になったようです.

 

推しに駄文が読まれてしまいましたね…….

事の顛末を簡潔に説明します.

  • 昨日,2022年11月13日 は 「MAKEOVER」Blu-ray発売記念のプレミア上映会でした.最高でした.
  • その中で「 #ナンスに質問 」をつけたツイートを拾って,舞台挨拶中に質問に答えてくれました.
  • この企画中に QR コードの話になりました.
  • 夏川さん「知ってんだからね!アレ解読した人いるんでしょ!!まさかメモって読む人が現れるとは……」

他に MAKEOVER の QR コード解読記事は意外にも見つからなかったので,十中八九この記事でしょう.発言を聞くに割とちゃんと読んでくれていたようでしたので,だいぶ恥ずかしかったです.夏川さん,読者の皆さん,お目汚し失礼致しました…….

 

推しに読んでもらえるような記事を書けるに至ったのは,たくさんの皆さんのご支援があってこそです.そこでこの追記では,巻き添えスペシャルサンクスの皆さんのツイートを引用していこうと思います!!

追記 (2022年11月12日 15:00):

想像を絶する数の人々にこの記事をご覧いただけてとても嬉しいと同時に,完全に身内向けだったライブ参加記の記事が,様々な人に読まれているという事実にとてもビビっております.

 

 

2022年11月第1週のはてなブログランキング13 (= -4 + 17) 位にランクインし,はてなブログ公式 Twitter でフィーチャーされる事態になってしまいました.

それはつまり,「夏川椎菜」がミュージックレイン所属の女性声優であり,あだ名が「ナンス」であり,黄色がイメージカラーであり,名前が「しいな」なので 417 がフィーチャーされがちなことなどを知らない方々も,この記事をご覧になっているということです.

そこで,説明が必要な箇所が増えたなと思ってきましたので, MAKEOVER Blu-ray 発売まであと417時間となったこのタイミングで,大幅な追記・修正を行なうことにしました.

 

 

こんばんは!

 

ついにこの時,「2022年11月1日 午前1時」を迎えました.

1 が沢山(-4+1+7 個)ありますね.出ている数字全部足すと 4-1+7 になりますね.

違います.そういうことじゃないです.

 

ちょうど今から数えて,

MAKEOVER Blu-ray 発売日(2022年11月30日)まであと 41700分

なんですよ!!!!!*2

この記念すべきときに,あの話をしようと思います.

 

 

 

はじめに

MAKEOVER は,2022年5月1日の埼玉公演から始まりました.

初日独特の「セトリも演出も全く予想がつかない」ことから生まれる緊張感が,会場全体に漂っていたことはよく覚えています.

 

連番者の方とセトリ予想をしているうちに会場が暗くなり,MAKEOVER が開幕しました──

 

 

──終わりました.楽しかったです.

 

通常なら上の2段の空白に相当する部分をブログに書くものなのでしょうが,本題はそこではありません.

「何だあの QR コードは」

これが開演中に思った感想のひとつでした.どこの場面で思ったのかはネタバレになるので言いません(この記事でネタバレするのは「QRコードが登場したこと」と「QRコードが何であったか」に関すること,それと中野初日の出来事だけです).

 

「あの QR コードには何が書かれているんだろうか」という疑問は当然ありましたが,そもそもなぜスマホ等を使用できない上演中に QR コードを表示させたのか,というのも疑問として残ります.

唯一考えられるのは「人力で読んでね(*>△<)」という夏川椎菜からの挑戦状という説です.

 

 

 

よっしゃああ!!!!!!!!!受けて立とうじゃねえか!!!!!!!!!!!

 

 

 

……とはいえ,一発で解読するのは流石に無理でしたので,一つの絵として見て「なんか下の方に横向いた猫(?)みたいなのいるな」とか,「右下に L が鏡映しされたようなのあったな」とか,己のパターン認識能力を駆使し,雰囲気だけ覚えて帰りました.

 

埼玉公演の3日後の 2022年5月4日 に大阪公演が開催されましたが,ここで前回パターン認識でなんとなく覚えていたものと比較すると,どうやら現れるコードはすべて同じであることがわかりました.以降はこの QR コード(「例の QR コード」とか呼んでいました)を解読することになります*3

 

何が書いてあったのか?大まかな予想

さて,MAKEOVER で登場した「QR コード」という概念ですが,これは何の脈絡もなく登場したわけではなく,夏川さんの楽曲『ハレノバテイクオーバー』の MV 内で既に使用されています.

 

youtu.be

 

これです.赤夏川さん太陽みたいで可愛いですね.

夏川さんに見惚れていると見落としますが,実はダンサーさんの背中に QR コードがくっついています.

これらを実際に読んでみると,「喜」「怒」「哀」「楽」の4文字になるようです.

MAKEOVER は,喜怒哀楽をコンセプトにしたアルバム『コンポジット』を引っ提げたライブでしたので,

 

「あの例の QR コードも『喜怒哀楽』とかなんじゃないか??」

 

という予想が立てられるわけです.

登場した QR コードのサイズ感的にも,そこまで長い文章を詰め込めないだろうと思うと,これは妥当な予想のように思えます.

また,あるいは「夏川椎菜」説も同様に可能性としてあります.

 

ところが,実際に QR コードを生成してみると,どちらの場合も人力パターン認識で覚えていたものとは全く異なる見かけをしていました.

思いつく単語などを取り敢えず突っ込んでいっても,それらしいものは見つからず,

 

「これは直接解読するほかないな……」

 

と思ってきました.

 

 

メモ取りの翁たち

今は今(?),メモ取りの翁といふ者ありけり.オタクにまじりてメモを取りつつ,よろづの事につかひけり.名をば,れす(@res_pyb)しろ(@nansuuuuu417) となむ言ひける.

──── 『メモ取り物語』より(『竹取物語』を一部改変)

これは有名な『メモ取り物語』の冒頭部ですが,ここで夏川さんにも存在を認知されている「メモ取りの翁」からお二人,れすさんとしろさんが解読に加わります.

今回の解読は,主にこのお二人のメモを元にして進められました.QR コード解読もよろづの事に当然含まれるようです.

 

Q: QR コードのメモを取るって,一体どうやって?

A: 見た通りに0と1を書いていきます.

 

とは言っても,隅々から目に入ったところを元にして書くのは,瞬間記憶か常人離れの速記能力がないと相当厳しそうですので,QR コードの仕様に基づいてメモを取っていくことにします.

追記 (2022年11月12日 15:00):

ところで筆者およびメモ取りの翁たちは全員このツアーを全通しています.息をするように全通していたので書いていませんでしたが,しれっと各公演の話が登場するのはそのせいです.

ちなみに,「LAWSON presents 夏川椎菜 2nd Live Tour 2022 MAKEOVER」の全日程は以下のとおりです.

  • 埼玉 さいたま市文化センター
    • 2022年5月1日(日)18:00開場/19:00開演
  • 大阪 オリックス劇場
    • 2022年5月4日(水・祝)17:00開場/18:00開演
  • 千葉 千葉県文化会館
    • 2022年5月20日(金)17:30開場/18:30開演
  • 群馬 高崎芸術劇場
    • 2022年5月29日(日)17:00開場/18:00開演
  • 東京 LINE CUBE SHIBUYA
    • 2022年6月5日(日)17:00開場/18:00開演
  • 愛知 日本特殊陶業市民会館フォレストホール
    • 2022年6月19日(日)17:00開場/18:00開演
  • 東京 中野サンプラザホール
    • 2022年6月28日(火)17:30開場/18:30開演
    • 2022年6月29日(水)16:30開場/17:30開演 <最終公演は生配信アリ>

QR コードの基本仕様と参考にしたサイトたち

ここで紹介するサイトたちは,とてもためになりました.この記事の参考文献として参照すると良いと思います.

QR コードの仕様を調べるにあたって,次のサイトが非常に参考になりました.

web.quizknock.com

流石 QuizKnock さん.大好きです*4.この記事も「QuizKnockと学ぼう」チャンネルの勉強 LIVE を使いながら書いています.

 

本家本元の仕様書は以下にあります.

kikakurui.com

こちらもとても参考にしました.やはり何事も原典に立ち返るのが良いですね.

もうちょっと分かりやすく書かれたものに,以下があります.

https://www.denso-si.jp/dictionary/dic_qr/GeneralDescriptionoftheQRCode.pdf

 

今回の記事では説明しませんが,QR コードの中には「誤り訂正符号」が詰め込まれています.

これについては,中日新聞痺れるほどカッコいい記事を書いていますので,ぜひそちらをご覧ください.

static.chunichi.co.jp

 

続いて,この記事の追記の際に参考にした記事も,以下で2つご紹介します.

次のサイトはかなり詳細に QR コードの仕様について解説されています.この方はなんと仕様に基づいて QR コードを自作しています*5

www.swetake.com

 

次に紹介する方は,QR コードのデータの記録法から誤り訂正符号の作り方から,さらに誤り訂正の方法まで,全部書いてます.多分これがQR コードの全部です.

ik1-316-18424.vs.sakura.ne.jp

 

 

さて,QR コードの仕様の中で,今回のメモ取りで使った特に重要なものを紹介しましょう.

  • QR コードは,右下から順に,2列ごとに記録されている
    • 右下から順に読んで行けば,はじめの数文字はわかるはず!
    • このことに関する詳しい解説は,先述した QuizKnock さんの記事にあるので,そちらをご覧ください.
  • QR コードは,生成したコード全体にマスクを掛けており,そのマスク情報は決まった場所に記録されている
    • マスク情報は解読に必須!ここもメモする.


特にマスク情報は重要です.マスクの働きを説明しようと思いますが,ここで QR コードの構造をより詳細に見てみます.

また,ここで例として取り上げた QR コードは今後の説明でも使っていきます.

QR コードの各部分の名称.類似の図は参考記事にもありますが,ここで改めて紹介します.これはクワイエットゾーンの制約をガン無視しているので上手く読めないかもしれません.そうなったら肉眼で読むしかねえな!!!!

上の各要素のうち「ファインダパターン」「アライメントパターン」「タイミングパターン」は予め決められており,位置合わせに使用されます.このパターンは読み取りに非常に重要ですから,似たようなパターンがデータ部分(残りの部分)にうっかり現れるとめちゃくちゃ困るわけです.そこで,出来上がった QR コードに適宜マスクを掛けて,こうしたパターンに似たものが現れないようにするという仕組みになっています.

また,「めっちゃ黒い QR コード」や「めっちゃスカスカな QR コード」も読み取りにくいので,いい塩梅にバラけてくれるようなマスクをいい感じに選ぶことで,読み取りやすいコードを生成します.

 

QR コードのマスクは 8 種類用意されており*6,それぞれに 000, 001, ..., 111 という,いわば「ID」が割り振られています.その 8 種類のうちどのマスクを使っているのかという情報は,上の図の赤い領域で示された部分に記されています.

マスクの一覧.000 から 111 までの8種類ある.黄色部分が「0」,紫色部分が「1」に相当する.こんな色味になっているのは,使っているプログラム(python の matplotlib)のデフォルトをそのまま使っているから MAKEOVERのイメージカラーに近づけたからです!(後付け)

先ほど説明した「なんとかパターン」の箇所以外に関しては,このようなマスクを掛けておくわけです.ちなみに,この図において「マスクを掛ける」というのは,「紫の部分は色を反転させ,黄色の部分は色を変えずそのままにする」ということと同じになります.

マスクについては,後ほどの解読フェイズで詳しく説明します.

 

得られたメモを元に解読! ── 「例の QR コード」解読キャス

さて,先ほど説明したような QR コードの仕様を利用する作戦で,夏川さんからの無理難題に挑むべく,メモ取りの翁たちとメモを取っていきました.

時は大阪公演(2022年5月4日)から流れて,愛知公演(2022年6月19日)の終演後.

最終日(2022年6月29日)には配信が行なわれることが発表されましたので,実質的なタイムリミットは中野追加公演初日(2022年6月28日)ということになります.

そこで,愛知公演と中野初日の間である 2022年6月25日 に,QRコード解読キャスを敢行しました(※現在は非公開).

 

追記 (2022年11月12日 15:00):

ここからは,実際にどのように解読が進んだのか,具体的な方法と経緯について,より詳しく書くことにしました.

長くなりますが,今後の参考になればと思います!!

マスク情報を読み取る

まずはマスク情報を読み取ります.例として,説明用の QR コードのマスク情報を以下で読み取ってみましょう!

フォーマット情報を読み取る.赤い矢印に沿って読み取っていくが,途中にある「タイミングパターン」は無視して飛ばして読む.

矢印に沿って読んでみると,110011000101111」という15桁の情報が得られます.しかし,このままではダメで, QR コードの仕様上,読み取った後に「101010000010010」でマスクするという作業が必要です.

 

せっかくなので,ここで「(ビット)マスク」についての一般論について復習しておきましょう.

とは言っても簡単で,元データとマスクを位ごとに見比べて,マスクが1の部分のゼロイチを反転させていくだけです.

「位ごとに足して,2になったら0にする」とか「同じところはゼロに,違うところはイチにする」と考えても良いでしょう.

マスクが 1 になっているところに下線を引いてみると,以下のようになります.

 

10001111 (元情報)

00000000 (マスク)

1000111 (マスク結果)

 

こういうマスク処理が解読では頻出しますので,後学のためにここで詳しく触れておきました.

さらに重要な性質として,マスクを掛けた後にもう一度同じマスクを掛けると,元通りになります.この性質はデータの解読のときに使います.

 

さて,ここで「うおおおおお 15 桁憶えるぞ!!!!!」と突っ走ってもいいのですが,フォーマット情報の仕様を使えば,もっと効率よくマスク情報を抜き出せます.

フォーマット情報には,以下の情報が詰まっています.

  • 初めの2桁には「誤り訂正レベル」の情報
    • 上の例だと「01」.これは誤り訂正レベル「L」に相当し,約7%の読み取りエラーまで訂正できるとされています.詳しい話は先ほど紹介した参考サイトたちをご参照ください.
  • 次の3桁には「マスクの種類」の情報
    • マスクは全部で 8 種類用意されています.
    • 上の例だと「100」.これを「例の QR コード」から読み取りたい!!
  • 残りの10桁には「フォーマット情報の誤り訂正符号」.
    • 上の例だと「1000111010」.実はこの部分は,初めの「01100」の部分がキチンと読めていれば要らない情報です.

いま自分たちがほしいのは「どのマスクを使っているか」なので,誤り訂正レベルとか誤り訂正符号とかは,究極的には要りません.

結局,マスク情報がほしいだけなら,「初めから3,4,5桁目」さえ読めていれば良いわけです.

マスク情報だけなら読み取るのは難しくありません.ただし,読み取ったあとにマスクを掛けるのを忘れずに!

読み取ったあとは「101010000010101」でマスクを掛ける必要があります.これを忘れるのは「肉眼で QR コードを読んだときあるある」ですね.

マスク情報に対応するマスクの箇所は「101」の部分ですので,上の例なら,「001」にマスク「101」を掛けて「100」であると,すぐにわかります.

(マスク情報を取るためにもマスクを掛ける必要があるのは若干混乱しますが,フォーマット情報のマスクは「101010000010101」で固定です)

 

さて,なぜここまで口酸っぱくして「マスク」の話をしたのかというと,どのマスクを掛けるかによって,出来上がる QR コードが全く変わってしまうからです.

例えば,次の QR コードはすべて同じデータが記録されたものですが,見た目が全く違っていますね*7

同じデータを違うマスクで出力した結果.見た目がすべて異なることがわかる.

というわけで,マスクがなにかを見逃すと解読ができなくなってしまうので,まずはマスクを読み取るところから始まります.

 

では,本題の「例の QR コード」でマスク部分読み取ってみると,「111」でした.何度見ても 111 でした.

これはマスクを掛ける前ですので,マスクを掛けると「010」のマスクであるとわかります.

「例の QR コード」で使われているマスクはこいつだ!!

ちなみに,この「010」のマスクの厳密な定義は,左上のセルを (0,0) 番地としたとき,(i, j) 番地のセルに関して「j mod 3 = 0 のとき 1」です.

ナンのこっちゃかと思いますが,これは

左から右へ 0,1,2,3... と数えていったときに,3の倍数の列にあるセルの白黒を反転させる

ようなマスクという意味です(ゼロから数え始めることに注意!!).これは後で使うので,その時にまた説明します.

 

データ部分を読み取る

さて,ここまででマスク情報がわかったので,メモの情報を元にいよいよ読み取って行きます!!

先述の仕様紹介のところで「QR コードは右下から順に記録されている」と言いましたが,

読み取り方の詳細は,以下のとおりです.

 

読み取るときのポイント.図では繰り返しになるところは省略しています.
基本は右下から上に登りながら,2つずつ「右→左→右→左→……」とジグザグに読んでいきます.ただし,「なんとかパターン」に出会ったらそこを無視します.

このように,2列ずつ右下から読んでいきます.これを踏まえて,いちばん右の2列を翁たちが「例の QR コード」を観察して銘々に記録してきたメモが以下です.

追記 (2022年11月14日 4:17):

以下に紹介するメモに関して.

冒頭で「夏川椎菜本人巡回」の件をお話しましたが,そこで夏川さんが

(*>△<) < なんかQRコードの配列をメモっていったらしいね.

と,メモを縦書きするジェスチャーと共に説明していました.普通メモのジェスチャーと言ったら流石に,少なくとも自分の常識では「横書き」のはずなので,

うわこの人,あのメモを見たんだ…….

と確信しました.ちゃんと中身読まれてると悟って急に恥ずかしくなりました.

 

ブログ書いてるオタクのみんな!!!!!読んでるって言ってないだけで推しにその記事読まれてるぞ!!!!!!!!!

メモA: れすさん(一部はしろさんの結果と統合したもの)のメモ解析結果

メモB: しろさんとどねさんのメモ解析結果(どねさんのやつで赤文字になっているところは,れすさんしろさんの結果を基に修正したもので,「*」になっているところは不明な箇所です)
隣に振ってある数字はマスクを掛けるときに使う「ゼロから数えて何列目か」の情報です.

 

微妙に似ている箇所はありますが,結構バラバラです.

一応この QR コードがライブ中で出現した場面を説明すると,めっちゃ激しい曲で,筆者はヘドバンの合間にメモっていました.暴れ回ってる隙間にメモ,なんなら暴れながらメモを取っているので,「まあ結構ズレるよね」という感じです.

 

ちなみに「メモ」の実物はこんな感じです(れすさんのツイート).

この方,初演の埼玉公演から QR コードのメモ取りをしていたので,「筋金入り」なんですよね.そのツイートがこちら(※演出のネタバレがあります).

 

さて,この曖昧なメモたちを読んでいくわけですが,その前にデータ部分の構造,つまりマスクした後に右下からジグザグに読んでいったデータ部分はどのようになっているかを,ここで説明しておきます.

詳しい話はこのサイトに載っていますので,もっと知りたいという方はこちらをご覧ください.

 

データ部分の構造,つまりマスクした後に,右下からジグザグに読んで得られるデータは,

「モード指示子」→「文字数指示子」→「データ」→「誤り訂正符号」→「終端パターン」→「埋め草ビット」→「埋め草ワード」

という構造になっており,矢印の順番に記述されています*8

この記事で扱うのは初めの3つ: 「モード指示子」→「文字数指示子」→「データ」だけです.

以下,これらを読み解いていく様子を紹介していきます.

 

モード指示子

まず注目するのは,初めの「モード指示子」です.これは 4 ビットの情報で,

  • 0001 = 「数字モード」
  • 0010 = 「英数字モード」
  • 0100 = 「8bit モード」
  • 1000 = 「漢字モード」

という仕様になっています.さて,まずはモード指示子を,メモから実際に読み取ってみましょう!

メモB の一部を抜粋し,そこからモード指示子を読み取っていく.マスクはさっき読み取ったものを使えばよい.

 

注意すべき点は,右下からの「ジグザグ」で読み取っていく前に,一度マスクを掛けて,元のデータを復元するところです.

マスクの説明のときにも触れましたが,掛かっているマスクを外すにはもう一度同じマスクを掛ければよいのでした.フォーマット情報から読み取ったマスク (今回は「010」でした) をもう一度掛けることで,仮面を剥がしているのです.

 

ここでは,(0番から数えて)24 番の列が右端に来ますので,先ほど読み取ったマスクを掛けると,右端の列の 0 と 1 がマスクで反転します.

 

いざモード指示子を読んでみると「0110」と読めるものと「0100」と読めるものの2つが得られますが,「0110」となるモード指示子は仕様にありません

以上から,モード指示子は「0100」,すなわち「8 bit モード」であると読み取れました!

 

 

「……ホントに??すごい読み違いしてて「0010」だったりしない???」と思うかもしれません.実際「ホントか……??」と疑いながら解読作業を進めていました

しかし一旦仮定して進めないと先に行かないので,ここでは「8 bit モード」と仮定して進めます.

このような,結構ふわっとした仮定を置いて解読を進めていたので,いつ仮定が崩壊するか分からない不安は常に付き纏っていましたね.

 

文字数指示子

さて,この調子で文字数指示子も読み取って行きましょう.やることは基本的には同じで,マスクを掛けて,ジグザグに読むのをやっていきます.

 

では,どこまで(何ビットぶん)読んでいけばいいのか?というと,「8 bit モード」の場合,「バージョン(型番)が 1〜9 のときは 8ビットバージョン(型番)が 10〜40 のときは 16ビット」ぶん読めばいいそうです.

www.tech-jp.com

ここに来て実は,今まで説明して来なかったバージョン情報というものが関わってきます.

ここでの「バージョン」とは, QR コードの「大きさ」のことだと思ってください.

www.qrcode.com

 

今回解読する「例のコード」に関しては,「アライメントパターン」が 1 つあったことから,バージョン2以上であると推定されていました*9

 

この事に関しては,ツイキャス@l6_6l さん から「アライメントパターンを確認した」旨のコメントがあり,目撃者が多そうです.

解読キャスに遊びに来てくれた方のコメント.この方は「例の QR コード」のメモも一緒に送ってくださいました!その節はありがとうございました.

また,「そんなにデカくなかったよなあ……」という見た感じのおぼろげな記憶があったので,試しにバージョン2,3,4 での出力結果を比較してみたのが以下です(これは解読キャス中のキャプチャなのでちょっと画質は落ちます).

解読キャス中,れすさんが試しに「 https://www.natsukawashiina.jp 」をバージョン 2,3,4 (左からこの順)で生成したもの.

比較してみると,「なんかバージョン 3 はデカすぎる気がする」というパッと見のサイズ感の記憶から,また遺されたメモの段数から,バージョン2 だと断定しました.

 

ところで,先ほどモード指示子を読み取る際にマスクを掛けたとき,しれっと

ここでは,(0番から数えて)24 番の列が右端に来ますので,先ほど読み取ったマスクを掛けると,右端の列の 0 と 1 がマスクで反転します.

と,「列数が 25 である」ようなことを仮定して言っていましたが,実はこれは数えたわけではなく,バージョン2 のサイズが「25 × 25」であることから導いた値でした.

 

どんどん仮定に仮定が積み重なって行きます.こうなると「どうかこれまでの仮定は正しくあってくれ……!」という祈りに似た感情が現れてきますね.

 

 

さて,ちょっとバージョン情報に寄り道が過ぎましたが,「8 bit モード」で「バージョン2」であると仮定して進めると,モード指示子の次の8ビットぶんが文字数情報ということになります.

この部分を読んでいくことになりますが,ここが結構メモによってバラバラでした.

曖昧なところを「?」と置いて全パターン試してみたものが次です.

文字数指示子の解読結果.メモによってバラつきが大きいところは不明として,あり得るパターンを試していく.赤字は自信があるところ.

こうして,文字数候補は 2,6,10,14,18,22,26,30 文字であると絞られました!

(実は漢字や平仮名などのマルチバイト文字だと「文字数」と一致しないので,厳密には「2,6,…,30バイトのデータ」です.)

 

データ部分

さて,いよいよ本丸のデータ部分の解読に取り掛かります.

先ほど読んだ文字数指示子の続きから読んでいけばよいのですが,モード指示子いわく「8 bit モード」ですので,漢字や平仮名などのマルチバイト文字も普通にあり得ます.つまり,どこまで読めば「1文字」の情報になるのかはわかりません.

今回の QR コード演出の元になったであろう『ハレノバテイクオーバー』の MV では,「喜」「怒」「哀」「楽」の4文字がそれぞれ QR コードとして出現していましたから,マルチバイト文字は覚悟の上です.

むしろ漢字などの場合,続きがある程度推測できそうなので,一文字目が分かってしまえば芋づる式に解読できるのでは??という期待もありました.

 

 

さてさて,ドキドキの解読ですね!では取り掛かってみましょう.やることはこれまでと同じです.マスクをかけて,右下からジグザグです.

1文字目(というか「1バイト目」)を読み取ってみた結果.「ジグザグ」を忘れてしまった人用に,緑色で薄く矢印を描いておきました.下には今までのまとめとして「モード指示子」と「文字数指示子」の解読結果が書いてあります.

 

結果 「01101000 と読めましたが,実は 8 bit モードで書かれるときの文字コード(UTF-8)の仕様から, 0から始まっている段階で英数字であると確定します

当初の目論見からは外れましたが,そのまま進めます.

読み取った「01101000」を10進表記にすると 104 ですが,これに対応する文字をコード表から探すと…………

 

 

 

h

 

 

そして,解読キャスに居たみんなが気づきます.

「……これ URL なんじゃね?????」

 

解読の難航 ── URL 説浮上

突如として浮上してきたURL説に乗り上げ,我々は座礁します.

 URL となると,めちゃくちゃな数のパターンがあり得るため,特定はかなり難しいです.

これがもしも公演後に初公開されるムービーへのリンクだったりしたら,特定はほぼ不可能です.暗雲が立ち込めます…….

我々は「右下から記録する」という方法で解読に挑んだわけですが,こうして得られるのは頑張って「https://」というほとんど意味を持たない情報でした…….

夏川さんの「きっとみんな右下から読み取ってくるはずだから,URL にて困らせちゃおうよ!(*>△<)」という策にまんまとハマってしまったわけです……!!!(諸説あり)

 

 

ですが,URL であっても条件があります.先程読み取った文字数の情報です.

読み取った結果から,2,6,10,14,18,22,26,30 文字のいずれかであると推定されたのでした.

 

ここに URL という条件を追加すると,「https://」で 8 文字使ってしまうので,短くて 18 文字,長くて 30 文字くらいであろうと推定できます.

 

ここまで仮定すると,

  • 18,22,26,30 文字のいずれかか
  • 夏川椎菜にゆかりのありそうな URL」である

まで候補が絞られそうです.

とはいっても,相当のパターンがあることには変わりないので,困ったことには変わりありません…….

 

天啓 ── 公式 Twitter の情報と別の解読部隊

そんなとき,実は夏川椎菜公式 Twitter で,ライブの様子を紹介する映像が公開されていたことを知ります.

 

 

この 0:36〜0:39 あたりを見ると,なんとQR コードの上のほうがチラッと見えています!!

これは魔の「https://」地帯を脱した先の情報のはずです.

ここから復元した QR コードの一部が以下です.

 

公式 Twitter の映像から読み取って復元したもの.

これを基に解読を試みます!0,1 は解読に関わるところだけ抜粋して書いておきます.

 

公式 Twitter の映像から読み取ったもの.マスクで反転された部分は青字で記入してあり,白黒はそのままで数字だけ反転してあります.

読み取った結果,17 文字目が u,18文字目が k になることが判明しました.

 

17 文字目が u, 18 文字目が k……??

 

 

 

 

 

……https://www.natsukawashiina.jp なのでは????

 

 

 

しかもこれは候補にあった30 文字ピッタリです!!

さっそくこれを確かめてみた結果が,冒頭のツイートです.

 

 

この結果は,埼玉公演・大阪公演から「人力パターン認識」で覚えてきたものの特徴を持っており,かなり信憑性が高いです!!

 

そして,キャスに遊びに来てくださった英通訳さん (@etyP_trnslator)がメモっていたものとも非常に近いです.

 

 

ところで QR コードをメモっている人が自分たち以外にも居るのいいですね.異常行動ではないことの証左です.

追記 (2022年11月14日 4:17):

実はこの英通訳さんのメモには,公式 Twitter の力を借りて読んだ「17文字目と18文字目の情報」が完璧に記録されていました.つまり,公式からの「ヒント」が無くても

オタクのメモだけで最終的な結論まで行き着けた

ということですね!!!

 

 

 

……と,ここまでこの記事を注意深く読んでくださった方も気づかなかったかもしれませんが,実はこの URL ,バージョン探しのときに一度通っているんですよね.

 

再掲: バージョン2,3,4 での 「 https://www.natsukawashiina.jp 」の結果.いちばん左が正解と一致するはずだったが……??

 

Q. なぜ見た目が違うんですか??

 

 

A. (この場合は)マスクが違うからです!!!!!!!!!

 

 

実は「マスクの選び方」について簡単に説明したときに,こんなことを言っていました.

また,「めっちゃ黒い QR コード」や「めっちゃスカスカな QR コード」も読み取りにくいので,いい塩梅にバラけてくれるようなマスクをいい感じに選ぶことで,読み取りやすいコードを生成します.

 

この「いい感じに選ぶ」部分がジェネレーターによってまちまちらしく,同じ入力でも異なったコードが生成されることがあります*10

 

だからマスクは重要なのです……(注釈に書いたいろんなマスクで試すスクリプトは,この後に作られたものなので,スルーしていた……).

まあ結局解読できたから良かったんですけどね!!!!!

 

答え合わせと思わぬアクシデント

この解読結果はいろいろな人に見てもらえて,「中野初日行くので確認してきます!!」と,たくさんのひとが「答え合わせ」のお手伝いに名乗りを上げてくれました.

「答え合わせ」の方法は人力パターン認識で,特徴的なパターンがあるかどうかを確認するものでした. 

追記 (2022年11月14日 4:17):

 ここで,解読を手伝ってくださった共犯者(なかま)たちのご紹介です.ゾンビのように噛み付いて(?)巻き込んでいきます!

 

こちらは「パターン」を見つけてくれた方々

 

こちらは,何故かステッカーを作ってきてくださり,ペンラに貼って直接比較してくれた猛者の方です(ちなみに1枚ステッカーいただきました).

 

こちらは,推測した QR コードを何故か LED パネルに表示して,どこでも MAKEOVER (一部) を実現した方です.

この方,過去ツイを見ると QR コードを解読していましたので,こちら側ですね.

 

以上です.それでは最後までどうぞ!

 

さて,いよいよ「答え合わせ」の日……!

開演前,「中野は追加公演だからガラッと QR コードを変えるんじゃないか」とか,「もう一個見たことがないコードが追加されるんじゃないか」など,謎の不安を抱えていましたが,やがて照明が落ちて開演……!

 

 

終演しました.

結論から言うと,変わっていませんでしたし,我々の解読は完璧に成功していました.

 

……はい.

悲願だった QR コードの解読に成功したのに,心は凪いでいます.

 

なぜか.ヒントをお伝えすると,

中野初日は,夏川さんの新曲『ササクレがサプライズ披露された日でした.

natalie.mu

 

QR コードのことなんて,どうでもよくなってしまいました────

 

 

 

おわりに

多くの人間が QR コードに注目しそうになった中野初日に『ササクレ』をサプライズ公開し,すべての注目を奪い返した夏川さん.

そんな我らが愛しの推し・夏川椎菜さんからの有り難い言葉を引用して,公演中に QR コードをメモっていた我々の旅の締めくくりとしましょう.

どっち見てんだよ こっち向いてよ

──── 夏川椎菜『ハレノバテイクオーバー』より

 

 

 

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

*2:日本時間(UTC+9)です.この記事ではすべて日本時間で書いています.

*3:余談ですが,友人知人にこの話を「ライブでコードの解析していた」と説明すると,大体「ああ,音楽のコード(chord)解析なのね」と誤解されて,「いや,QR コード(code)でして……」と誤り訂正するやり取りが生じます.冷静に考えて上演中に QR コード出すのも狂っているので「この推しにしてこのオタクあり」という感じはしますね.

*4:夏川椎菜さんのことも大好きです!!!!!!!!!!!

*5:有限体(ガロア体)上の多項式の割り算も定義から計算していました.すごい…….

*6:ここで紹介する図を生成するコードは Google Colab で公開しています.ご興味のある方はどうぞ.

*7:すべて誤り訂正レベルは L のものを使っています.

*8:参考サイトでは「データ」と「誤り訂正符号」をあわせて「データ」と呼んでいますが,ここでは便宜上分けています.

*9:実はアライメントパターンが存在することから,「モデル1」という旧型のものでなく,「モデル2」であることもわかります(詳細はこちら).QR コードも進化しているのですね.

*10:その他にも,モードが「8 bit モード」ではなく「英数字モード」だったりとか,モードが異なっている場合も同じ結果で異なったコードが生成されることがありますね.

ハードルくぐり

こんばんは,marich です.どねです.( 。•̀_•́。)です.
自分自身をなんと呼べばいいのかもよくわからないまま,ブログ開設してから丸13ヶ月が経とうとしています.

元々は「Twitter で語り切れないことをこの場で書こう」と思って始めたものですが,どうやら俺の言いたいことは140字に収まってしまうらしいな…….


今まで更新しなかった理由に,
「記事にしようと思う面白いネタも,それを形にするだけのスキルや知識も足らなかった」
というのはあると思いますが,
これはもっと正確に言うと,「面白いネタを綺麗に纏められなきゃダメだ!」みたいに,「記事を書く」という行為を神聖視(?)しすぎていたのかもしれませんね.

というわけで,まあそんなこと気にせず取り敢えず更新しておけということで,こうして更新してみました.
高くなりすぎたハードルをくぐってみただけです.
トピックは特にありません.

 

近況です.

トイレットペーパーが切れかけたので買い物に行きました.

よく見たらキッチンペーパーでした.

長いですね.

次の買い物までにお腹を下さないことを祈ります.

 

それでは.

 

ブログ開設しました

はじめまして,marich1224 です.

2021年4月25日現在,@donnay1224Twitter をやっている者と同一人物です.

そこそこ長い文章を書くことがあった場合,更新する可能性があります.

よろしくお願いします.

 

Mathlog: donnay_biobioの記事一覧 | Mathlog

GitHub IO: https://marich1224.github.io/