【QRコード入門】作成・読込からリアルタイムカメラ取得QRコードのDCGAN画像変換で遊んでみた♪

最近、QRコードを使って決済するのがはやりなので、遅まきながらQRコードで遊んでみました。
考えていたより、簡単にかつ処理も早いのでいろいろ使えそうです。
今回は、QRコードについて作成は参考①、読み込みは参考②を参考に実施しました。また、カメラでの読み込みとして参考③を参考に実施しました。
【参考】
①[Python, Pillow, qrcodeでQRコード画像を生成、保存＠note.nkmk.me](https://note.nkmk.me/python-pillow-qrcode/)
②[pythonで画像からQRコードを読み込む@python入門ブログ](https://python-minutes.blogspot.com/2017/06/pythonqr_28.html)
③[PYTHONでMACのカメラからQRコードを読んでみた@FROM UMENTU IMPORT STUPID](https://www.blog.umentu.work/python-qrcode/)
###やったこと
・QRコードを使ってみる生成・読込
・生成QRコードをカメラ読み込み処理をする
・DCGAN生成パラメータのQRコード画像を作成する
・DCGANのQRコード画像をカメラ読み込みしつつ、リアルタイム画像動画を表示する
###・QRコードを使ってみる生成・読込
これは参考①、②のとおりだが、簡単に作成したものをまとめておく
まず、今回主に使うqrコード関係のインストールは以下のとおり

```
>pip install --upgrade pip
>pip install opencv-python
>pip install pillow
>pip install pyzbar
>pip install qrcode
```
早速、QRコード生成のコードを見てましょう。

```py
import qrcode
from PIL import Image
qr = qrcode.QRCode(box_size=1)
qr.add_data('最近、QRコードを使って決済するのがはやりなので、遅まきながらQRコードで遊んでみました。')
qr.make()
img_qr = qr.make_image()
img_qr.save('./gen_images/qr_lena_1.png')
```
これでbox_sizeを1,2,5,10と変更すると以下のような出力となる。

| box_size|   size    | QRコード |
|:-:|:-:|:-:|
| 1 |57x57 | ![qr_lena_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/a5787d80-d358-5538-d3a6-0932009b6648.png)|
| 2 |114x114|![qr_lena_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/c4d38992-b28e-3e06-347e-06f7dac533a3.png)|
| 5 |285x285|![qr_lena_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/7decd326-026c-9a89-bbdf-ecd496d5203b.png)|
| 10|570x570|![qr_lena_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/22eaec17-eb74-24d5-2d58-63091e15b2b9.png)|
QRコードは以下のパラメータを持つ(詳細は[![cr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/8b0d2202-b844-dc0a-b23e-6a082a7b8b90.png)](https://www.qrcode.com/about/)を見て下さい)
・バージョン（種類）は、1から40まで設定；21x21ピクセルから177x177ピクセルまで
・ピクセルのサイズをbox_sizeで変更できる
・情報量に応じて最小のバージョンがデフォルトに選ばれるが指定もできる
・[誤り訂正機構を選べる](https://www.qrcode.com/about/version.html)が、サイズは大きくなる
...
これらの出力制御は以下のような感じらしいです。参考①を参照願います。

```py
qr = qrcode.QRCode(
    version=12,
    error_correction=qrcode.constants.ERROR_CORRECT_H,
    box_size=2,
    border=8
)
```
一方読み込みの基本も簡単で、以下のとおり

```read_qr.py
# -*- coding: UTF-8 -*-
from pyzbar.pyzbar import decode
from PIL import Image

# 画像ファイルの指定
image = './gen_images/qr_lena_1.png'

# QRコードの読取り
data = decode(Image.open(image))
input=data[0][0].decode('utf-8', 'ignore')
print(input)
```
一番小さいのも無事に入力通りの文章が得られました。

```
>python read_qr.py
最近、QRコードを使って決済するのがはやりなので、遅まきながらQRコードで遊んでみました。
```
###・生成QRコードをカメラ読み込み処理をする
コードはほぼ参考②のとおりで、以下のとおり
ちょっと、解説いれます。
利用するLibです。

```py
from pyzbar.pyzbar import decode
from pyzbar.pyzbar import ZBarSymbol
import cv2
import numpy as np
```
以下がQRコードのコントラスト調整用の関数です。

```py
def edit_contrast(image, gamma):
    """コントラスト調整"""
    look_up_table = [np.uint8(255.0 / (1 + np.exp(-gamma * (i - 128.) / 255.)))
        for i in range(256)]
    result_image = np.array([look_up_table[value]
                             for value in image.flat], dtype=np.uint8)
    result_image = result_image.reshape(image.shape)
    return result_image
```
以下がカメラ入力と読み取りの処理です。
num0=input[1:len(input)-1]は読み込んだ一文字目と最後の文字を削除していますが、これは後で、[-1.0,1.0]のような形式のデータを読むために入れていますが、上記の文章などではいらないのでコメントアウトしています。

```py
if __name__ == "__main__":
    capture = cv2.VideoCapture(1)
    cv2.namedWindow('frame')
    if capture.isOpened() is False:
        raise("IO Error")
    ret, frame = capture.read()
    while True:
        if ret == False:
            continue
        # グレースケール化してコントラストを調整する
        gray_scale = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        image = edit_contrast(gray_scale, 5)
        # 加工した画像からフレームQRコードを取得してデコードする
        codes = decode(image)
        if len(codes) > 0:
            input=codes[0][0].decode('utf-8', 'ignore')
            num0=input　　#[1:len(input)-1]
            # コード内容を出力
            print(num0)
            font = cv2.FONT_HERSHEY_SIMPLEX
            cv2.putText(frame,str(input),(10,300), font, 2,(255,255,255),2,cv2.LINE_AA)
        cv2.imshow('frame',frame)
        if cv2.waitKey(1) >= 0:
            break
        ret, frame = capture.read() 
```
実は上記のコードだと、数値などは出力できますが、日本語は出力できません。
日本語部分は、？？？？みたいな文字化けがでます。
ということで、日本語は以下のようなコードになります。
【参考】
④[Python + OpenCV でWindowsの日本語フォントを描画する](https://qiita.com/goodboy_max/items/d5e7f1c0ecd3d446c4d0)
参考④のコードを使って日本語の場合は以下のようになります。
・[QR_code/camera_qr_jpn.py](https://github.com/MuAuan/QR_code/blob/master/camera_qr_jpn.py)
このコードで以下のようなカメラ画像が得られます。
※起動に少し時間がかかります。QRコードを認識しないと画像がでません
![res.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/6e6c1764-572c-62a5-e088-336d7cdc3ce4.png)
###・DCGAN生成パラメータのQRコード画像を作成する
先日のDCGANのパラメータをQRコードにして画像をカメラ画像上に出現させようというアイディアです。ということで、DCGANのパラメータのQRコードを連続作成しました。
以下のようにnoise_listに書かれているのがそのパラメータです。

```py
import qrcode
from PIL import Image

img_bg = Image.open('./gen_images/generator_22000_1.png')
noise_list=['[-1. -1.0]','[-1.  -0.9]','[-1.  -0.8]','[-1.  -0.7]','[-1.  -0.6]','[-1.  -0.5]','[-1.  -0.4]','[-1.  -0.3]','[-1.  -0.2]','[-1.  -0.1]']
img_bg =img_bg.resize(size=(640,640), resample=Image.NEAREST)
for noise in noise_list:  #='[-1. -1.]'
    qr = qrcode.QRCode(box_size=10)
    qr.add_data(noise)
    qr.make()
    img_qr = qr.make_image()
    img_qr =img_qr.resize(size=(img_bg.size[0], img_bg.size[1]), resample=Image.NEAREST)
    pos = (img_bg.size[0] - img_qr.size[0], img_bg.size[1] - img_qr.size[1])
    img_bg.paste(img_qr, pos)
    img_bg.save('./gen_images/qr_img_'+noise+'.png')
```
上記により生成された連続したQRコードのGifアニメが以下です。
![qr_img_.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/dabe5576-5e89-0f7f-b23e-5e42bf9ed91d.gif)
###・DCGANのQRコード画像をカメラ読み込みしつつ、リアルタイム画像動画を表示する
全体のコードは以下に置きました。
・[QR_code/qr_dcgan_generate_one.py](https://github.com/MuAuan/QR_code/blob/master/qr_dcgan_generate_one.py)

まず、DCGANで画像生成する部分を説明します。
コードは以下の通りとなっています。
ここではモデルを定義してnoiseを使ってgenerated_imagesを生成します。
読み取りが[-1.0　-1.0]のような文字データで、そこから-1.0　-1.0だけ切り出されて取得できます。そこで、最初の４桁と５桁目から９桁目を取得して、noiseに入力しています。

```py
def generate(BATCH_SIZE=1,num0=[1,0]):
    g = generator_model()
    g.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0002, beta_1=0.5))
    g.load_weights('./gen_images/generator_22000.h5')
    noise = np.random.uniform(size=[BATCH_SIZE, 2], low=-1.0, high=1.0) ##32*32
    x=np.float(num0[0:3])
    y=np.float(num0[5:9])
    print(x,y)
    noise[0]=[x,y]  #[-1,0.5]
    generated_images = g.predict(noise)
    return generated_images
```
次にnoise_getを説明します。
上記と同じようなコードですが、ここでは必要な機能だけ置いています。
つまり、ほぼQRコードを読み込むだけで、読み込んだ文字列から最初と最後の[]を削除して返すようにしています。

```py
def noise_get(ret,frame):
    while True:
        if ret == False:
            continue
        # グレースケール化してコントラストを調整する
        gray_scale = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        image = edit_contrast(gray_scale, 5)
        # 加工した画像からフレームQRコードを取得してデコードする
        codes = decode(image)
        if len(codes) > 0:
            input=codes[0][0].decode('utf-8', 'ignore')
            num0=input[1:len(input)-1]
            print(num0)
            return num0,frame
        if cv2.waitKey(1) >= 0:
            break
        ret, frame = capture.read()
```
そして、メインは以下のとおりとしました。
ここで最大の工夫は、取得した画像を一度保存して再度読みだしています。
そのとき表示はせずに単に保存だけしています。こうすることによりImageで扱えるフォーマットにしています。

```py
        plt.imshow(frame)
        plt.savefig('./gen_images/frame.png')
        frame = Image.open('./gen_images/frame.png')
```
その結果、画像合成を簡単に実施しています。
しかも、mask = Image.new("L", frame.size, 1)のこの１を変更することにより、それぞれの透過度を状況により変更できます。
そして、imgArray = np.asarray(im)で再度numpy配列に変更し、取得したテキストを重畳してcv2.imshowします。

```py
if __name__ == "__main__":
    while True:
        capture = cv2.VideoCapture(1)
        cv2.namedWindow('frame')
        if capture.isOpened() is False:
            raise("IO Error")
        ret, frame = capture.read()
        num0,frame=noise_get(ret,frame)
        generated_images=[]
        generated_images=generate(BATCH_SIZE=1,num0=num0)
        plt.imshow(frame)
        plt.axis("off")
        plt.savefig('./gen_images/frame.png')
        plt.close()
        frame = Image.open('./gen_images/frame.png')
        plt.imshow(generated_images[0])
        plt.axis("off")
        plt.savefig('./gen_images/generated_images.png')
        plt.close()
        img = Image.open('./gen_images/generated_images.png').resize(frame.size)
        mask = Image.new("L", frame.size, 1)
        im = Image.composite(frame, img, mask)
        im.save('./gen_images/qr_'+str(num0)+'.png')
        imgArray = np.asarray(im)
        
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(imgArray,str(num0),(100,200), font, 2,(255,255,255),2,cv2.LINE_AA)
        cv2.imshow('frame',imgArray)
        if cv2.waitKey(1) >= 0:
            break
```
以下のような出力がでます。
![realTime_transform_qr.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/64720ad7-3fd7-3824-e625-ade9402b31e1.jpeg)
上記のQRコードを読み込んで、以下のような画像が得られました。
※ちょっと画像が綺麗じゃないけど、このまま貼っておきます
![qr_pic_.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/8fef126f-3363-f422-9197-13facb890019.gif)
###まとめ
・QRコードの読み書きで遊んでみた
・カメラでリアルタイムで読み書きできた
・DCGANを利用してカメラ取得のQRコードからリアルタイム画像変換ができた

・いろいろなシーンで利用可能なのでさらなる応用をしたいと思う