- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 写真のように見えるQRコードを見て抱く疑い
- QRコードは二種類のマスでできています
- 写真が入る場所 — モジュールひとつを九マスに割る
- だから九マスのうちひとつが無作為です
- ディザリング — 二値化の代わりに誤差を流す
- 核心のアイデア — データモジュールの誤差を先に流す
- 余裕は予算です
- 実際に使うときに引っかかるもの — 余白と拡大
- まとめと出典
写真のように見えるQRコードを見て抱く疑い
白黒写真のような見た目なのに、スキャンするとリンクが開くQRコードを見たことがあると思います。真ん中にロゴが埋まっている程度ではなく、コード全体が絵です。初めて見ると大したものに見えて、二度目に浮かぶ考えはたいていこれです。あれはどうやって読めているのか。
答えは予想より単純で、単純だからこそ重要です。実務で知っておくべきなのは読める理由ではなく何を対価に払ったのかのほうです。この記事はAndrew T.が公開した誤差拡散QRコード生成器の説明文書をたどりながら、その対価が何なのかを押さえます。
QRコードは二種類のマスでできています
元資料の説明はQRコードを二つの部分に分けます。ひとつは機能パターンです。角の太い四角のような図形で、スキャナがコードを見つけて向きと大きさを決めるのに使います。もうひとつはデータモジュールです。残り全部で、実際のデータとヘッダが入ります。
核心は読む順序です。スキャナはまず機能パターンでコードの座標系を確定し、そのあとでデータモジュールを読みます。だから機能パターンはとても鮮明でなければならず、データモジュールは相対的に手を加える余地があります。ブランドが作る個性的なQRコードはこの余地を使っているわけで、資料の表現どおり、スキャンの堅牢性は多少落ちるものの、かなり手を加えてもたいていは読めます。
写真が入る場所 — モジュールひとつを九マスに割る
ここで出てくる技法が面白いところです。元資料が紹介する方式は、モジュールひとつを3×3の格子に割り、真ん中の一マスだけを元のデータの色にして、残りの八マスを写真に明け渡すというものです。
これが通じる理由が先ほどの順序にあります。スキャナは機能パターンが教えてくれた位置を基準に、各モジュールの中心を見ます。中心さえ合っていれば周りは自由です。だから格子の一マスのなかで中心の一マスだけを守り、八マスを絵で埋めれば、コードも読めて絵も見えます。
出来上がるのは低解像度の1ビット白黒写真です。元資料が例に挙げた画像は147×147画素です。この数字を3で割ると49になり、QRコードの一辺が49モジュールである場合と噛み合います。つまりモジュール49個をそれぞれ3マスに割った結果だということです。
だから九マスのうちひとつが無作為です
この方式には避けられないノイズがついてきます。各モジュールの中心の一マスは、写真が何であろうと関係なくデータの定めた色でなければなりません。データは写真と無関係なので、その一マスの色は絵の側から見れば事実上無作為です。
元資料はこれを正確にこう表現します。画素九個のうちひとつが事実上無作為な色だ、ということです。だから結果の画像に塩と胡椒を振りかけたような斑点が生じます。147×147の1ビット画像なので元から粗いのですが、粗さのかなりの部分がこの九分の一から来ます。
ここで普通に思いつく解法がひとつあります。その中心画素のいくつかを絵に合わせて変えてしまうことです。QRコードには誤り訂正があるので、いくつかは間違っていても読めるからです。真ん中にロゴが埋まったQRコードは、まさにその原理で動いています。ところが元資料の結論は違います。あとで見ます。
ディザリング — 二値化の代わりに誤差を流す
まず絵の側の話を整理しなければなりません。明るさを白黒の二値に落とすときに単純な二値化を使うと、中間調が全部消えます。そこで普通は格子模様で中間調を真似るのですが、規則的な格子はそれ自体の模様が目につきます。
フロイド・スタインバーグの誤差拡散は違う攻め方をします。元資料の説明をそのまま移すとこうです。左上から始めて画素を普通どおり二値化します。明るさが50パーセントを超えれば白、そうでなければ黒です。ある画素が70パーセントだったなら、白にすることで30パーセント分だけ明るさを多く与えたことになります。この30パーセントを、まだ処理していない周りの画素に分けてその分だけ暗くします。その画素を二値化するときにその値が反映されます。
全体を回し終えると、画像の各部分が平均的には元の明るさに近づきます。そして格子模様の代わりに不規則なパターンが出るので、目に障りにくくなります。
核心のアイデア — データモジュールの誤差を先に流す
ここで二つの話が出会います。元資料の本当のアイデアは誤差拡散を二度回すことです。
二度目の通過は平凡なフロイド・スタインバーグです。一度目の通過が違います。データモジュールの中心画素だけを対象にします。その画素の色はすでに決まっているので、決まった色で塗り、そのときに生じた誤差を周りの八マスへ流します。
ここで誤差がとても大きくなりえます。普通のディザリングでは色を選ぶのは私たちなので誤差が50パーセントを超えません。ところがこの場合は、とても暗い領域の画素を純白にしなければならないこともあり、そうすると95パーセントの誤差が生まれます。元資料は、これは悪いことのように聞こえるが、まさにそれゆえに流すことが重要なのだと言います。そのまま受け入れればその場に斑点として残り、流せば周りがその分だけ暗くなって平均が合います。
そこで先送りにした問いの答えが出ます。元資料の結論は、データモジュールをいくつか絵に合わせて変える方式が画質にはたいして役立たないのにスキャン性能はひどく削るというものです。誤差をきちんと流せば、色を変える必要そのものがほとんどなくなるからです。
余裕は予算です
この地点がこの記事でいちばん言いたいことです。画質を上げる軸と誤り訂正の余裕を使う軸は別の軸です。二度目の誤差拡散は余裕を一粒も使わずに絵を改善します。一方でデータモジュールを変えることは余裕を直接燃やします。
余裕が何のためにあるのかを考えると、この区別がなぜ重要かがはっきりします。QRコードの誤り訂正水準は四段階で、規格を作ったデンソーウェーブは、そのうちもっとも多く使われる水準が15パーセントを復元すると案内しています。水準を上げれば復元力は上がりますが、同じデータにより大きなコードが必要になります。そしてその復元力は、もともと印刷不良と皺と汚れと斜めの角度と暗い照明のために残しておいたものです。
つまりきれいに見せるために余裕を使うということは、現場の悪条件に備えた予算をデザインへ移して使うことです。元資料も同じ結論に至ります。大きな画面やポスターに貼るコードなら構わないが、くしゃくしゃになりうる紙のチラシならその余裕が必要になる、というものです。そして一文を付け足します。自分の携帯でノートパソコンの画面から問題なく読めるからといって、見知らぬ人の古い携帯が暗いところで印刷物を読み取れるという意味にはならない、と。
実際に使うときに引っかかるもの — 余白と拡大
元資料が最後に添えた実務上の注意二つも、そのまま移す価値があります。
一つ目は余白です。生成器は余白のない小さな画像を作ってくれますが、QRコードが安定して読まれるには外側に余白が必要です。そしてその余白の色は、角の大きな四角の内側の色の反対でなければなりません。普通は白ですが、反転したコードを作ったなら黒い背景が必要です。
二つ目は拡大です。ブラウザは小さな画像を大きく表示するとき、既定でぼかして補間します。ぼやけた境界は、スキャナがモジュールの中心を判定しにくくします。CSSで補間を切らないと、この問題が静かに起こります。
二つの項目には共通点があります。作った人の画面では問題なく、使う人の環境でだけ失敗します。きれいなQRコードの失敗は、たいていこういう形でやってきます。
まとめと出典
QRコードに絵を入れるのは自由です。ただ、それが何から借りた自由なのかは知って使うほうがよいと思います。スキャンの失敗は私たちの画面ではなく利用者の手元で起き、そのときにはすでに印刷が終わっています。
- How to make error-diffused QR codes — 機能パターンとデータモジュールの区別、3×3の分割、九分の一のノイズ、二度の誤差拡散、95パーセントの誤差、余白と拡大の注意事項は、すべてこの文書の説明です。
- 生成器 と ソースコード — 自分で作ってみられます。
- 誤り訂正水準に関するデンソーウェーブの文書 — 四段階があること、もっとも多く使われる水準が15パーセントであること、水準を上げるとコードが大きくなることを確認しました。残り三つの水準の正確な数値は当該ページで画像としてのみ提供されているため、本文には書きませんでした。
- 147を3で割ると49になるという計算と、それが一辺49モジュールに対応するという記述は、元資料の数字から私が導いたものです。