🤖

QRコードをQR分解する

2021/04/10に公開

概要

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(Q @ Q.transpose())

Q

ちゃんと対角行列になっていますね。

次はRを見てみましょう。

get_image(R)

R

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

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

get_image(Q @ R)

QR

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

まとめ

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

関連記事

GitHubで編集を提案

Discussion