赤と黒のQRコードが読めないのはなぜか — 2値化と相対輝度をJSで計算する
QRコードにブランドカラーを当てたら読めなくなった、という話は繰り返し出てくる。原因を「色を変えたから」で終わらせると次も同じことをするので、読み取り機が実際に何を見ているのかまで降りておきたい。
結論を先に書くと、読み取りが見ているのは色相ではなく明るさである。だから赤と黒のように、目には強い対比に見える組み合わせでも読めなくなる。ハシトシステムのQRコード生成ツールで前景色と背景色を選ぶときに何を基準にすればよいか、その基準を計算で出す。
読み取りの最初の工程は2値化である
QRコードのデコードは、おおまかに次の順で進む。
- 撮影した画像を明るいセルと暗いセルの2値に分ける(2値化)
- 3隅の位置検出パターンを見つけ、傾きと歪みを補正する
- モジュールを読み出し、マスクを外す
- 誤り訂正で欠損を復元し、データを取り出す
注目すべきは順番である。誤り訂正は4番目にしかいない。つまり1番目で明暗が分かれなければ、誤り訂正は一度も働かない。汚れやかすれが復元できるのは2値化を通過したあとの話であって、色の失敗はその手前で起きている。
自分のコードがどう2値化されるかを確かめる一番早い方法は、画像をグレースケールに変換してみることである。グレーにして模様が判別できなければ、読み取り機にも判別できない。
sRGB から相対輝度を出す
「明るさ」を数値にするには、色空間の話が少し要る。CSSで指定する #ff0000 のような値は sRGB のガンマ補正がかかった値で、そのまま平均を取っても人が感じる明るさにはならない。
W3C のアクセシビリティ指針(WCAG)が定義している相対輝度の式が、この用途にそのまま使える。手順は2段階である。
まず各チャンネルを 0〜1 に正規化し、ガンマを外して線形値に戻す。
1 | |
次に、人の目の感度に合わせた係数で足し合わせる。緑の係数が大きいのは、同じ強度なら緑が最も明るく見えるためである。
1 | |
コントラスト比は、明るいほうと暗いほうの相対輝度から出す。分子分母に 0.05 を足すのは、真っ黒(輝度0)で比が発散しないようにするためである。
1 | |
赤と黒を実際に計算する
用意した関数で、よく使われる組み合わせを並べてみる。
1 | |
出力は次のようになる。
1 | |
黒と白の 21.00 が理論上の上限である。ここで見たいのは黒と青の 2.44 で、目には「濃い青と黒」で十分に違って見えるのに、輝度で見るとほとんど差がない。青の係数が 0.0722 と最も小さいので、青をいくら濃くしても輝度はさほど上がらないためである。
黒と赤の 5.25 は微妙な位置にある。明るい室内なら通ることが多いが、照明が落ちた場所や斜めからの撮影では失敗が増える。一方で黒と黄の 19.56 は白に近く、色を付けても安全な例である。黄色は赤と緑の合成なので、係数の大きい緑を含むぶん輝度が高い。
実務上の目安としては、4:1 を下回ったら採用しない、3:1 未満は論外、という線で運用すると事故が減る。文字の可読性に使う WCAG の 4.5:1 とおおむね同じ水準で考えておけばよい。
明暗の向きは比では表せない
コントラスト比は絶対値なので、どちらが明るいかの情報を持たない。黒地に白のコード(反転コード)は、白地に黒のコードと同じ 21.00 を返す。
しかしQRコードの規格は、明るい背景に暗いセルを前提にしている。反転コードを読める実装は実際にあるが、それは実装ごとの好意であって保証ではない。したがって判定は2つ要る。
1 | |
比だけを見て通すと反転コードが素通りするので、inverted の判定を別に持つ必要がある。
背景が一様でない場合はこの計算が効かない
ここまでの計算は、前景と背景がそれぞれ単色であることを前提にしている。写真の上にコードを重ねる、セルにグラデーションをかける、金や銀の光沢インクを使う、といった構成では背景の輝度が場所ごとに変わるため、単一のしきい値が決まらない。
この場合の対処は計算ではなく構成の側にある。コードの下に無地の下地を敷き、その下地をクワイエットゾーン(周囲4モジュール分の余白)の外側まで広げる。余白の内側だけ白くして外が写真のままだと、境界の検出が不安定になる。
なお、光沢インクは角度によって明暗が反転するので、静止画で計算しても意味がない。読める角度と読めない角度が生まれることを前提に、印刷前に実物で確かめる以外にない。
まとめ
QRコードの色の失敗は、誤り訂正より手前の2値化で起きている。判断に使えるのは相対輝度とコントラスト比で、どちらもブラウザだけで数行の関数として書ける。比が 4:1 以上あること、明るい側が背景であること、背景が単色であることの3つを満たせば、ブランドカラーを当てても読み取りはおおむね安定する。
色を決めたら、実際にその色でコードを作って確かめておきたい。QRコード生成ツールで前景色と背景色を指定でき、できあがったコードの構造はQRコード解析ツールで読み解ける。うまく読めなくなったコードの原因を切り分けるなら写真のQRコード復元ツールが使える。