QRコードをQR分解する

commits2 min read読了の目安(約2500字

概要

QRコードをQR分解します。

QRコードをQR分解する

QRコードを良く目にすると思います。例えばこんなのです。

QR

これを見ると、まるで疎行列のように見えてきますね。なので、これを行列だと思ってQR分解したくなりますね。

QR分解とは、正方行列Aを、直交行列Qと上三角行列Rの積、

A = QR

と分解することです。なぜQR分解が必要かはその辺にいるガチ勢に聞いてください。ではさっそくQRコードをQR分解してみましょう。以下、Google Colabで実行することを想定していますが、何を使ってもかまいません。

まずはQRコードを作るのに必要なqrcodeをインストールしておきましょう。

!pip install qrcode

必要なライブラリをimportしましょう。

import qrcode
import numpy as np
import scipy.linalg as linalg
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw

まずはQRコードを作ります。

BOX_SIZE = 10
qr = qrcode.QRCode(box_size=BOX_SIZE,border=0)
qr.add_data('Hello QR Code')
qr.make()
img = qr.make_image()
img

いきなりqrcode.make("Hello QR Code")としてQRコードを作ることもできますが、そうするとまわりに「枠」ができてしまうので、それを消すためにQRCodeクラスのコンストラクタでboder=0を指定しています。

ここから行列Aを作りましょう。要素を見て、BOX_SIZE倍縮小するだけです。

LX, LY = img.size
data = np.array(img.getdata()).reshape(LX, LY)
X, Y = LX//BOX_SIZE, LY//BOX_SIZE
A = np.zeros((X, Y))
for ix in range(X):
  for iy in range(Y):
    A[ix][iy] = 255-data[ix*BOX_SIZE][iy*BOX_SIZE]

あとで使うので、行列を可視化する関数を作っておきましょう。

def get_image(M):
  M = np.abs(M)
  M = M/np.max(M)
  im = Image.new("L",(LX,LY), "white")
  draw = ImageDraw.Draw(im)
  for ix in range(X):
    for iy in range(Y):
      c = 255-int(M[iy][ix]*255)
      sx = ix*BOX_SIZE
      sy = iy*BOX_SIZE
      draw.rectangle((sx, sy, sx+BOX_SIZE, sy+BOX_SIZE), fill=c)
  return im

これで先ほどの行列Aを可視化してみます。

get_image(A)

A

できてるっぽいですね。

次に、AをQR分解します。linalg.qrを呼ぶだけです。

Q, R = linalg.qr(A)

それぞれ可視化してみましょう。

get_image(Q)

Q

これが直交行列のはずですが、パッと見ではわかりませんね。

get_image(R)

R

こちらは上三角行列だということがわかりやすいですね。

さて、A=QRなので、QRをかけたら元に戻るはずです。確認してみましょう。

get_image(Q @ R)

QR

ちゃんと元に戻りました。

まとめ

QRコードを行列だと思ってQR分解してみました。本稿がQRコードをQR分解したい人の参考になれば幸いです。

関連記事