QRコードのデコードに挑む!特別編:汎用デコーダの完成と「汚れ」への挑戦【QRコードを解読する 第12回(完)】

0. はじめに

BM法、チェン探索、フォルニー法を統合し、汎用デコーダを完成させたことを示すイメージ図。

これまで数回にわたり、QRコードの心臓部である「リード・ソロモン符号」のエラー訂正アルゴリズム(BM法、チェン探索、フォルニー法)を自作し、その強力な復元力を小さなモデルで体験してきました。

今回はシリーズの総決算です。これまでに作成したすべてのアルゴリズムを結合し、「画像から文字列を復元し、エラーがあれば自動で修復する完全版デコーダ」を完成させます!
そして、自作エンコーダで生成した本物のQRコード画像に対し「わざとペイントで汚した状態(Sad Path)」でテストを行い、ロバストに検出できることを検証していこうと思います。

1. 「汎用デコーダ」の統合

これまでのコードを結合し、以下の一連の流れをすべて自動で行う Python の関数 decode_qr() を作成しました。

  1. フォーマット解析: マスクの種類と誤り訂正レベルを特定し、盤面の白黒を反転(マスク解除)する。
  2. バイト列の抽出: ジグザグスキャンで盤面を読み取り、44個の「コード語(数字)」に変換する。
  3. シンドロームの計算: エラーの兆候を調べる。すべて 0 なら即座に文字列へ!
  4. エラー修復(BM法〜フォルニー法): もしエラーがあれば、方程式を育てて場所を特定し、正しい数値に反転(修復)させる。
  5. デコード: 修復済みのデータから「HELLO」の文字を取り出す。

2. Jupyter Notebookで完全版デコーダを試す

それでは、本編で作ったこのデコーダに、実際の画像を通してみましょう。
以下のリンクからGoogle Colaboratoryを開き、ご自身のブラウザ上でコードを実行してみてください。

Qr from Scratch:Step6 汎用デコーダの完成

Qr from Scratch:Step6 汎用デコーダの完成

QRコードの車輪の再発明、ここに完結。フォルニー法でエラー値を算出し、傷ついたデータから「HAL」を復元します!

github.com

github : QR Decode
(https://github.com/Tsukumo-999/qr-code-from-scratch/blob/master/step4/full_decoder_final.ipynb)

3. 実験1:綺麗なQRコード(Happy Path)

まずは、エンコード編で出力した汚れの一切ない qr_hello.png を読み込ませてみます。

フルスクラッチしたQR生成自作コードで作成した"HELLO"という文字列を入れたQRコード

画像1 きれいなQR

【実行結果】

===== 汎用デコーダ 解析開始 =====
[1] マスクパターンを認識: 000
[2] データを抽出完了 (44 バイト)
[3] シンドローム検証: エラーなし!完璧なデータです。

 デコード結果(文字列復元成功): HELLO

以前確認した通り復元できます。

4. 実験2:汚れやノイズに挑む(Sad Path)

いよいよ本番です。ペイントソフトでデータ領域にグチャッと線を引いて、白黒のマス目を意図的に破壊した画像 qr_hello_dirty.png を読み込ませます。

フルスクラッチしたQR生成自作コードで作成した"HELLO"という文字列を入れたQRコードをわざと汚したもの

画像2 汚したQR

【実行結果】

===== 汎用デコーダ 解析開始 =====
[1] マスクパターンを認識: 000
[2] データを抽出完了 (44 バイト)
[3] シンドローム検証: エラーを検出しました。修復を開始します...
  -> [BM法] エラー位置多項式 σ(x) を生成しました。エラーの数は約 11 個です。
  -> [チェン探索] エラー位置(インデックス)を 11 箇所特定: [43, 13, 12, 8, 7, 6, 5, 4, 2, 1, 0]
  -> [フォルニー法] インデックス 43 を正しい値に修復しました!
  -> [フォルニー法] インデックス 13 を正しい値に修復しました!
  -> [フォルニー法] インデックス 12 を正しい値に修復しました!
  -> [フォルニー法] インデックス 8 を正しい値に修復しました!
  -> [フォルニー法] インデックス 7 を正しい値に修復しました!
  -> [フォルニー法] インデックス 6 を正しい値に修復しました!
  -> [フォルニー法] インデックス 5 を正しい値に修復しました!
  -> [フォルニー法] インデックス 4 を正しい値に修復しました!
  -> [フォルニー法] インデックス 2 を正しい値に修復しました!
  -> [フォルニー法] インデックス 1 を正しい値に修復しました!
  -> [フォルニー法] インデックス 0 を正しい値に修復しました!
[4] データの修復がすべて完了しました!

デコード結果(文字列復元成功): HELLO

結果を見ると、11個破損しており、これは22byteの誤り訂正の限界の復元可能な破損量です。
(1つ復元するのには位置と値の2変数を求める必要がありこれに2つの方程式が必要なため)

見事に復元成功です!
ノイズによってデータが数バイト分化けてしまっていましたが、私たちが手打ちで実装したガロア体とリード・ソロモン符号のアルゴリズムが、裏でひっそりと、しかし確実にエラーを検知・特定・修復し、無傷の「HELLO」を返してくれました!

5. シリーズ完結!QRコードの車輪の再発明を終えて

カメラをかざすだけで「ピッ」と一瞬で読み取れるQRコード。
その裏側では、「画像処理による透視変換」「ジグザグスキャンとマスク解除」、そして「ガロア体という有限の数学空間を駆け巡る複雑なエラー修復計算」という、途方もないリレーがミリ秒単位で行われていました。

「なぜこんなに早く、しかも多少汚れていても読み取れるのか?」
その答えは、先人たちが築き上げた極めて美しい数学的アプローチと、それをコンピュータ上で無駄なく動かすための規格の集合体に他なりません。この「車輪の再発明」を通して、日常のインフラを支える技術の凄みと、プログラミングで数学を解き明かす楽しさを少しでも感じていただけたなら幸いです。

長きにわたる連載にお付き合いいただき、本当にありがとうございました!

※実は、実際のQRコードでは、画像処理にて明らかに破損している部分をあらかじめ見つける(消失訂正(Erasure Correction): もし画像処理の段階で「このマスは汚れで完全に潰れていて白黒判定不能!」とエラーの場所が最初から分かっている場合、場所を見つける計算をスキップできる)ことで、位置検出を簡略化することでより復元できる割合を上げているようです。

6. 参考文献および参考リンク

当シリーズの軌跡(デコード編)

公式規格・技術解説

余談

今回のシリーズ「QRコードの符号化と復元」は、たまたま二次元コードを自作する機会があり、「参考までにQRコードの仕組みってどうなってるのだろう?」と調べたことの副産物だったりします。

この解析を始める前は、めちゃくちゃ難解なものだと思っており、結構身構えていました。もちろん簡単とは言いませんし、ガロア体を扱う部分などはちょっと理解に壁がありましたが、実際にやっていることは割り算や掛け算のひっ算、そして連立方程式の解を求める方法に近く、手を動かして作ってみると案外わかりやすいものだと感じました。

もし、同じように2次元コードを自作する必要がある人や、仕組みを深く知りたいと思っている人がいれば、本シリーズが参考になれば幸いです。

では、また今度の記事で。 Lumen Hero. 2026/06/17