Pythonによる文字表示付きQRコードの大量生成

# 記事の概要

Pythonのqrcodeを用いてQRコードを自動生成します。
QRコードに識別番号も同時に記載したいので、pillowやその他のツールも用いて文字も挿入します。

（追記）
@konikoni428 さんのご指摘により、画像をグレースケールに変換することで、文字が荒くなるのを回避できることが分かり、「画像の拡大・縮小処理」が不要になりました。（追記終わり）

（追記）
日本語の文字を表示させるためには、fontを日本語に対応したもの変更しないといけないようです。
詳細は以下の記事をご参照ください。

https://qiita.com/yutaka_m/items/9ce55f82c9c0d42b0c3c#_reference-b1ebfd02d00fedecb497

（追記終わり）

# ツールのインストール

QRコードの生成にはqrcodeを使用します。

https://pypi.org/project/qrcode/

qrcodeをインストールしてください。

```python
pip install qrcode
```

画像の加工にはpillowをします。

https://pypi.org/project/Pillow/

pillowをインストールしてください。

```python
pip install Pillow
```

# QRコードの生成

```python
import qrcode
from PIL import Image

img = qrcode.make("Hello World!")
img.save('test.png')

img.show()
```

<img width="30%" alt="test.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/2e6ae605-e3c2-018c-0b74-312858e1df97.png">

生成されたQRコードが正しく読み取れるかは、http://qrcode.red/ で読み込んで確認できます。

<img width="30%" alt="qrコード読み取り結果.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/e5b02526-68e4-10d5-09ad-29e2b4256773.png">


# QRコードの生成（詳細設定）
詳細な設定を行うこともできます。

```python
import qrcode
from PIL import Image

qr = qrcode.QRCode(
    version=3,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=10,
    border=2,
)
qr.add_data('Hello World!')
qr.make(fit=True)

img = qr.make_image(fill_color="black", back_color="white").convert('L')
img.save('test.png')

img.show()
```

- version
    - セル数を決めるパラメータで、1から40の値に設定できます。
    - 1で21セルx21セル、2で25セルx25セル、40で177セルx177セルです
    - 以下参照
        - https://www.qrcode.com/about/version.html
- error_correction
    - 誤り訂正レベルで以下のパラメータから選択します
        - ERROR_CORRECT_L
            - 7％以下のエラーを修正
        - ERROR_CORRECT_M
            - 初期設定値
            - 15％以下のエラーを修正
        - ERROR_CORRECT_Q
            - 20％以下のエラーを修正
        - ERROR_CORRECT_H
            - 30％以下のエラーを修正
- box_size
    - ボックスのピクセル数
- border
    - 境界線の太さ
    - 初期値4（最小設定値）

（追記）
@konikoni428 さんのご指摘を受けて、QRコードをグレースケールに変換しています。

```
img = qr.make_image(fill_color="black", back_color="white").convert('L')
```

もしデータサイズをなるべく小さくしたいなどの理由で白黒2階調にしたい場合は、グレースケール変換を省略ください

```
img = qr.make_image(fill_color="black", back_color="white")
```

（追記終わり）

# 余白の追加

画像に余白を追加、削除するには[note.nkmk.me]()にて公開されている関数add_marginが便利です。

[Python, Pillowで画像の上下左右に余白を追加しサイズ変更](https://note.nkmk.me/python-pillow-add-margin-expand-canvas/)

関数の詳細は上記ブログを参照してください。
今回はQRコードの下に文字を表示する余白を作りたいので、下方向にサイズを追加します。
背景色は白（#ffffff）にしています。

```python
from PIL import Image
img = Image.open('test.png') #先ほどに保存した画像を読み込む
img_new = add_margin(img, 0, 0, 100, 0, '#ffffff')

img_new.show()
```

この関数はマイナスの数値を入力すれば、サイズを縮小できるので、画像サイズの調整に便利です。

# 画像サイズの変更

後で使用するので、画像サイズの拡大縮小方法を紹介します。
resizeにより画像を拡大縮小できます。

```python
from PIL import Image, ImageDraw, ImageFont
img = Image.open('test.png') #画像の読み込み
img_resize = img.resize((int(img .width / 2), int(img .height / 2)), Image.LANCZOS)

img_resize.show()
```

'Image.LANCZOS'　は圧縮フィルタを指定するパラメータです。
以下から選択します。一般的に下の方が圧縮率が低くなる代わりに、画像はキレイになります。
このパラメータは省略可能で、省略すると自動的にNEARESTが適用されます。

- NEAREST
    - 何も指定しなければ、これが選択される
- BOX
- BILINEAR
- HAMMING
- BICUBIC
- LANCZOS

# 文字の挿入
[Pythonで一気に画像編集できるPillowを使った画像処理のチートシート](https://hashikake.com/Pillow#toc14)にて紹介されていた方法で、文字を挿入してみました。

```python
from PIL import Image, ImageDraw, ImageFont
img = Image.open('test.png')
img_new = add_margin(img, 0, 0, 100, 0, '#ffffff')

draw = ImageDraw.Draw(img_new) 
font = ImageFont.truetype("./fonts/arial.ttf", 55) 
draw.text((20, 320), 'Hello! 12345', font=font, fill='#000000') 
img_new.save('test.png')

img_new.show()
```

<img width="40%" alt="test.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/c10a3548-e471-cce8-6bcc-42417e8edca3.png">

フォントの種類は変更できます。
以下を参照して選択ください。 
https://helpx.adobe.com/jp/x-productkb/global/cq08041028.html

# 文字の画像解像度が荒い場合

（追記）
画像をグレースケールに変換している場合は、文字画像は荒くなりません。
以下は参考用に残しますが、必要のない処理になります。
（追記終わり）


小さなサイズのQRコードに対しては、上記の方法で挿入した文字は荒いという欠点があります。
そこでresizeで拡大した解像度の高い画像を保存し、それを画像サイズを縮小するツールで適切なサイズにします。

まずは余白を入れた画像を10倍に拡大し、そこに大きめの文字を挿入します。

```python
from PIL import Image, ImageDraw, ImageFont
img = Image.open('test.png')
img_add = add_margin(img, 0, 0, 100, 0, '#ffffff')

img_resized = img_add.resize((int(img_add.width*10), int(img_add.height*10)), resample=Image.LANCZOS)

draw = ImageDraw.Draw(img_resized)
font = ImageFont.truetype("./fonts/arial.ttf", 530) 
draw.text((190, 3200), 'Hello! 12345', font=font, fill='#000000') 
img_resized.save('test.png')

img_resized.show()
```

画像の縮小には、フリーソフトの [藤 -Resizer-](https://www.vector.co.jp/soft/winnt/art/se132646.html?ds) を使用しました。
何百枚の画像でも、アイコンにドラッグ&ドロップすれば一括で縮小してくれます。

https://pcgenki.com/soft/resizer.htm

<img width="60%" alt="画像縮小.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/031e1957-0f5c-dff2-52d8-7a725049d5b5.png">

文字がキレイに表示されています。

<img width="60%" alt="test_s.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/3d896e3d-abd2-7b9f-ac20-2d10cb448765.png">

画像編集ソフトで確認すると、印刷しても問題ない解像度であることが分かります。

<img width="80%" alt="qrコード画像解像度結果.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/834504a1-35d7-9a9a-e919-df696a584e3c.png">

# 大量生成

今までのコードを関数化すれば、連番のQRコードを大量生成できます。

フォルダの自動作成は以下の記事をマネしました。
[Python フォルダの作成 既存かチェックあり](https://qiita.com/yusa0827/items/b70780c7ee38a8282375)

```python
import os
import qrcode
from PIL import Image, ImageDraw, ImageFont

def add_margin(pil_img, top, right, bottom, left, color):
    #先ほどに紹介した記事のコードをコピペしてください。
    return result

def qr_generator(number):
    qr = qrcode.QRCode(
        version=2,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=9,
        border=4,
    )
    qr.add_data('Hello!' + number)
    qr.make(fit=True)

    img = qr.make_image(fill_color="black", back_color="white").convert('L')
    img_add = add_margin(img, -15, -15, 45, -15, '#ffffff')
    # （追記）画像拡大・縮小工程が必要だった時の設定
    # img_resized = img_add.resize((int(img_add.width*10), int(img_add.height*10)), resample=Image.LANCZOS)
    
    draw = ImageDraw.Draw(img_add)
    font = ImageFont.truetype("./fonts/arialbd", 45)
    num = number

    # （追記）画像拡大・縮小工程が必要だった時の設定
    #draw.text((200, 2550), 'Hello!' + num, font=font, fill='#000000') 
    #img_resized.save('./my_qr_code/' + number + '.png', quality=95)

    draw.text((15, 250), 'Hello! ' + num, font=font, fill='#000000') 
    img_add.save('./my_qr_code/' + number + '.png', quality=95)

    #img_add.show()


def main(total_num):
    new_path = "my_qr_code"#フォルダ名
    if not os.path.exists(new_path):#ディレクトリ有無確認
        os.mkdir(new_path)
    
    for number in range(total_num):
        qr_generator(str(1000+number))    
    
main(100) 
```

~~以上で作成した画像を[藤 -Resizer-](https://www.vector.co.jp/soft/winnt/art/se132646.html?ds) で一括縮小すれば完成です。~~
（追記：画像拡大・縮小が不要になったので、必要のない処理になりました）